diff --git a/helper/resources.go b/helper/resources.go index 9c2d31a..8218c3b 100644 --- a/helper/resources.go +++ b/helper/resources.go @@ -5,11 +5,144 @@ import ( "encoding/json" "fmt" + "github.com/ProtonMail/gopenpgp/v3/crypto" "github.com/passbolt/go-passbolt/api" ) -// CreateResource Creates a Resource where the Password and Description are Encrypted and Returns the Resources ID +// CreateResource Creates a Resource, Creates a v4 or v5 Resources based on the server Preference func CreateResource(ctx context.Context, c *api.Client, folderParentID, name, username, uri, password, description string) (string, error) { + // Create a v5 Password if that is the Server Default + if c.MetadataTypeSettings().DefaultResourceType == api.PassboltAPIVersionTypeV5 { + return CreateResourceV5(ctx, c, folderParentID, name, username, uri, password, description) + } else { + return CreateResourceV4(ctx, c, folderParentID, name, username, uri, password, description) + } +} + +func CreateResourceV5(ctx context.Context, c *api.Client, folderParentID, name, username, uri, password, description string) (string, error) { + if c.MetadataTypeSettings().AllowCreationOfV5Resources == false { + return "", fmt.Errorf("Creation of V5 Passwords is disabled on this Server") + } + + types, err := c.GetResourceTypes(ctx, nil) + if err != nil { + return "", fmt.Errorf("Getting ResourceTypes: %w", err) + } + var rType *api.ResourceType + for _, tmp := range types { + if tmp.Slug == "v5-default" { + rType = &tmp + break + } + } + if rType == nil { + return "", fmt.Errorf("Cannot find Resource type password-and-description") + } + + // Base Resource + resource := api.Resource{ + ResourceTypeID: rType.ID, + FolderParentID: folderParentID, + } + + // Resource Metadata + meta := api.ResourceMetadataTypeV5Default{ + ObjectType: api.PASSBOLT_OBJECT_TYPE_RESOURCE_METADATA, + Name: name, + Username: username, + URIs: []string{uri}, + } + + metaData, err := json.Marshal(&meta) + if err != nil { + return "", fmt.Errorf("Marshalling metadata: %w", err) + } + + err = validateMetadata(rType, string(metaData)) + if err != nil { + return "", fmt.Errorf("Validating metadata: %w", err) + } + + var publicMetadataKey *crypto.Key + // Since we are not sharing, use the Personal Key if allowed + if c.MetadataKeySettings().AllowUsageOfPersonalKeys { + publicMetadataKey, err = c.GetUserPrivateKeyCopy() + if err != nil { + return "", fmt.Errorf("Get User Private Key: %w", err) + } + + me, err := c.GetMe(ctx) + if err != nil { + return "", fmt.Errorf("Get User Me: %w", err) + } + + if me.GPGKey == nil { + return "", fmt.Errorf("User Me GPG Key nil") + } + + resource.MetadataKeyID = me.GPGKey.ID + resource.MetadataKeyType = api.MetadataKeyTypeUserKey + } else { + keys, err := c.GetMetadataKeys(ctx, nil) + if err != nil { + return "", fmt.Errorf("Get Metadata Key: %w", err) + } + + // TODO Get Key by id? + if len(keys) != 1 { + return "", fmt.Errorf("Not Exactly One Metadatakey Available") + } + + publicMetadataKey, err = crypto.NewKeyFromArmored(keys[0].ArmoredKey) + if err != nil { + return "", fmt.Errorf("Get Metadata Public Key: %w", err) + } + + resource.MetadataKeyID = keys[0].ID + resource.MetadataKeyType = api.MetadataKeyTypeSharedKey + } + + encMetadata, err := c.EncryptMessageWithKey(publicMetadataKey, string(metaData)) + if err != nil { + return "", fmt.Errorf("Encrypt Metadata: %w", err) + } + resource.Metadata = encMetadata + + // Resource Secret + secret := api.SecretDataTypeV5Default{ + ObjectType: api.PASSBOLT_OBJECT_TYPE_SECRET_DATA, + Password: password, + Description: description, + } + + secretData, err := json.Marshal(&secret) + if err != nil { + return "", fmt.Errorf("Marshalling Secret Data: %w", err) + } + + err = validateSecretData(rType, string(secretData)) + if err != nil { + return "", fmt.Errorf("Validating Secret Data: %w", err) + } + + encSecretData, err := c.EncryptMessage(string(secretData)) + if err != nil { + return "", fmt.Errorf("Encrypting Secret Data for User me: %w", err) + } + resource.Secrets = []api.Secret{{Data: encSecretData}} + + newresource, err := c.CreateResource(ctx, resource) + if err != nil { + return "", fmt.Errorf("Creating Resource: %w", err) + } + return newresource.ID, nil +} + +func CreateResourceV4(ctx context.Context, c *api.Client, folderParentID, name, username, uri, password, description string) (string, error) { + if c.MetadataTypeSettings().AllowCreationOfV4Resources == false { + return "", fmt.Errorf("Creation of V4 Passwords is disabled on this Server") + } + types, err := c.GetResourceTypes(ctx, nil) if err != nil { return "", fmt.Errorf("Getting ResourceTypes: %w", err) @@ -62,6 +195,12 @@ func CreateResource(ctx context.Context, c *api.Client, folderParentID, name, us // CreateResourceSimple Creates a Legacy Resource where only the Password is Encrypted and Returns the Resources ID func CreateResourceSimple(ctx context.Context, c *api.Client, folderParentID, name, username, uri, password, description string) (string, error) { + if c.MetadataTypeSettings().AllowCreationOfV4Resources == false { + return "", fmt.Errorf("Creation of V4 Passwords is disabled on this Server") + } + + // TODO Create a v5-password-string if v5 is enabled + enc, err := c.EncryptMessage(password) if err != nil { return "", fmt.Errorf("Encrypting Password: %w", err)