mirror of
https://github.com/passbolt/go-passbolt.git
synced 2025-05-12 18:48:22 +00:00
Merge 146fe6662c
into d4132c756b
This commit is contained in:
commit
1456570c17
11 changed files with 353 additions and 36 deletions
|
@ -6,12 +6,11 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
"github.com/ProtonMail/gopenpgp/v2/crypto"
|
"github.com/ProtonMail/gopenpgp/v3/crypto"
|
||||||
"github.com/google/go-querystring/query"
|
"github.com/google/go-querystring/query"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -39,6 +38,9 @@ type Client struct {
|
||||||
// You need to Return the Cookie that Passbolt expects to verify you MFA, usually it is called passbolt_mfa
|
// You need to Return the Cookie that Passbolt expects to verify you MFA, usually it is called passbolt_mfa
|
||||||
MFACallback func(ctx context.Context, c *Client, res *APIResponse) (http.Cookie, error)
|
MFACallback func(ctx context.Context, c *Client, res *APIResponse) (http.Cookie, error)
|
||||||
|
|
||||||
|
// gopengpg Handler, allow for custom settings in the future
|
||||||
|
pgp *crypto.PGPHandle
|
||||||
|
|
||||||
// Enable Debug Logging
|
// Enable Debug Logging
|
||||||
Debug bool
|
Debug bool
|
||||||
}
|
}
|
||||||
|
@ -67,6 +69,8 @@ func NewClient(httpClient *http.Client, UserAgent, BaseURL, UserPrivateKey, User
|
||||||
return nil, fmt.Errorf("Parsing Base URL: %w", err)
|
return nil, fmt.Errorf("Parsing Base URL: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pgp := crypto.PGP()
|
||||||
|
|
||||||
// Verify that the Given Privatekey and Password are valid and work Together if we were provieded one
|
// Verify that the Given Privatekey and Password are valid and work Together if we were provieded one
|
||||||
if UserPrivateKey != "" {
|
if UserPrivateKey != "" {
|
||||||
privateKeyObj, err := crypto.NewKeyFromArmored(UserPrivateKey)
|
privateKeyObj, err := crypto.NewKeyFromArmored(UserPrivateKey)
|
||||||
|
@ -93,6 +97,7 @@ func NewClient(httpClient *http.Client, UserAgent, BaseURL, UserPrivateKey, User
|
||||||
userAgent: UserAgent,
|
userAgent: UserAgent,
|
||||||
userPassword: []byte(UserPassword),
|
userPassword: []byte(UserPassword),
|
||||||
userPrivateKey: UserPrivateKey,
|
userPrivateKey: UserPrivateKey,
|
||||||
|
pgp: pgp,
|
||||||
}
|
}
|
||||||
return c, err
|
return c, err
|
||||||
}
|
}
|
||||||
|
@ -150,7 +155,7 @@ func (c *Client) do(ctx context.Context, req *http.Request, v *APIResponse) (*ht
|
||||||
resp.Body.Close()
|
resp.Body.Close()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
bodyBytes, err := ioutil.ReadAll(resp.Body)
|
bodyBytes, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return resp, fmt.Errorf("Error Reading Resopnse Body: %w", err)
|
return resp, fmt.Errorf("Error Reading Resopnse Body: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -231,3 +236,8 @@ func (c *Client) setMetadataTypeSettings(ctx context.Context) error {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetPGPHandle Gets the Gopgenpgp Handler
|
||||||
|
func (c *Client) GetPGPHandle() *crypto.PGPHandle {
|
||||||
|
return c.pgp
|
||||||
|
}
|
||||||
|
|
|
@ -3,33 +3,142 @@ package api
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/ProtonMail/gopenpgp/v2/helper"
|
"github.com/ProtonMail/gopenpgp/v3/crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
// EncryptMessage encrypts a message using the users public key and then signes the message using the users private key
|
// EncryptMessage encrypts a message using the users public key and then signes the message using the users private key
|
||||||
func (c *Client) EncryptMessage(message string) (string, error) {
|
func (c *Client) EncryptMessage(message string) (string, error) {
|
||||||
if c.userPrivateKey == "" {
|
key, err := c.getPrivateKey(c.userPrivateKey, c.userPassword)
|
||||||
return "", fmt.Errorf("Client has no Private Key")
|
if err != nil {
|
||||||
} else if c.userPublicKey == "" {
|
return "", fmt.Errorf("Get Private Key: %w", err)
|
||||||
return "", fmt.Errorf("Client has no Public Key")
|
|
||||||
}
|
}
|
||||||
return helper.EncryptSignMessageArmored(c.userPublicKey, c.userPrivateKey, c.userPassword, message)
|
|
||||||
|
defer key.ClearPrivateParams()
|
||||||
|
|
||||||
|
encHandle, err := c.pgp.Encryption().SigningKey(key).Recipient(key).New()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("New Encryptor: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer encHandle.ClearPrivateParams()
|
||||||
|
|
||||||
|
encMessage, err := encHandle.Encrypt([]byte(message))
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("Encrypt Message: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
encArmor, err := encMessage.Armor()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("Armor Message: %w", err)
|
||||||
|
}
|
||||||
|
return encArmor, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// EncryptMessageWithPublicKey encrypts a message using the provided public key and then signes the message using the users private key
|
// EncryptMessageWithPublicKey encrypts a message using the provided public key and then signes the message using the users private key
|
||||||
func (c *Client) EncryptMessageWithPublicKey(publickey, message string) (string, error) {
|
func (c *Client) EncryptMessageWithPublicKey(publickey, message string) (string, error) {
|
||||||
if c.userPrivateKey == "" {
|
key, err := c.getPrivateKey(c.userPrivateKey, c.userPassword)
|
||||||
return "", fmt.Errorf("Client has no Private Key")
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("Get Private Key: %w", err)
|
||||||
}
|
}
|
||||||
return helper.EncryptSignMessageArmored(publickey, c.userPrivateKey, c.userPassword, message)
|
|
||||||
|
defer key.ClearPrivateParams()
|
||||||
|
|
||||||
|
publicKey, err := crypto.NewKeyFromArmored(publickey)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("Get Public Key: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
encHandle, err := c.pgp.Encryption().SigningKey(key).Recipient(publicKey).New()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("New Encryptor: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer encHandle.ClearPrivateParams()
|
||||||
|
|
||||||
|
encMessage, err := encHandle.Encrypt([]byte(message))
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("Encrypt Message: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
encArmor, err := encMessage.Armor()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("Armor Message: %w", err)
|
||||||
|
}
|
||||||
|
return encArmor, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecryptMessage decrypts a message using the users Private Key
|
// DecryptMessage decrypts a message using the users Private Key
|
||||||
func (c *Client) DecryptMessage(message string) (string, error) {
|
func (c *Client) DecryptMessage(message string) (string, error) {
|
||||||
if c.userPrivateKey == "" {
|
key, err := c.getPrivateKey(c.userPrivateKey, c.userPassword)
|
||||||
return "", fmt.Errorf("Client has no Private Key")
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("Get Private Key: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
defer key.ClearPrivateParams()
|
||||||
|
|
||||||
|
decHandle, err := c.pgp.Decryption().DecryptionKey(key).New()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("New Decryptor: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer decHandle.ClearPrivateParams()
|
||||||
|
|
||||||
|
res, err := decHandle.Decrypt([]byte(message), crypto.Armor)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("Decrypt Message: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
// We cant Verify the signature as we don't store other users public keys locally and don't know which user did encrypt it
|
// We cant Verify the signature as we don't store other users public keys locally and don't know which user did encrypt it
|
||||||
//return helper.DecryptVerifyMessageArmored(c.userPublicKey, c.userPrivateKey, c.userPassword, message)
|
//return helper.DecryptVerifyMessageArmored(c.userPublicKey, c.userPrivateKey, c.userPassword, message)
|
||||||
return helper.DecryptMessageArmored(c.userPrivateKey, c.userPassword, message)
|
|
||||||
|
return res.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO change []byte to string?
|
||||||
|
func (c *Client) DecryptMessageWithPrivateKey(privateKey string, passphrase []byte, ciphertextArmored string) (string, error) {
|
||||||
|
key, err := c.getPrivateKey(privateKey, passphrase)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("Get Private Key: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer key.ClearPrivateParams()
|
||||||
|
|
||||||
|
decHandle, err := c.pgp.Decryption().DecryptionKey(key).New()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("New Decryptor: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer decHandle.ClearPrivateParams()
|
||||||
|
|
||||||
|
res, err := decHandle.Decrypt([]byte(ciphertextArmored), crypto.Armor)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("Decrypt: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(res.Bytes()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) getPrivateKey(privateKey string, passphrase []byte) (*crypto.Key, error) {
|
||||||
|
if c.userPrivateKey == "" {
|
||||||
|
return nil, fmt.Errorf("Client has no Private Key")
|
||||||
|
}
|
||||||
|
|
||||||
|
key, err := crypto.NewKeyFromArmored(privateKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Key From Armored: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
locked, err := key.IsLocked()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Is Key Locked: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if locked {
|
||||||
|
unlocked, err := key.Unlock(passphrase)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Unlock Key: %w", err)
|
||||||
|
}
|
||||||
|
return unlocked, nil
|
||||||
|
}
|
||||||
|
return key, nil
|
||||||
}
|
}
|
||||||
|
|
19
api/foreign_model.go
Normal file
19
api/foreign_model.go
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
package api
|
||||||
|
|
||||||
|
type ForeignModelTypes string
|
||||||
|
|
||||||
|
const (
|
||||||
|
ForeignModelTypesResource ForeignModelTypes = "Resource"
|
||||||
|
ForeignModelTypesSecret ForeignModelTypes = "Secret"
|
||||||
|
ForeignModelTypesFolder = "Folder"
|
||||||
|
ForeignModelTypesComment = "Comment"
|
||||||
|
ForeignModelTypesTag = "Tag"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s ForeignModelTypes) IsValid() bool {
|
||||||
|
switch s {
|
||||||
|
case ForeignModelTypesResource, ForeignModelTypesSecret, ForeignModelTypesFolder, ForeignModelTypesComment, ForeignModelTypesTag:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
61
api/metadata_sessionkey.go
Normal file
61
api/metadata_sessionkey.go
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MetadataSessionKey is a MetadataSessionKey
|
||||||
|
type MetadataSessionKey struct {
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
UserID string `json:"user_id,omitempty"`
|
||||||
|
Data string `json:"data,omitempty"`
|
||||||
|
Created Time `json:"created,omitempty"`
|
||||||
|
Modified Time `json:"modified,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MetadataSessionKeyData is a MetadataSessionKeyData
|
||||||
|
type MetadataSessionKeyData struct {
|
||||||
|
// ObjectType Must always be PASSBOLT_SESSION_KEYS
|
||||||
|
ObjectType string `json:"object_type,omitempty"`
|
||||||
|
SessionKeys []MetadataSessionKeyDataElement `json:"session_keys,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MetadataSessionKeyData is a MetadataSessionKeyData
|
||||||
|
type MetadataSessionKeyDataElement struct {
|
||||||
|
ForeignModel ForeignModelTypes `json:"foreign_model"`
|
||||||
|
ForeignID string `json:"foreign_id"`
|
||||||
|
SessionKey string `json:"session_key"`
|
||||||
|
Modified Time `json:"modified"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMetadataTypeSettings gets the Servers Settings about which Types to use
|
||||||
|
func (c *Client) GetMetadataSessionKeys(ctx context.Context) ([]MetadataSessionKey, error) {
|
||||||
|
msg, err := c.DoCustomRequest(ctx, "GET", "/metadata/session-keys.json", "v2", nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var metadataSessionKeys []MetadataSessionKey
|
||||||
|
err = json.Unmarshal(msg.Body, &metadataSessionKeys)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return metadataSessionKeys, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO add Create and Update
|
||||||
|
|
||||||
|
// DeleteSessionKey Deletes a Passbolt SessionKey
|
||||||
|
func (c *Client) DeleteSessionKey(ctx context.Context, sessionKeyID string) error {
|
||||||
|
err := checkUUIDFormat(sessionKeyID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Checking ID format: %w", err)
|
||||||
|
}
|
||||||
|
_, err = c.DoCustomRequest(ctx, "DELETE", "/metadata/session-keys/"+sessionKeyID+".json", "v2", nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
86
api/metadatakey.go
Normal file
86
api/metadatakey.go
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MetadataKeyType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
MetadataKeyTypeUserKey MetadataKeyType = "user_key"
|
||||||
|
MetadataKeyTypeSharedKey = "shared_key"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s MetadataKeyType) IsValid() bool {
|
||||||
|
switch s {
|
||||||
|
case MetadataKeyTypeUserKey, MetadataKeyTypeSharedKey:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// MetadataKey is a MetadataKey
|
||||||
|
type MetadataKey struct {
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
Fingerprint string `json:"fingerprint,omitempty"`
|
||||||
|
ArmoredKey string `json:"armored_key,omitempty"`
|
||||||
|
Created Time `json:"created,omitempty"`
|
||||||
|
Modified Time `json:"modified,omitempty"`
|
||||||
|
|
||||||
|
// These are always null? Used for Key Rotation?
|
||||||
|
//"expired": null,
|
||||||
|
//"deleted": null,
|
||||||
|
|
||||||
|
CreatedBy *string `json:"created_by,omitempty"`
|
||||||
|
ModifiedBy *string `json:"modified_by,omitempty"`
|
||||||
|
|
||||||
|
MetadataPrivateKeys []MetadataPrivateKey `json:"metadata_private_keys,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MetadataPrivateKey is a MetadataPrivateKey
|
||||||
|
type MetadataPrivateKey struct {
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
MetadataKeyID string `json:"metadata_key_id,omitempty"`
|
||||||
|
UserID *string `json:"user_id,omitempty"` // TODO, is this nullable. The Docs says yes and no
|
||||||
|
Data string `json:"data,omitempty"`
|
||||||
|
Created Time `json:"created,omitempty"`
|
||||||
|
Modified Time `json:"modified,omitempty"`
|
||||||
|
CreatedBy *string `json:"created_by,omitempty"`
|
||||||
|
ModifiedBy *string `json:"modified_by,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MetadataPrivateKeyData is a MetadataPrivateKeyData
|
||||||
|
type MetadataPrivateKeyData struct {
|
||||||
|
// ObjectType Must always be PASSBOLT_METADATA_PRIVATE_KEY
|
||||||
|
ObjectType string `json:"object_type,omitempty"`
|
||||||
|
// Domain Must be the Passbolt Server URL
|
||||||
|
Domain string `json:"domain,omitempty"`
|
||||||
|
Fingerprint string `json:"fingerprint,omitempty"`
|
||||||
|
ArmoredKey string `json:"armored_key,omitempty"`
|
||||||
|
// Passphrase must be Empty for Server Keys
|
||||||
|
Passphrase string `json:"passphrase,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMetadataKeysOptions are all available query parameters
|
||||||
|
type GetMetadataKeysOptions struct {
|
||||||
|
FilterDeleted bool `url:"filter[deleted,omitempty"`
|
||||||
|
FilterExpired bool `url:"filter[expired,omitempty"`
|
||||||
|
|
||||||
|
ContainMetadataPrivateKeys bool `url:"contain[metadata_private_keys],omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMetadataKeys gets all Passbolt GetMetadataKeys
|
||||||
|
func (c *Client) GetMetadataKeys(ctx context.Context, opts *GetMetadataKeysOptions) ([]MetadataKey, error) {
|
||||||
|
msg, err := c.DoCustomRequest(ctx, "GET", "/metadata/keys.json", "v2", nil, opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var metadataKeys []MetadataKey
|
||||||
|
err = json.Unmarshal(msg.Body, &metadataKeys)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return metadataKeys, nil
|
||||||
|
}
|
|
@ -9,6 +9,7 @@ import (
|
||||||
// Resource is a Resource.
|
// Resource is a Resource.
|
||||||
// Warning: Since Passbolt v3 some fields here may not be populated as they may be in the Secret depending on the ResourceType,
|
// Warning: Since Passbolt v3 some fields here may not be populated as they may be in the Secret depending on the ResourceType,
|
||||||
// for now the only Field like that is the Description.
|
// for now the only Field like that is the Description.
|
||||||
|
// With Passbolt v5 it is now Possible that all Unencrypted User Supplied Fields are empty, and need to be decrypted from the Metadata Message
|
||||||
type Resource struct {
|
type Resource struct {
|
||||||
ID string `json:"id,omitempty"`
|
ID string `json:"id,omitempty"`
|
||||||
Created *Time `json:"created,omitempty"`
|
Created *Time `json:"created,omitempty"`
|
||||||
|
@ -27,8 +28,13 @@ type Resource struct {
|
||||||
FolderParentID string `json:"folder_parent_id,omitempty"`
|
FolderParentID string `json:"folder_parent_id,omitempty"`
|
||||||
ResourceTypeID string `json:"resource_type_id,omitempty"`
|
ResourceTypeID string `json:"resource_type_id,omitempty"`
|
||||||
ResourceType ResourceType `json:"resource_type,omitempty"`
|
ResourceType ResourceType `json:"resource_type,omitempty"`
|
||||||
Secrets []Secret `json:"secrets,omitempty"`
|
|
||||||
Tags []Tag `json:"tags,omitempty"`
|
MetadataKeyID string `json:"metadata_key_id,omitempty"`
|
||||||
|
MetadataKeyType MetadataKeyType `json:"metadata_key_type,omitempty"`
|
||||||
|
Metadata string `json:"metadata,omitempty"`
|
||||||
|
|
||||||
|
Secrets []Secret `json:"secrets,omitempty"`
|
||||||
|
Tags []Tag `json:"tags,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tag is a Passbolt Password Tag
|
// Tag is a Passbolt Password Tag
|
||||||
|
@ -48,6 +54,8 @@ type GetResourcesOptions struct {
|
||||||
// Parent Folder id
|
// Parent Folder id
|
||||||
FilterHasParent []string `url:"filter[has-parent][],omitempty"`
|
FilterHasParent []string `url:"filter[has-parent][],omitempty"`
|
||||||
FilterHasTag string `url:"filter[has-tag],omitempty"`
|
FilterHasTag string `url:"filter[has-tag],omitempty"`
|
||||||
|
// TODO Are undescores correct heare?
|
||||||
|
MetadataKeyType MetadataKeyType `url:"filter[metadata_key_type],omitempty"`
|
||||||
|
|
||||||
ContainCreator bool `url:"contain[creator],omitempty"`
|
ContainCreator bool `url:"contain[creator],omitempty"`
|
||||||
ContainFavorites bool `url:"contain[favorite],omitempty"`
|
ContainFavorites bool `url:"contain[favorite],omitempty"`
|
||||||
|
|
31
api/users.go
31
api/users.go
|
@ -24,6 +24,9 @@ type User struct {
|
||||||
GPGKey *GPGKey `json:"gpgKey,omitempty"`
|
GPGKey *GPGKey `json:"gpgKey,omitempty"`
|
||||||
LastLoggedIn string `json:"last_logged_in,omitempty"`
|
LastLoggedIn string `json:"last_logged_in,omitempty"`
|
||||||
Locale string `json:"locale,omitempty"`
|
Locale string `json:"locale,omitempty"`
|
||||||
|
|
||||||
|
// Admin only, needs contains
|
||||||
|
MissingMetadataKeyIDs []string `json:"missing_metadata_key_ids,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Profile is a Profile
|
// Profile is a Profile
|
||||||
|
@ -42,6 +45,8 @@ type GetUsersOptions struct {
|
||||||
FilterHasGroup []string `url:"filter[has-group][],omitempty"`
|
FilterHasGroup []string `url:"filter[has-group][],omitempty"`
|
||||||
FilterHasAccess []string `url:"filter[has-access][],omitempty"`
|
FilterHasAccess []string `url:"filter[has-access][],omitempty"`
|
||||||
FilterIsAdmin bool `url:"filter[is-admin],omitempty"`
|
FilterIsAdmin bool `url:"filter[is-admin],omitempty"`
|
||||||
|
// Admin only, TODO are underscores correct?
|
||||||
|
MissingMetadataKeyIDs bool `url:"filter[missing_metadata_key_ids],omitempty"`
|
||||||
|
|
||||||
ContainLastLoggedIn bool `url:"contain[LastLoggedIn],omitempty"`
|
ContainLastLoggedIn bool `url:"contain[LastLoggedIn],omitempty"`
|
||||||
}
|
}
|
||||||
|
@ -82,9 +87,11 @@ func (c *Client) GetMe(ctx context.Context) (*User, error) {
|
||||||
|
|
||||||
// GetUser gets a Passbolt User
|
// GetUser gets a Passbolt User
|
||||||
func (c *Client) GetUser(ctx context.Context, userID string) (*User, error) {
|
func (c *Client) GetUser(ctx context.Context, userID string) (*User, error) {
|
||||||
err := checkUUIDFormat(userID)
|
if userID != "me" {
|
||||||
if err != nil {
|
err := checkUUIDFormat(userID)
|
||||||
return nil, fmt.Errorf("Checking ID format: %w", err)
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Checking ID format: %w", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
msg, err := c.DoCustomRequest(ctx, "GET", "/users/"+userID+".json", "v2", nil, nil)
|
msg, err := c.DoCustomRequest(ctx, "GET", "/users/"+userID+".json", "v2", nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -101,9 +108,11 @@ func (c *Client) GetUser(ctx context.Context, userID string) (*User, error) {
|
||||||
|
|
||||||
// UpdateUser Updates a existing Passbolt User
|
// UpdateUser Updates a existing Passbolt User
|
||||||
func (c *Client) UpdateUser(ctx context.Context, userID string, user User) (*User, error) {
|
func (c *Client) UpdateUser(ctx context.Context, userID string, user User) (*User, error) {
|
||||||
err := checkUUIDFormat(userID)
|
if userID != "me" {
|
||||||
if err != nil {
|
err := checkUUIDFormat(userID)
|
||||||
return nil, fmt.Errorf("Checking ID format: %w", err)
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Checking ID format: %w", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
msg, err := c.DoCustomRequest(ctx, "PUT", "/users/"+userID+".json", "v2", user, nil)
|
msg, err := c.DoCustomRequest(ctx, "PUT", "/users/"+userID+".json", "v2", user, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -119,11 +128,13 @@ func (c *Client) UpdateUser(ctx context.Context, userID string, user User) (*Use
|
||||||
|
|
||||||
// DeleteUser Deletes a Passbolt User
|
// DeleteUser Deletes a Passbolt User
|
||||||
func (c *Client) DeleteUser(ctx context.Context, userID string) error {
|
func (c *Client) DeleteUser(ctx context.Context, userID string) error {
|
||||||
err := checkUUIDFormat(userID)
|
if userID != "me" {
|
||||||
if err != nil {
|
err := checkUUIDFormat(userID)
|
||||||
return fmt.Errorf("Checking ID format: %w", err)
|
if err != nil {
|
||||||
|
return fmt.Errorf("Checking ID format: %w", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_, err = c.DoCustomRequest(ctx, "DELETE", "/users/"+userID+".json", "v2", nil, nil)
|
_, err := c.DoCustomRequest(ctx, "DELETE", "/users/"+userID+".json", "v2", nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/ProtonMail/gopenpgp/v2/crypto"
|
"github.com/ProtonMail/gopenpgp/v3/crypto"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
1
go.mod
1
go.mod
|
@ -12,6 +12,7 @@ require (
|
||||||
require (
|
require (
|
||||||
github.com/ProtonMail/go-crypto v1.1.6 // indirect
|
github.com/ProtonMail/go-crypto v1.1.6 // indirect
|
||||||
github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f // indirect
|
github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f // indirect
|
||||||
|
github.com/ProtonMail/gopenpgp/v3 v3.1.3 // indirect
|
||||||
github.com/cloudflare/circl v1.6.0 // indirect
|
github.com/cloudflare/circl v1.6.0 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
golang.org/x/crypto v0.35.0 // indirect
|
golang.org/x/crypto v0.35.0 // indirect
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -9,6 +9,8 @@ github.com/ProtonMail/gopenpgp/v2 v2.7.5 h1:STOY3vgES59gNgoOt2w0nyHBjKViB/qSg7Nj
|
||||||
github.com/ProtonMail/gopenpgp/v2 v2.7.5/go.mod h1:IhkNEDaxec6NyzSI0PlxapinnwPVIESk8/76da3Ct3g=
|
github.com/ProtonMail/gopenpgp/v2 v2.7.5/go.mod h1:IhkNEDaxec6NyzSI0PlxapinnwPVIESk8/76da3Ct3g=
|
||||||
github.com/ProtonMail/gopenpgp/v2 v2.8.3 h1:1jHlELwCR00qovx2B50DkL/FjYwt/P91RnlsqeOp2Hs=
|
github.com/ProtonMail/gopenpgp/v2 v2.8.3 h1:1jHlELwCR00qovx2B50DkL/FjYwt/P91RnlsqeOp2Hs=
|
||||||
github.com/ProtonMail/gopenpgp/v2 v2.8.3/go.mod h1:LiuOTbnJit8w9ZzOoLscj0kmdALY7hfoCVh5Qlb0bcg=
|
github.com/ProtonMail/gopenpgp/v2 v2.8.3/go.mod h1:LiuOTbnJit8w9ZzOoLscj0kmdALY7hfoCVh5Qlb0bcg=
|
||||||
|
github.com/ProtonMail/gopenpgp/v3 v3.1.3 h1:nxUd0Na4MeElx0sA1t6U8/IxmjmCv3MKnTJGhEUK+qY=
|
||||||
|
github.com/ProtonMail/gopenpgp/v3 v3.1.3/go.mod h1:Ve9JYzwGau9DT0F9C9gsuEBU/T3Zbk0j1/+mPpWBogc=
|
||||||
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
|
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
|
||||||
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
|
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
|
||||||
github.com/cloudflare/circl v1.3.9 h1:QFrlgFYf2Qpi8bSpVPK1HBvWpx16v/1TZivyo7pGuBE=
|
github.com/cloudflare/circl v1.3.9 h1:QFrlgFYf2Qpi8bSpVPK1HBvWpx16v/1TZivyo7pGuBE=
|
||||||
|
|
|
@ -6,9 +6,6 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/passbolt/go-passbolt/api"
|
"github.com/passbolt/go-passbolt/api"
|
||||||
|
|
||||||
"github.com/ProtonMail/gopenpgp/v2/crypto"
|
|
||||||
"github.com/ProtonMail/gopenpgp/v2/helper"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ParseInviteUrl Parses a Passbolt Invite URL into a user id and token
|
// ParseInviteUrl Parses a Passbolt Invite URL into a user id and token
|
||||||
|
@ -31,21 +28,34 @@ func SetupAccount(ctx context.Context, c *api.Client, userID, token, password st
|
||||||
|
|
||||||
keyName := install.Profile.FirstName + " " + install.Profile.LastName + " " + install.Username
|
keyName := install.Profile.FirstName + " " + install.Profile.LastName + " " + install.Username
|
||||||
|
|
||||||
privateKey, err := helper.GenerateKey(keyName, install.Username, []byte(password), "rsa", 4096)
|
pgp := c.GetPGPHandle()
|
||||||
|
|
||||||
|
keyHandler := pgp.KeyGeneration().AddUserId(keyName, install.Username).New()
|
||||||
|
|
||||||
|
key, err := keyHandler.GenerateKey()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("Generating Private Key: %w", err)
|
return "", fmt.Errorf("Generating Private Key: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
key, err := crypto.NewKeyFromArmoredReader(strings.NewReader(privateKey))
|
defer key.ClearPrivateParams()
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("Reading Private Key: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
publicKey, err := key.GetArmoredPublicKey()
|
publicKey, err := key.GetArmoredPublicKey()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("Get Public Key: %w", err)
|
return "", fmt.Errorf("Get Public Key: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lockedKey, err := pgp.LockKey(key, []byte(password))
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("Locking Private Key: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer lockedKey.ClearPrivateParams()
|
||||||
|
|
||||||
|
privateKey, err := lockedKey.Armor()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("Get Private Key: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
request := api.SetupCompleteRequest{
|
request := api.SetupCompleteRequest{
|
||||||
AuthenticationToken: api.AuthenticationToken{
|
AuthenticationToken: api.AuthenticationToken{
|
||||||
Token: token,
|
Token: token,
|
||||||
|
|
Loading…
Add table
Reference in a new issue