mirror of
https://github.com/passbolt/go-passbolt.git
synced 2025-05-09 09:48:20 +00:00
218 lines
7.3 KiB
Go
218 lines
7.3 KiB
Go
package helper
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/speatzle/go-passbolt/api"
|
|
)
|
|
|
|
// ShareOperation defines how Resources are to be Shared With Users/Groups
|
|
type ShareOperation struct {
|
|
// Type of Permission: 1 = Read, 7 = can Update, 15 = Owner (Owner can also Share Resource)
|
|
// Note: Setting this to -1 Will delete this Permission if it already Exists, errors if this Permission Dosen't Already Exists
|
|
Type int
|
|
// ARO is what Type this should be Shared With (User, Group)
|
|
ARO string
|
|
// AROID is the ID of the User or Group(ARO) this should be Shared With
|
|
AROID string
|
|
}
|
|
|
|
// ShareResourceWithUsersAndGroups Shares a Resource With The Users and Groups with the Specified Permission Type,
|
|
// if the Resource has already been shared With the User/Group the Permission Type will be Adjusted/Deleted
|
|
func ShareResourceWithUsersAndGroups(ctx context.Context, c *api.Client, resourceID string, Users []string, Groups []string, permissionType int) error {
|
|
changes := []ShareOperation{}
|
|
for _, userID := range Users {
|
|
changes = append(changes, ShareOperation{
|
|
Type: permissionType,
|
|
ARO: "User",
|
|
AROID: userID,
|
|
})
|
|
}
|
|
for _, groupID := range Groups {
|
|
changes = append(changes, ShareOperation{
|
|
Type: permissionType,
|
|
ARO: "Group",
|
|
AROID: groupID,
|
|
})
|
|
}
|
|
return ShareResource(ctx, c, resourceID, changes)
|
|
}
|
|
|
|
// ShareResource Shares a Resource as Specified in the Passed ShareOperation Struct Slice
|
|
func ShareResource(ctx context.Context, c *api.Client, resourceID string, changes []ShareOperation) error {
|
|
oldPermissions, err := c.GetResourcePermissions(ctx, resourceID)
|
|
if err != nil {
|
|
return fmt.Errorf("Getting Resource Permissions: %w", err)
|
|
}
|
|
|
|
permissionChanges, err := GeneratePermissionChanges(oldPermissions, changes)
|
|
if err != nil {
|
|
return fmt.Errorf("Generating Resource Permission Changes: %w", err)
|
|
}
|
|
|
|
shareRequest := api.ResourceShareRequest{Permissions: permissionChanges}
|
|
|
|
secret, err := c.GetSecret(ctx, resourceID)
|
|
if err != nil {
|
|
return fmt.Errorf("Get Resource: %w", err)
|
|
}
|
|
|
|
secretData, err := c.DecryptMessage(secret.Data)
|
|
if err != nil {
|
|
return fmt.Errorf("Decrypting Resource Secret: %w", err)
|
|
}
|
|
|
|
simulationResult, err := c.SimulateShareResource(ctx, resourceID, shareRequest)
|
|
if err != nil {
|
|
return fmt.Errorf("Simulate Share Resource: %w", err)
|
|
}
|
|
|
|
users, err := c.GetUsers(ctx, nil)
|
|
if err != nil {
|
|
return fmt.Errorf("Get Users: %w", err)
|
|
}
|
|
|
|
shareRequest.Secrets = []api.Secret{}
|
|
for _, user := range simulationResult.Changes.Added {
|
|
pubkey, err := getPublicKeyByUserID(user.User.ID, users)
|
|
if err != nil {
|
|
return fmt.Errorf("Getting Public Key for User %v: %w", user.User.ID, err)
|
|
}
|
|
|
|
encSecretData, err := c.EncryptMessageWithPublicKey(pubkey, secretData)
|
|
if err != nil {
|
|
return fmt.Errorf("Encrypting Secret for User %v: %w", user.User.ID, err)
|
|
}
|
|
shareRequest.Secrets = append(shareRequest.Secrets, api.Secret{
|
|
UserID: user.User.ID,
|
|
Data: encSecretData,
|
|
})
|
|
}
|
|
|
|
err = c.ShareResource(ctx, resourceID, shareRequest)
|
|
if err != nil {
|
|
return fmt.Errorf("Sharing Resource: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ShareFolderWithUsersAndGroups Shares a Folder With The Users and Groups with the Specified Type,
|
|
// if the Folder has already been shared With the User/Group the Permission Type will be Adjusted/Deleted.
|
|
// Note: Resources Permissions in the Folder are not Adjusted (Like the Extention does)
|
|
func ShareFolderWithUsersAndGroups(ctx context.Context, c *api.Client, folderID string, Users []string, Groups []string, permissionType int) error {
|
|
changes := []ShareOperation{}
|
|
for _, userID := range Users {
|
|
changes = append(changes, ShareOperation{
|
|
Type: permissionType,
|
|
ARO: "User",
|
|
AROID: userID,
|
|
})
|
|
}
|
|
for _, groupID := range Groups {
|
|
changes = append(changes, ShareOperation{
|
|
Type: permissionType,
|
|
ARO: "Group",
|
|
AROID: groupID,
|
|
})
|
|
}
|
|
return ShareFolder(ctx, c, folderID, changes)
|
|
}
|
|
|
|
// ShareFolder Shares a Folder as Specified in the Passed ShareOperation Struct Slice.
|
|
// Note Resources Permissions in the Folder are not Adjusted
|
|
func ShareFolder(ctx context.Context, c *api.Client, folderID string, changes []ShareOperation) error {
|
|
oldPermissions, err := c.GetFolderPermissions(ctx, folderID)
|
|
if err != nil {
|
|
return fmt.Errorf("Getting Folder Permissions: %w", err)
|
|
}
|
|
|
|
permissionChanges, err := GeneratePermissionChanges(oldPermissions, changes)
|
|
if err != nil {
|
|
return fmt.Errorf("Generating Folder Permission Changes: %w", err)
|
|
}
|
|
|
|
err = c.ShareFolder(ctx, folderID, permissionChanges)
|
|
if err != nil {
|
|
return fmt.Errorf("Sharing Folder: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GeneratePermissionChanges Generates the Permission Changes for a Resource/Folder nessesary for a single Share Operation
|
|
func GeneratePermissionChanges(oldPermissions []api.Permission, changes []ShareOperation) ([]api.Permission, error) {
|
|
// Check for Duplicate Users/Groups as that would break stuff
|
|
for i, changeA := range changes {
|
|
for j, changeB := range changes {
|
|
if i != j && changeA.AROID == changeB.AROID && changeA.ARO == changeB.ARO {
|
|
return nil, fmt.Errorf("Change %v and %v are Both About the same ARO %v ID: %v, there can only be once change per ARO", i, j, changeA.ARO, changeA.AROID)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Get ACO and ACO ID from Existing Permissions
|
|
if len(oldPermissions) == 0 {
|
|
return nil, fmt.Errorf("There has to be atleast one Permission on a ACO")
|
|
}
|
|
ACO := oldPermissions[0].ACO
|
|
ACOID := oldPermissions[0].ACOForeignKey
|
|
|
|
permissionChanges := []api.Permission{}
|
|
for _, change := range changes {
|
|
// Find Permission thats invloves the Same ARO as Requested in the change
|
|
var oldPermission *api.Permission
|
|
for _, oldPerm := range oldPermissions {
|
|
if oldPerm.ARO == change.ARO && oldPerm.AROForeignKey == change.AROID {
|
|
oldPermission = &oldPerm
|
|
}
|
|
}
|
|
// Check Wheter Matching Permission Already Exists and needs to be adjusted or is a new one can be created
|
|
if oldPermission == nil {
|
|
if change.Type == 15 || change.Type == 7 || change.Type == 1 {
|
|
permissionChanges = append(permissionChanges, api.Permission{
|
|
IsNew: true,
|
|
Type: change.Type,
|
|
ARO: change.ARO,
|
|
AROForeignKey: change.AROID,
|
|
ACO: ACO,
|
|
ACOForeignKey: ACOID,
|
|
})
|
|
} else if change.Type == -1 {
|
|
return nil, fmt.Errorf("Permission for %v %v Cannot be Deleted as No Matching Permission Exists", change.ARO, change.AROID)
|
|
} else {
|
|
return nil, fmt.Errorf("Unknown Permission Type: %v", change.Type)
|
|
}
|
|
} else {
|
|
tmp := api.Permission{
|
|
ID: oldPermission.ID,
|
|
ARO: change.ARO,
|
|
AROForeignKey: change.AROID,
|
|
ACO: ACO,
|
|
ACOForeignKey: ACOID,
|
|
}
|
|
|
|
if change.Type == 15 || change.Type == 7 || change.Type == 1 {
|
|
if oldPermission.Type == change.Type {
|
|
return nil, fmt.Errorf("Permission for %v %v is already Type %v", change.ARO, change.AROID, change.Type)
|
|
}
|
|
tmp.Type = change.Type
|
|
} else if change.Type == -1 {
|
|
tmp.Delete = true
|
|
tmp.Type = oldPermission.Type
|
|
} else {
|
|
return nil, fmt.Errorf("Unknown Permission Type: %v", change.Type)
|
|
}
|
|
permissionChanges = append(permissionChanges, tmp)
|
|
}
|
|
}
|
|
return permissionChanges, nil
|
|
}
|
|
|
|
func getPublicKeyByUserID(userID string, Users []api.User) (string, error) {
|
|
for _, user := range Users {
|
|
if user.ID == userID {
|
|
return user.GPGKey.ArmoredKey, nil
|
|
}
|
|
}
|
|
return "", fmt.Errorf("Cannot Find Key for user id %v", userID)
|
|
}
|