From 3f4ed25a8399f28ff22872f49cfe5efa39d87200 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Thu, 2 Sep 2021 13:31:23 +0200 Subject: [PATCH 01/74] allow empty Client for Registration --- api/auth.go | 4 ++++ api/client.go | 34 +++++++++++++++++++--------------- api/encryption.go | 17 ++++++++++++++++- 3 files changed, 39 insertions(+), 16 deletions(-) diff --git a/api/auth.go b/api/auth.go index a128bff..5882f87 100644 --- a/api/auth.go +++ b/api/auth.go @@ -57,6 +57,10 @@ func (c *Client) CheckSession(ctx context.Context) bool { // Login gets a Session and CSRF Token from Passbolt and Stores them in the Clients Cookie Jar func (c *Client) Login(ctx context.Context) error { + if c.userPrivateKey == "" { + return fmt.Errorf("Client has no Private Key") + } + privateKeyObj, err := crypto.NewKeyFromArmored(c.userPrivateKey) if err != nil { return fmt.Errorf("Parsing User Private Key: %w", err) diff --git a/api/client.go b/api/client.go index 2a7833d..210e8eb 100644 --- a/api/client.go +++ b/api/client.go @@ -36,6 +36,8 @@ type Client struct { // NewClient Returns a new Passbolt Client. // if httpClient is nil http.DefaultClient will be used. // if UserAgent is "" "goPassboltClient/1.0" will be used. +// if UserPrivateKey is "" Key Setup is Skipped to Enable using the Client for User Registration, Most other function will be broken. +// After Registration a new Client Should be Created. func NewClient(httpClient *http.Client, UserAgent, BaseURL, UserPrivateKey, UserPassword string) (*Client, error) { if httpClient == nil { httpClient = http.DefaultClient @@ -49,22 +51,24 @@ func NewClient(httpClient *http.Client, UserAgent, BaseURL, UserPrivateKey, User return nil, fmt.Errorf("Parsing Base URL: %w", err) } - // Verify that the Given Privatekey and Password are valid and work Together - privateKeyObj, err := crypto.NewKeyFromArmored(UserPrivateKey) - if err != nil { - return nil, fmt.Errorf("Unable to Create Key From UserPrivateKey string: %w", err) - } - unlockedKeyObj, err := privateKeyObj.Unlock([]byte(UserPassword)) - if err != nil { - return nil, fmt.Errorf("Unable to Unlock UserPrivateKey using UserPassword: %w", err) - } - privateKeyRing, err := crypto.NewKeyRing(unlockedKeyObj) - if err != nil { - return nil, fmt.Errorf("Unable to Create a new Key Ring using the unlocked UserPrivateKey: %w", err) - } + // Verify that the Given Privatekey and Password are valid and work Together if we were provieded one + if UserPrivateKey != "" { + privateKeyObj, err := crypto.NewKeyFromArmored(UserPrivateKey) + if err != nil { + return nil, fmt.Errorf("Unable to Create Key From UserPrivateKey string: %w", err) + } + unlockedKeyObj, err := privateKeyObj.Unlock([]byte(UserPassword)) + if err != nil { + return nil, fmt.Errorf("Unable to Unlock UserPrivateKey using UserPassword: %w", err) + } + privateKeyRing, err := crypto.NewKeyRing(unlockedKeyObj) + if err != nil { + return nil, fmt.Errorf("Unable to Create a new Key Ring using the unlocked UserPrivateKey: %w", err) + } - // Cleanup Secrets - privateKeyRing.ClearPrivateParams() + // Cleanup Secrets + privateKeyRing.ClearPrivateParams() + } // Create Client Object c := &Client{ diff --git a/api/encryption.go b/api/encryption.go index 8193115..e87537e 100644 --- a/api/encryption.go +++ b/api/encryption.go @@ -1,19 +1,34 @@ package api -import "github.com/ProtonMail/gopenpgp/v2/helper" +import ( + "fmt" + + "github.com/ProtonMail/gopenpgp/v2/helper" +) // 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) { + if c.userPrivateKey == "" { + return "", fmt.Errorf("Client has no Private Key") + } else if c.userPublicKey == "" { + return "", fmt.Errorf("Client has no Public Key") + } return helper.EncryptSignMessageArmored(c.userPublicKey, c.userPrivateKey, c.userPassword, message) } // 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) { + if c.userPrivateKey == "" { + return "", fmt.Errorf("Client has no Private Key") + } return helper.EncryptSignMessageArmored(publickey, c.userPrivateKey, c.userPassword, message) } // DecryptMessage decrypts a message using the users Private Key func (c *Client) DecryptMessage(message string) (string, error) { + if c.userPrivateKey == "" { + return "", fmt.Errorf("Client has no Private Key") + } // 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.DecryptMessageArmored(c.userPrivateKey, c.userPassword, message) From 8c2be0f37f956fe279f24390ae2c7ef4f16ae151 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Thu, 2 Sep 2021 13:31:58 +0200 Subject: [PATCH 02/74] Add missing field for registration --- api/users.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/api/users.go b/api/users.go index b71a296..5ded066 100644 --- a/api/users.go +++ b/api/users.go @@ -5,6 +5,8 @@ import ( "encoding/json" ) +const UserLocaleENUK = "en-UK" + // User contains information about a passbolt User type User struct { ID string `json:"id,omitempty"` @@ -20,6 +22,7 @@ type User struct { Role *Role `json:"role,omitempty"` GPGKey *GPGKey `json:"gpgKey,omitempty"` LastLoggedIn string `json:"last_logged_in,omitempty"` + Locale string `json:"locale,omitempty"` } // Profile is a Profile From 39dc7c833db0f6a698c34f3f4583bfb3711e6ec5 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Thu, 2 Sep 2021 13:32:50 +0200 Subject: [PATCH 03/74] support for registration --- api/setup.go | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 api/setup.go diff --git a/api/setup.go b/api/setup.go new file mode 100644 index 0000000..17b4a57 --- /dev/null +++ b/api/setup.go @@ -0,0 +1,44 @@ +package api + +import ( + "context" + "encoding/json" +) + +type SetupInstallResponse struct { + User `json:"user,omitempty"` +} + +type AuthenticationToken struct { + Token string `json:"token,omitempty"` +} + +type SetupCompleteRequest struct { + AuthenticationToken AuthenticationToken `json:"authenticationtoken,omitempty"` + GPGKey GPGKey `json:"gpgkey,omitempty"` + User User `json:"user,omitempty"` +} + +// SetupInstall validates the userid and token used for Account setup, gives back the User Information +func (c *Client) SetupInstall(ctx context.Context, userID, token string) (*SetupInstallResponse, error) { + msg, err := c.DoCustomRequest(ctx, "GET", "/setup/install/"+userID+"/"+token+".json", "v2", nil, nil) + if err != nil { + return nil, err + } + + var install SetupInstallResponse + err = json.Unmarshal(msg.Body, &install) + if err != nil { + return nil, err + } + return &install, nil +} + +// SetupComplete Completes setup of a Passbolt Account +func (c *Client) SetupComplete(ctx context.Context, userID string, request SetupCompleteRequest) error { + _, err := c.DoCustomRequest(ctx, "POST", "/setup/complete/ "+userID+".json", "v2", request, nil) + if err != nil { + return err + } + return nil +} From 716e537f245b76374c98b524010358a3fd45654a Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Thu, 2 Sep 2021 13:40:52 +0200 Subject: [PATCH 04/74] add Setup Account Helper function --- helper/setup.go | 62 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 helper/setup.go diff --git a/helper/setup.go b/helper/setup.go new file mode 100644 index 0000000..3971cb4 --- /dev/null +++ b/helper/setup.go @@ -0,0 +1,62 @@ +package helper + +import ( + "context" + "fmt" + "strings" + + "github.com/speatzle/go-passbolt/api" + + "github.com/ProtonMail/gopenpgp/v2/crypto" + "github.com/ProtonMail/gopenpgp/v2/helper" +) + +// SetupAccount Setup a Account for a Invited User +func SetupAccount(ctx context.Context, c *api.Client, inviteURL, password string) (string, error) { + split := strings.Split(inviteURL, "/") + if len(split) < 4 { + return "", fmt.Errorf("Invite URL does not have enough slashes") + } + userID := split[len(split)-2] + token := strings.TrimSuffix(split[len(split)-1], ".json") + + install, err := c.SetupInstall(ctx, userID, token) + if err != nil { + return "", fmt.Errorf("Get Setup Install Data: %w", err) + } + + keyName := install.Profile.FirstName + " " + install.Profile.LastName + " <" + install.Username + ">" + + privateKey, err := helper.GenerateKey(keyName, install.Username, []byte(password), "rsa", 2048) + if err != nil { + return "", fmt.Errorf("Generating Private Key: %w", err) + } + + key, err := crypto.NewKeyFromArmoredReader(strings.NewReader(privateKey)) + if err != nil { + return "", fmt.Errorf("Reading Private Key: %w", err) + } + + publicKey, err := key.GetArmoredPublicKey() + if err != nil { + return "", fmt.Errorf("Get Public Key: %w", err) + } + + request := api.SetupCompleteRequest{ + AuthenticationToken: api.AuthenticationToken{ + Token: token, + }, + User: api.User{ + Locale: api.UserLocaleENUK, + }, + GPGKey: api.GPGKey{ + ArmoredKey: publicKey, + }, + } + + err = c.SetupComplete(ctx, userID, request) + if err != nil { + return "", fmt.Errorf("Setup Completion Failed: %w", err) + } + return privateKey, nil +} From 492cc57a97e813cfb714c7ab8a805fdbd07e8038 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Thu, 2 Sep 2021 16:11:42 +0200 Subject: [PATCH 05/74] remove bad space --- api/setup.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/setup.go b/api/setup.go index 17b4a57..b7a941b 100644 --- a/api/setup.go +++ b/api/setup.go @@ -36,7 +36,7 @@ func (c *Client) SetupInstall(ctx context.Context, userID, token string) (*Setup // SetupComplete Completes setup of a Passbolt Account func (c *Client) SetupComplete(ctx context.Context, userID string, request SetupCompleteRequest) error { - _, err := c.DoCustomRequest(ctx, "POST", "/setup/complete/ "+userID+".json", "v2", request, nil) + _, err := c.DoCustomRequest(ctx, "POST", "/setup/complete/"+userID+".json", "v2", request, nil) if err != nil { return err } From e5e0a160103a3617cb37a86ca2ea26486950ec29 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Thu, 2 Sep 2021 16:12:28 +0200 Subject: [PATCH 06/74] Move Invite URL Parsing to Seperate function --- helper/setup.go | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/helper/setup.go b/helper/setup.go index 3971cb4..02c0e50 100644 --- a/helper/setup.go +++ b/helper/setup.go @@ -11,21 +11,25 @@ import ( "github.com/ProtonMail/gopenpgp/v2/helper" ) -// SetupAccount Setup a Account for a Invited User -func SetupAccount(ctx context.Context, c *api.Client, inviteURL, password string) (string, error) { - split := strings.Split(inviteURL, "/") +// ParseInviteUrl Parses a Passbolt Invite URL into a user id and token +func ParseInviteUrl(url string) (string, string, error) { + split := strings.Split(url, "/") if len(split) < 4 { - return "", fmt.Errorf("Invite URL does not have enough slashes") + return "", "", fmt.Errorf("Invite URL does not have enough slashes") } - userID := split[len(split)-2] - token := strings.TrimSuffix(split[len(split)-1], ".json") + return split[len(split)-2], strings.TrimSuffix(split[len(split)-1], ".json"), nil +} + +// SetupAccount Setup a Account for a Invited User. +// (Use ParseInviteUrl to get the userid and token from a Invite URL) +func SetupAccount(ctx context.Context, c *api.Client, userID, token, password string) (string, error) { install, err := c.SetupInstall(ctx, userID, token) if err != nil { return "", fmt.Errorf("Get Setup Install Data: %w", err) } - 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", 2048) if err != nil { From 28e2ccf7e4b15f929b72db0ad64aee1d6cf2b75e Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Mon, 6 Sep 2021 15:55:55 +0200 Subject: [PATCH 07/74] fix lint --- api/auth.go | 5 +---- api/misc.go | 26 ++++++++------------------ 2 files changed, 9 insertions(+), 22 deletions(-) diff --git a/api/auth.go b/api/auth.go index 5882f87..5b9c2fd 100644 --- a/api/auth.go +++ b/api/auth.go @@ -146,10 +146,7 @@ func (c *Client) Login(ctx context.Context) error { } // Validate that this Publickey that the Server gave us actually Matches our Privatekey - randomString, err := randStringBytesRmndr(50) - if err != nil { - return fmt.Errorf("Generating Random String as PublicKey Validation Message: %w", err) - } + randomString := randStringBytesRmndr(50) armor, err := helper.EncryptMessageArmored(user.GPGKey.ArmoredKey, randomString) if err != nil { return fmt.Errorf("Encryping PublicKey Validation Message: %w", err) diff --git a/api/misc.go b/api/misc.go index eeebdfd..9990219 100644 --- a/api/misc.go +++ b/api/misc.go @@ -1,25 +1,15 @@ package api import ( - "crypto/rand" - "math/big" + "math/rand" ) -func randStringBytesRmndr(length int) (string, error) { - result := "" - for { - if len(result) >= length { - return result, nil - } - num, err := rand.Int(rand.Reader, big.NewInt(int64(127))) - if err != nil { - return "", err - } - n := num.Int64() - // Make sure that the number/byte/letter is inside - // the range of printable ASCII characters (excluding space and DEL) - if n > 32 && n < 127 { - result += string(n) - } +const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + +func randStringBytesRmndr(length int) string { + b := make([]byte, length) + for i := range b { + b[i] = letterBytes[rand.Intn(len(letterBytes))] } + return string(b) } From 54f4b4ca234c5383d0e5d7382c79b39209eeb998 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Mon, 6 Sep 2021 15:56:15 +0200 Subject: [PATCH 08/74] ad basic ci testing --- .github/workflows/.go.yml | 34 ++++++++++++++++++++++++ helper/resource_test.go | 30 ++++++++++++++++++++++ helper/setup_test.go | 54 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 118 insertions(+) create mode 100644 .github/workflows/.go.yml create mode 100644 helper/resource_test.go create mode 100644 helper/setup_test.go diff --git a/.github/workflows/.go.yml b/.github/workflows/.go.yml new file mode 100644 index 0000000..400af22 --- /dev/null +++ b/.github/workflows/.go.yml @@ -0,0 +1,34 @@ +name: Go + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-go@v2 + with: + go-version: 1.17 + - name: "Setup Passbolt" + run: | + git clone https://github.com/passbolt/passbolt_docker.git ../passbolt_docker + cd ../passbolt_docker + docker-compose up -d + docker ps -a + - name: "Test" + run: | + docker exec passbolt_docker_passbolt_1 sh -c '/usr/bin/wait-for.sh -t 30 localhost:443' + output=$(docker exec passbolt_docker_passbolt_1 sh -c 'su -m -c "/usr/share/php/passbolt/bin/cake \ + passbolt register_user \ + -u your@email.com \ + -f yourname \ + -l surname \ + -r admin" -s /bin/sh www-data') + export REG_URL=$(echo ${output##* your mailbox or here: } | tr -d '\n') + echo "Register with $REG_URL" + go test -v ./... diff --git a/helper/resource_test.go b/helper/resource_test.go new file mode 100644 index 0000000..10a9619 --- /dev/null +++ b/helper/resource_test.go @@ -0,0 +1,30 @@ +package helper + +import ( + "context" + "testing" +) + +func TestResourceCreate(t *testing.T) { + id, err := CreateResource(context.TODO(), client, "", "name", "username", "https://url.lan", "password123", "a password description") + if err != nil { + t.Fatalf("Creating Resource %v", err) + } + + _, name, username, uri, password, description, err := GetResource(context.TODO(), client, id) + if err != nil { + t.Fatalf("Getting Resource %v", err) + } + + equal(t, "Name", name, "name") + equal(t, "Username", username, "username") + equal(t, "URI", uri, "https://url.lan") + equal(t, "Password", password, "password123") + equal(t, "Description", description, "a password description") +} + +func equal(t *testing.T, name, a, b string) { + if a != b { + t.Fatalf("Value %v is %v instead of %v", name, a, b) + } +} diff --git a/helper/setup_test.go b/helper/setup_test.go new file mode 100644 index 0000000..d1f749d --- /dev/null +++ b/helper/setup_test.go @@ -0,0 +1,54 @@ +package helper + +import ( + "context" + "crypto/tls" + "fmt" + "net/http" + "os" + "testing" + + "github.com/speatzle/go-passbolt/api" +) + +var client *api.Client + +func TestMain(m *testing.M) { + url := os.Getenv("REG_URL") + fmt.Printf("Registering with url: %v\n", url) + userID, token, err := ParseInviteUrl(url) + if err != nil { + panic(fmt.Errorf("Unable to Parse Invite URL: %w", err)) + } + + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + hc := &http.Client{Transport: tr} + + rc, err := api.NewClient(hc, "", "https://localhost", "", "") + if err != nil { + panic(fmt.Errorf("Creating Registration Client: %w", err)) + } + + ctx := context.TODO() + + privkey, err := SetupAccount(ctx, rc, userID, token, "password123") + if err != nil { + panic(fmt.Errorf("Setup Account: %w", err)) + } + + c, err := api.NewClient(hc, "", "https://localhost", privkey, "password123") + if err != nil { + panic(fmt.Errorf("Setup Client: %w", err)) + } + + c.Login(ctx) + if err != nil { + panic(fmt.Errorf("Login Client: %w", err)) + } + + client = c + + os.Exit(m.Run()) +} From 8a3b33ba983bbc8ade9b7409f8cf868009bb330c Mon Sep 17 00:00:00 2001 From: Cassio Santos Date: Mon, 6 Sep 2021 19:45:52 -0300 Subject: [PATCH 09/74] It fixes minor typos --- README.md | 126 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 81 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index a02d7c7..0a92cc1 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,19 @@ # go-passbolt [![Go Reference](https://pkg.go.dev/badge/github.com/speatzle/go-passbolt.svg)](https://pkg.go.dev/github.com/speatzle/go-passbolt) -A Go Module to interact with [Passbolt](https://www.passbolt.com/), a Open source Password Manager for Teams +A Go module to interact with [Passbolt](https://www.passbolt.com/), an open-source password manager for teams -This Module tries to Support the Latest Passbolt Community/PRO Server Release, PRO Features Such as Folders are Supported. -Older Versions of Passbolt such as v2 are unsupported (it's a Password Manager, please update it) +This module tries to support the latest Passbolt Community/PRO server release, PRO Features such as folders are supported. Older versions of Passbolt such as v2 are unsupported (it's a password manager, please update it) -This Module is split into 2 packages: api and helper, in the api package you will find everything to directly interact with the API. The helper Package has simplified functions that use the api package to perform common but complicated tasks such as Sharing a Password. To use the API Package please read the Passbolt API [Docs](https://help.passbolt.com/api). -Sadly the Docs aren't Complete so many Things here have been found by looking at the source of Passbolt or through trial and error, if you have a Question just ask. +This module is divided into two packages: API and helper. -PR's are Welcome, if it's something bigger / fundamental: Please make a Issue and ask first. +In the API package, you will find everything to directly interact with the API. + +The helper package has simplified functions that use the API package to perform common but complicated tasks such as sharing a password. + +To use the API package, please read the [Passbolt API docs](https://help.passbolt.com/api). Sadly the docs aren't complete so many things here have been found by looking at the source of Passbolt or through trial and error. If you have a question just ask. + +PR's are welcome. But be gentle: if it's something bigger or fundamental: please [create an issue](https://github.com/speatzle/go-passbolt/issues/new) and ask first. # Install @@ -17,7 +21,8 @@ PR's are Welcome, if it's something bigger / fundamental: Please make a Issue an # Examples ## Login -First you will need to Create a Client, and then Login on the Server using the Client + +First, you will need to create a client and then log in on the server using the client: ```go package main @@ -54,11 +59,14 @@ func main() { } ``` -Note: if you want to use the client for some time then you'll have to make sure it is still logged in. +Note: if you want to use the client for a long time then you'll have to make sure it is still logged in. + You can do this using the `client.CheckSession()` function. ## Create a Resource -Creating a Resource using the helper package is simple, first add `"github.com/speatzle/go-passbolt/helper"` to your imports. + +Creating a resource using the helper package is simple. First, add `"github.com/speatzle/go-passbolt/helper"` to your imports. + Then you can simply: ```go @@ -70,11 +78,11 @@ resourceID, err := helper.CreateResource( "user123", // Username "https://test.example.com", // URI "securePassword123", // Password - "This is a Account for the example test portal", // Description + "This is an Account for the example test portal", // Description ) ``` -Creating a (Legacy) Resource Without the helper package would look like this: +Creating a (legacy) resource without the helper package would look like this: ```go enc, err := client.EncryptMessage("securePassword123") @@ -86,7 +94,7 @@ res := api.Resource{ Name: "Example Account", Username: "user123", URI: "https://test.example.com", - Description: "This is a Account for the example test portal", + Description: "This is an Account for the example test portal", Secrets: []api.Secret{ {Data: enc}, }, @@ -98,13 +106,15 @@ if err != nil { } ``` -Note: Since Passbolt v3 There are Resource Types, this Manual Example just creates a "password-string" Type Password where the Description is Unencrypted, Read More [Here](https://help.passbolt.com/api/resource-types). +Note: Since Passbolt v3 there are resource types. This manual example creates a "password-string" type password where the description is unencrypted. Read more [here](https://help.passbolt.com/api/resource-types). ## Getting -Generally API Get Calls will have options (opts) that allow for specifing filters and contains, if you dont want to specify options just pass nil. -Filters just filter by whatever is given, contains on the otherhand specify what to include in the response. Many Filters And Contains are undocumented in the Passbolt Docs. -Here We Specify that we want to Filter by Favorites and that the Response Should Contain the Permissions for each Resource: +Generally, API GET calls will have parameters that allow specifying `filters` and `contains`, if you don't want to define those parameters just pass nil. + +`Filters` just filter by whatever is given, `contains` on the other hand specify what information you want to include in the response. Many `filters` and `contains` are undocumented in the Passbolt docs. + +Here we specify that we want to filter by favorites and that the response should contain the permissions for each resource: ```go favorites, err := client.GetResources(ctx, &api.GetResourcesOptions{ @@ -113,7 +123,7 @@ favorites, err := client.GetResources(ctx, &api.GetResourcesOptions{ }) ``` -We Can do the Same for Users: +We can do the same for users: ```go users, err := client.GetUsers(ctx, &api.GetUsersOptions{ @@ -131,7 +141,7 @@ groups, err := client.GetGroups(ctx, &api.GetGroupsOptions{ }) ``` -And also for Folders (PRO Only): +And also for folders (PRO only): ```go folders, err := client.GetFolders(ctx, &api.GetFolderOptions{ @@ -140,28 +150,29 @@ folders, err := client.GetFolders(ctx, &api.GetFolderOptions{ }) ``` -Getting by ID is also Supported Using the Singular Form: +Getting by ID is also supported using the singular form: ```go resource, err := client.GetResource(ctx, "resource ID") ``` -Since the Password is Encrypted (and sometimes the description too) the helper package has a function to decrypt all encrypted fields Automatically: +Since the password is encrypted (and sometimes the description too) the helper package has a function to decrypt all encrypted fields automatically: ```go folderParentID, name, username, uri, password, description, err := helper.GetResource(ctx, client, "resource id") ``` ## Updating -The Helper Package has a function to save you needing to deal with Resource Types When Updating a Resource: + +The helper package has a function to save you from dealing with resource types when updating a resource: ```go err := helper.UpdateResource(ctx, client,"resource id", "name", "username", "https://test.example.com", "pass123", "very descriptive") ``` -Note: As Groups are also Complicated to Update there will be a helper function for them in the future. +Note: As groups are also complicated to update there will be a helper function for them in the future. -For other less Complicated Updates you can Simply use the Client directly: +For other less complicated updates you can simply use the client directly: ```go client.UpdateUser(ctx, "user id", api.User{ @@ -173,45 +184,55 @@ client.UpdateUser(ctx, "user id", api.User{ ``` ## Sharing -As Sharing Resources is very Complicated there are multipe helper Functions. During Sharing you will encounter the permissionType. -The permissionType can be 1 for Read only, 7 for Can Update, 15 for Owner or -1 if you want to delete Existing Permissions. +As sharing resources is very complicated there are multiple helper functions. -The ShareResourceWithUsersAndGroups function Shares the Resource With all Provided Users and Groups with the Given permissionType. +During sharing you will encounter the [permission type](https://github.com/passbolt/passbolt_api/blob/858971516c5e61e1f1be37b007693f0869a70486/src/Model/Entity/Permission.php#L43-L45). + +The `permissionType` can be: + +| Code | Meaning | +| --- | --- | +| `1` | "Read-only" | +| `7` | "Can update" | +| `15` | "Owner" | +| `-1` | If you want to delete existing permissions | + +The `ShareResourceWithUsersAndGroups` function shares the resource with all provided users and groups with the given `permissionType`. ```go err := helper.ShareResourceWithUsersAndGroups(ctx, client, "resource id", []string{"user 1 id"}, []string{"group 1 id"}, 7) ``` -Note: Existing Permission of Users and Groups will be adjusted to be of the Provided permissionType. +Note: Existing permission of users and groups will be adjusted to be of the provided `permissionType`. -If you need to do something more Complicated like setting Users/Groups to different Type then you can Use ShareResource directly: +If you need to do something more complicated like setting users/groups to different types then you can use `ShareResource` directly: ```go changes := []helper.ShareOperation{} -// Make this User Owner +// Make this user Owner changes = append(changes, ShareOperation{ Type: 15, ARO: "User", AROID: "user 1 id", }) -// Make this User Can Update +// Make this user "Can Update" changes = append(changes, ShareOperation{ Type: 5, ARO: "User", AROID: "user 2 id", }) -// Delete This Users Current Permission +// Delete this users current permission changes = append(changes, ShareOperation{ Type: -1, ARO: "User", AROID: "user 3 id", }) -// Make this Group Read Only +// Make this group "Read-only" changes = append(changes, ShareOperation{ Type: 1, ARO: "Group", @@ -221,20 +242,27 @@ changes = append(changes, ShareOperation{ err := helper.ShareResource(ctx, c, resourceID, changes) ``` -Note: These Functions are Also Availabe for Folders (PRO) +Note: These functions are also available for folders (PRO) -## Moveing (PRO) -In Passbolt PRO there are Folders, during Creation of Resources and Folders you can Specify in which Folder you want to create the Resource / Folder inside of. But if you want to change which Folder the Resource / Folder is in then you can't use the Update function (it is / was possible to update the parent Folder using the Update function but that breaks things). Instead you use the Move function. -``` +## Moving (PRO) + +In Passbolt PRO there are folders, during the creation of resources and folders you can specify in which folder you want to create the resource/folder inside. But if you want to change which folder the resource/folder is in then you can't use the `Update` function (it is/was possible to update the parent folder using the `Update` function but that breaks things). Instead, you use the `Move` function. + +```go err := client.MoveResource(ctx, "resource id", "parent folder id") ``` -``` + +```go err := client.MoveFolder(ctx, "folder id", "parent folder id") ``` ## Groups -Groups are extra complicated, it doesen't help that the Passbolt Documentation is wrong and missing important details. -Since helper functions for Groups were added you can now create, get, update and delete Groups easily: + + +Groups are extra complicated, it doesn't help that the Passbolt documentation is wrong and missing important details. + +Since helper functions for groups were added you can now create, get, update and delete groups easily: + ```go err := helper.UpdateGroup(ctx, client, "group id", "group name", []helper.GroupMembershipOperation{ { @@ -245,10 +273,18 @@ err := helper.UpdateGroup(ctx, client, "group id", "group name", []helper.GroupM ``` ## Other -These Examples are just the main Usecases of this Modules, there are many more API calls that are supported. Look at the [Reference](https://pkg.go.dev/github.com/speatzle/go-passbolt) for more information. + +These examples are just the main use cases of these Modules, many more API calls are supported. Look at the [reference](https://pkg.go.dev/github.com/speatzle/go-passbolt) for more information. + ## Full Example -This Example Creates a Resource, Searches for a User Named Test User, Checks that its Not itself and Shares the Password with the Test User if Nessesary: + +This example: + +1. Creates a resource; +2. Searches for a user named "Test User"; +3. Checks that it's not itself; and, +4. Shares the password with the "Test User" if necessary: ```go package main @@ -292,7 +328,7 @@ func main() { "user123", // Username "https://test.example.com", // URI "securePassword123", // Password - "This is a Account for the example test portal", // Description + "This is an Account for the example test portal", // Description ) if err != nil { panic(err) @@ -328,6 +364,6 @@ func main() { ``` # TODO -- get a Passbolt Instance to Work in Github Actions -- write Integration Tests -- add ability to verify Server +- get a Passbolt instance to work in Github Actions +- write integration tests +- add the ability to verify a server From c8e7981f4b133adcfdb6450396b85e59a892b7a2 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Tue, 7 Sep 2021 13:11:53 +0200 Subject: [PATCH 10/74] fix modifing permissions targeting wrong user --- helper/share.go | 1 + 1 file changed, 1 insertion(+) diff --git a/helper/share.go b/helper/share.go index d37daad..3294cea 100644 --- a/helper/share.go +++ b/helper/share.go @@ -168,6 +168,7 @@ func GeneratePermissionChanges(oldPermissions []api.Permission, changes []ShareO for _, oldPerm := range oldPermissions { if oldPerm.ARO == change.ARO && oldPerm.AROForeignKey == change.AROID { oldPermission = &oldPerm + break } } // Check Whether Matching Permission Already Exists and needs to be adjusted or is a new one can be created From 00dbd90175fb28d89dc6dba532a51eff7365b3a3 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Wed, 8 Sep 2021 11:09:01 +0200 Subject: [PATCH 11/74] update folder get options --- api/folders.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/api/folders.go b/api/folders.go index 2622200..961c788 100644 --- a/api/folders.go +++ b/api/folders.go @@ -20,8 +20,8 @@ type Folder struct { ChildrenFolders []Folder `json:"children_folders,omitempty"` } -// GetFolderOptions are all available query parameters -type GetFolderOptions struct { +// GetFoldersOptions are all available query parameters +type GetFoldersOptions struct { ContainChildrenResources bool `url:"contain[children_resources],omitempty"` ContainChildrenFolders bool `url:"contain[children_folders],omitempty"` ContainCreator bool `url:"contain[creator],omitempty"` @@ -33,13 +33,13 @@ type GetFolderOptions struct { ContainPermissionUserProfile bool `url:"contain[permissions.user.profile],omitempty"` ContainPermissionGroup bool `url:"contain[permissions.group],omitempty"` - FilterHasID string `url:"filter[has-id][],omitempty"` - FilterHasParent string `url:"filter[has-parent][],omitempty"` - FilterSearch string `url:"filter[search],omitempty"` + FilterHasID []string `url:"filter[has-id][],omitempty"` + FilterHasParent []string `url:"filter[has-parent][],omitempty"` + FilterSearch string `url:"filter[search],omitempty"` } // GetFolders gets all Folders from the Passboltserver -func (c *Client) GetFolders(ctx context.Context, opts *GetFolderOptions) ([]Folder, error) { +func (c *Client) GetFolders(ctx context.Context, opts *GetFoldersOptions) ([]Folder, error) { msg, err := c.DoCustomRequest(ctx, "GET", "/folders.json", "v2", nil, opts) if err != nil { return nil, err From ea631ac6a54fbe1a95ff24d1bb277753ea1c6647 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Wed, 8 Sep 2021 11:09:23 +0200 Subject: [PATCH 12/74] update resource get options --- api/resources.go | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/api/resources.go b/api/resources.go index fd91769..91ff97f 100644 --- a/api/resources.go +++ b/api/resources.go @@ -38,21 +38,23 @@ type Tag struct { // GetResourcesOptions are all available query parameters type GetResourcesOptions struct { - FilterIsFavorite bool `url:"filter[is-favorite],omitempty"` - FilterIsSharedWithMe bool `url:"filter[is-shared-with-me],omitempty"` - FilterIsSharedWithGroup string `url:"filter[is-shared-with-group],omitempty"` - FilterHasID string `url:"filter[has-id],omitempty"` + FilterIsFavorite bool `url:"filter[is-favorite],omitempty"` + FilterIsSharedWithGroup []string `url:"filter[is-shared-with-group][],omitempty"` + FilterIsOwnedByMe bool `url:"filter[is-owned-by-me],omitempty"` + FilterIsSharedWithMe bool `url:"filter[is-shared-with-me],omitempty"` + FilterHasID []string `url:"filter[has-id][],omitempty"` // Parent Folder id - FilterHasParent string `url:"filter[has-parent],omitempty"` - FilterHasTag string `url:"filter[has-tag],omitempty"` + FilterHasParent []string `url:"filter[has-parent][],omitempty"` + FilterHasTag string `url:"filter[has-tag],omitempty"` ContainCreator bool `url:"contain[creator],omitempty"` ContainFavorites bool `url:"contain[favorite],omitempty"` ContainModifier bool `url:"contain[modifier],omitempty"` + ContainSecret bool `url:"contain[secret],omitempty"` + ContainResourceType bool `url:"contain[resource-type],omitempty"` ContainPermissions bool `url:"contain[permission],omitempty"` ContainPermissionsUserProfile bool `url:"contain[permissions.user.profile],omitempty"` ContainPermissionsGroup bool `url:"contain[permissions.group],omitempty"` - ContainSecret bool `url:"contain[secret],omitempty"` ContainTags bool `url:"contain[tag],omitempty"` } From 824e0f7ccf5e82ca4dd9c88416d6c1d173452ad3 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Wed, 8 Sep 2021 11:09:38 +0200 Subject: [PATCH 13/74] add Group Membership fields --- helper/group.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/helper/group.go b/helper/group.go index eb816b7..e029b9f 100644 --- a/helper/group.go +++ b/helper/group.go @@ -17,6 +17,9 @@ type GroupMembershipOperation struct { // GroupMembership containes who and what kind of membership they have with a group type GroupMembership struct { UserID string + Username string + UserFirstName string + UserLastName string IsGroupManager bool } @@ -58,6 +61,9 @@ func GetGroup(ctx context.Context, c *api.Client, groupID string) (string, []Gro for _, m := range g.GroupUsers { memberships = append(memberships, GroupMembership{ UserID: m.UserID, + Username: m.User.Username, + UserFirstName: m.User.Profile.FirstName, + UserLastName: m.User.Profile.LastName, IsGroupManager: m.IsAdmin, }) } From 6bb4283e5ec5dc3096cbbaa1696f188e3b4b0564 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Wed, 8 Sep 2021 11:26:50 +0200 Subject: [PATCH 14/74] Update README.md --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0a92cc1..e7503ab 100644 --- a/README.md +++ b/README.md @@ -349,7 +349,7 @@ func main() { if client.GetUserID() == users[0].ID { fmt.Println("I am the Test User, No Need to Share Password With myself") - client.Logout(ctx) + client.Logout(ctx) return } @@ -364,6 +364,5 @@ func main() { ``` # TODO -- get a Passbolt instance to work in Github Actions -- write integration tests +- write more integration tests - add the ability to verify a server From 815df3e9f33c1587ccd44e711a56abe5f02321b8 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Wed, 8 Sep 2021 13:16:41 +0200 Subject: [PATCH 15/74] add user helper functions --- helper/user.go | 102 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 helper/user.go diff --git a/helper/user.go b/helper/user.go new file mode 100644 index 0000000..5d5fce0 --- /dev/null +++ b/helper/user.go @@ -0,0 +1,102 @@ +package helper + +import ( + "context" + "fmt" + + "github.com/speatzle/go-passbolt/api" +) + +// CreateUser Creates a new User +func CreateUser(ctx context.Context, c *api.Client, role, username, firstname, lastname string) (string, error) { + roles, err := c.GetRoles(ctx) + if err != nil { + return "", fmt.Errorf("Get Role: %w", err) + } + + roleID := "" + + for _, r := range roles { + if r.Name == role { + roleID = r.ID + break + } + } + + if roleID == "" { + return "", fmt.Errorf("Cannot Find Role %v", role) + } + + u, err := c.CreateUser(ctx, api.User{ + Username: username, + Profile: &api.Profile{ + FirstName: firstname, + LastName: lastname, + }, + RoleID: roleID, + }) + return u.ID, err +} + +// GetUser Gets a User +func GetUser(ctx context.Context, c *api.Client, userID string) (string, string, string, string, error) { + u, err := c.GetUser(ctx, userID) + if err != nil { + return "", "", "", "", fmt.Errorf("Get User %w", err) + } + return u.Username, u.Profile.FirstName, u.Profile.LastName, u.Role.Name, nil +} + +// UpdateUser Updates a User +func UpdateUser(ctx context.Context, c *api.Client, userID, role, firstname, lastname string) error { + user, err := c.GetUser(ctx, userID) + if err != nil { + return fmt.Errorf("Get User %w", err) + } + + new := api.User{ + Profile: &api.Profile{ + FirstName: user.Profile.FirstName, + LastName: user.Profile.LastName, + }, + } + + if role != "" { + roles, err := c.GetRoles(ctx) + if err != nil { + return fmt.Errorf("Get Role: %w", err) + } + + roleID := "" + + for _, r := range roles { + if r.Name == role { + roleID = r.ID + break + } + } + + if roleID == "" { + return fmt.Errorf("Cannot Find Role %v", role) + } + user.RoleID = roleID + } + + if firstname != "" { + new.Profile.FirstName = firstname + } + if lastname != "" { + new.Profile.LastName = lastname + } + + _, err = c.UpdateUser(ctx, userID, new) + if err != nil { + return fmt.Errorf("Updating User: %w", err) + } + return nil +} + +// DeleteUser Deletes a User +func DeleteUser(ctx context.Context, c *api.Client, userID string) error { + return c.DeleteUser(ctx, userID) +} From f3e40caa8f4740681fe6c81fffe8d99a0588b4f1 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Wed, 8 Sep 2021 14:41:02 +0200 Subject: [PATCH 16/74] adjust user get options --- api/users.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/api/users.go b/api/users.go index 5ded066..9cea6c8 100644 --- a/api/users.go +++ b/api/users.go @@ -37,10 +37,10 @@ type Profile struct { // GetUsersOptions are all available query parameters type GetUsersOptions struct { - FilterSearch string `url:"filter[search],omitempty"` - FilterHasGroup string `url:"filter[has-group],omitempty"` - FilterHasAccess string `url:"filter[has-access],omitempty"` - FilterIsAdmin bool `url:"filter[is-admin],omitempty"` + FilterSearch string `url:"filter[search],omitempty"` + FilterHasGroup []string `url:"filter[has-group][],omitempty"` + FilterHasAccess []string `url:"filter[has-access][],omitempty"` + FilterIsAdmin bool `url:"filter[is-admin],omitempty"` ContainLastLoggedIn bool `url:"contain[LastLoggedIn],omitempty"` } From 8381328ea97eaba8666d4e15d19082380b0ceec0 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Wed, 8 Sep 2021 14:43:12 +0200 Subject: [PATCH 17/74] improve errors --- helper/folder.go | 26 ++++++++++++++++++++++---- helper/resources.go | 14 +++++++++++--- helper/user.go | 21 ++++++++++++++------- 3 files changed, 47 insertions(+), 14 deletions(-) diff --git a/helper/folder.go b/helper/folder.go index 33895cd..50428b9 100644 --- a/helper/folder.go +++ b/helper/folder.go @@ -2,6 +2,7 @@ package helper import ( "context" + "fmt" "github.com/speatzle/go-passbolt/api" ) @@ -12,27 +13,44 @@ func CreateFolder(ctx context.Context, c *api.Client, folderParentID, name strin Name: name, FolderParentID: folderParentID, }) - return f.ID, err + if err != nil { + return "", fmt.Errorf("Creating Folder: %w", err) + } + return f.ID, nil } // GetFolder Gets a Folder func GetFolder(ctx context.Context, c *api.Client, folderID string) (string, string, error) { f, err := c.GetFolder(ctx, folderID) - return f.FolderParentID, f.Name, err + if err != nil { + return "", "", fmt.Errorf("Getting Folder: %w", err) + } + return f.FolderParentID, f.Name, nil } // UpdateFolder Updates a Folder func UpdateFolder(ctx context.Context, c *api.Client, folderID, name string) error { _, err := c.UpdateFolder(ctx, folderID, api.Folder{Name: name}) + if err != nil { + return fmt.Errorf("Updating Folder: %w", err) + } return err } // DeleteFolder Deletes a Folder func DeleteFolder(ctx context.Context, c *api.Client, folderID string) error { - return c.DeleteFolder(ctx, folderID) + err := c.DeleteFolder(ctx, folderID) + if err != nil { + return fmt.Errorf("Deleting Folder: %w", err) + } + return nil } // MoveFolder Moves a Folder into a Folder func MoveFolder(ctx context.Context, c *api.Client, folderID, folderParentID string) error { - return c.MoveFolder(ctx, folderID, folderParentID) + err := c.MoveFolder(ctx, folderID, folderParentID) + if err != nil { + return fmt.Errorf("Moving Folder: %w", err) + } + return nil } diff --git a/helper/resources.go b/helper/resources.go index 248b487..b5a2108 100644 --- a/helper/resources.go +++ b/helper/resources.go @@ -136,7 +136,7 @@ func UpdateResource(ctx context.Context, c *api.Client, resourceID, name, userna } opts := &api.GetUsersOptions{ - FilterHasAccess: resourceID, + FilterHasAccess: []string{resourceID}, } users, err := c.GetUsers(ctx, opts) if err != nil { @@ -201,10 +201,18 @@ func UpdateResource(ctx context.Context, c *api.Client, resourceID, name, userna // DeleteResource Deletes a Resource func DeleteResource(ctx context.Context, c *api.Client, resourceID string) error { - return c.DeleteResource(ctx, resourceID) + err := c.DeleteResource(ctx, resourceID) + if err != nil { + return fmt.Errorf("Deleting Resource: %w", err) + } + return nil } // MoveResource Moves a Resource into a Folder func MoveResource(ctx context.Context, c *api.Client, resourceID, folderParentID string) error { - return c.MoveResource(ctx, resourceID, folderParentID) + err := c.MoveResource(ctx, resourceID, folderParentID) + if err != nil { + return fmt.Errorf("Moveing Resource: %w", err) + } + return err } diff --git a/helper/user.go b/helper/user.go index 5d5fce0..b7d5723 100644 --- a/helper/user.go +++ b/helper/user.go @@ -24,7 +24,7 @@ func CreateUser(ctx context.Context, c *api.Client, role, username, firstname, l } if roleID == "" { - return "", fmt.Errorf("Cannot Find Role %v", role) + return "", fmt.Errorf("Cannot Find Role: %v", role) } u, err := c.CreateUser(ctx, api.User{ @@ -35,23 +35,26 @@ func CreateUser(ctx context.Context, c *api.Client, role, username, firstname, l }, RoleID: roleID, }) - return u.ID, err + if err != nil { + return "", fmt.Errorf("Creating User: %w", err) + } + return u.ID, nil } // GetUser Gets a User func GetUser(ctx context.Context, c *api.Client, userID string) (string, string, string, string, error) { u, err := c.GetUser(ctx, userID) if err != nil { - return "", "", "", "", fmt.Errorf("Get User %w", err) + return "", "", "", "", fmt.Errorf("Getting User: %w", err) } - return u.Username, u.Profile.FirstName, u.Profile.LastName, u.Role.Name, nil + return u.Role.Name, u.Username, u.Profile.FirstName, u.Profile.LastName, nil } // UpdateUser Updates a User func UpdateUser(ctx context.Context, c *api.Client, userID, role, firstname, lastname string) error { user, err := c.GetUser(ctx, userID) if err != nil { - return fmt.Errorf("Get User %w", err) + return fmt.Errorf("Getting User: %w", err) } new := api.User{ @@ -79,7 +82,7 @@ func UpdateUser(ctx context.Context, c *api.Client, userID, role, firstname, las if roleID == "" { return fmt.Errorf("Cannot Find Role %v", role) } - user.RoleID = roleID + new.RoleID = roleID } if firstname != "" { @@ -98,5 +101,9 @@ func UpdateUser(ctx context.Context, c *api.Client, userID, role, firstname, las // DeleteUser Deletes a User func DeleteUser(ctx context.Context, c *api.Client, userID string) error { - return c.DeleteUser(ctx, userID) + err := c.DeleteUser(ctx, userID) + if err != nil { + return fmt.Errorf("Deleting User: %w", err) + } + return nil } From 444a3d05834379077b2ab5b0fdb1afdad4a46dae Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Wed, 8 Sep 2021 15:17:33 +0200 Subject: [PATCH 18/74] make update functions keep old value if passed "" --- helper/group.go | 6 +++++ helper/resources.go | 55 ++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 56 insertions(+), 5 deletions(-) diff --git a/helper/group.go b/helper/group.go index e029b9f..1540a7c 100644 --- a/helper/group.go +++ b/helper/group.go @@ -84,9 +84,11 @@ func UpdateGroup(ctx context.Context, c *api.Client, groupID, name string, opera } var currentMemberships []api.GroupMembership + var currentName string for _, g := range groups { if g.ID == groupID { currentMemberships = g.GroupUsers + currentName = g.Name break } } @@ -100,6 +102,10 @@ func UpdateGroup(ctx context.Context, c *api.Client, groupID, name string, opera Secrets: []api.Secret{}, } + if name == "" { + request.Name = currentName + } + // Generate Group Membership changes based on current Group Memberships for _, operation := range operations { membership, err := getMembershipByUserID(currentMemberships, operation.UserID) diff --git a/helper/resources.go b/helper/resources.go index b5a2108..4457436 100644 --- a/helper/resources.go +++ b/helper/resources.go @@ -147,21 +147,66 @@ func UpdateResource(ctx context.Context, c *api.Client, resourceID, name, userna ID: resourceID, // This needs to be specified or it will revert to a legacy password ResourceTypeID: resource.ResourceTypeID, - Name: name, - Username: username, - URI: uri, + Name: resource.Name, + Username: resource.Username, + URI: resource.URI, + } + + if name != "" { + newResource.Name = name + } + if username != "" { + newResource.Username = username + } + if uri != "" { + newResource.URI = uri } var secretData string switch rType.Slug { case "password-string": - newResource.Description = description - secretData = password + newResource.Description = resource.Description + if description != "" { + newResource.Description = description + } + if password != "" { + secretData = password + } else { + secret, err := c.GetSecret(ctx, resourceID) + if err != nil { + return fmt.Errorf("Getting Secret: %w", err) + } + secretData, err = c.DecryptMessage(secret.Data) + if err != nil { + return fmt.Errorf("Decrypting Secret: %w", err) + } + } case "password-and-description": tmp := api.SecretDataTypePasswordAndDescription{ Password: password, Description: description, } + if password != "" || description != "" { + secret, err := c.GetSecret(ctx, resourceID) + if err != nil { + return fmt.Errorf("Getting Secret: %w", err) + } + oldSecretData, err := c.DecryptMessage(secret.Data) + if err != nil { + return fmt.Errorf("Decrypting Secret: %w", err) + } + var oldSecret api.SecretDataTypePasswordAndDescription + err = json.Unmarshal([]byte(oldSecretData), &oldSecret) + if err != nil { + return fmt.Errorf("Parsing Decrypted Secret Data: %w", err) + } + if password == "" { + tmp.Password = oldSecret.Password + } + if description == "" { + tmp.Description = oldSecret.Description + } + } res, err := json.Marshal(&tmp) if err != nil { return fmt.Errorf("Marshalling Secret Data: %w", err) From bdd57b482eab0908b8ab70e5b53658614f01e7ac Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Wed, 8 Sep 2021 16:00:59 +0200 Subject: [PATCH 19/74] Update Readme --- README.md | 84 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 55 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index e7503ab..228854e 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ A Go module to interact with [Passbolt](https://www.passbolt.com/), an open-source password manager for teams +There also is a CLI Tool to interact with Passbolt using this module [here](https://speatzle/go-passbolt-cli). + This module tries to support the latest Passbolt Community/PRO server release, PRO Features such as folders are supported. Older versions of Passbolt such as v2 are unsupported (it's a password manager, please update it) This module is divided into two packages: API and helper. @@ -119,7 +121,7 @@ Here we specify that we want to filter by favorites and that the response should ```go favorites, err := client.GetResources(ctx, &api.GetResourcesOptions{ FilterIsFavorite: true, - ContainPermissions: true, + ContainPermissions: true, }) ``` @@ -136,7 +138,7 @@ Groups: ```go groups, err := client.GetGroups(ctx, &api.GetGroupsOptions{ - FilterHasUsers: []string{"id of user", "id of other user"}, + FilterHasUsers: []string{"id of user", "id of other user"}, ContainUser: true, }) ``` @@ -167,22 +169,52 @@ folderParentID, name, username, uri, password, description, err := helper.GetRes The helper package has a function to save you from dealing with resource types when updating a resource: ```go -err := helper.UpdateResource(ctx, client,"resource id", "name", "username", "https://test.example.com", "pass123", "very descriptive") +err = helper.UpdateResource( + ctx, // Context + client, // API Client + "id", // Resource ID + "name", // Name + "username", // Username + "url", // URI + "strong", // Password + "very strong", // Description +) ``` -Note: As groups are also complicated to update there will be a helper function for them in the future. - -For other less complicated updates you can simply use the client directly: +The same goes for Groups: ```go -client.UpdateUser(ctx, "user id", api.User{ - Profile: &api.Profile{ - FirstName: "Test", - LastName: "User", - }, -}) +err = helper.UpdateGroup( + ctx, // Context + client, // API Client + "id", // Group ID + "name", // Group Name + []helper.GroupMembershipOperation{ + { + UserID: "id", // ID of User to Add/Modify/Delete + IsGroupManager: true, // Should User be a Group Manager + Delete: false, // Should this User be Remove from the Group + }, + } +) ``` +And for Users: + +```go +err = helper.UpdateUser( + ctx, // Context + client, // API Client + "id", // User ID + "user", // Role (user or admin) + "firstname", // FirstName + "lastname", // LastName +) +``` +Note: These helpers will only update fields that are not "". + +Helper update functions also exists for Folders. + ## Sharing As sharing resources is very complicated there are multiple helper functions. @@ -196,7 +228,7 @@ The `permissionType` can be: | `1` | "Read-only" | | `7` | "Can update" | | `15` | "Owner" | -| `-1` | If you want to delete existing permissions | +| `-1` | Delete existing permission | The `ShareResourceWithUsersAndGroups` function shares the resource with all provided users and groups with the given `permissionType`. @@ -256,20 +288,18 @@ err := client.MoveResource(ctx, "resource id", "parent folder id") err := client.MoveFolder(ctx, "folder id", "parent folder id") ``` -## Groups - - -Groups are extra complicated, it doesn't help that the Passbolt documentation is wrong and missing important details. - -Since helper functions for groups were added you can now create, get, update and delete groups easily: +## Setup +You can setup a Account using a Invite Link like this: ```go -err := helper.UpdateGroup(ctx, client, "group id", "group name", []helper.GroupMembershipOperation{ - { - UserID: "user id", - IsGroupManager: true, - }, -}) +// Get the UserID and Token from the Invite Link +userID, token, err := ParseInviteUrl(url) + +// Make a Client for Registration +rClient, err := api.NewClient(nil, "", "https://localhost", "", "") + +// Complete Account Setup +privkey, err := SetupAccount(ctx, rClient, userID, token, "password123") ``` ## Other @@ -362,7 +392,3 @@ func main() { client.Logout(ctx) } ``` - -# TODO -- write more integration tests -- add the ability to verify a server From 5fbed0b4d5464cc98038b9f46a36a5d0ff5bfc09 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Fri, 10 Sep 2021 10:18:00 +0200 Subject: [PATCH 20/74] update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 228854e..0878f3e 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ A Go module to interact with [Passbolt](https://www.passbolt.com/), an open-source password manager for teams -There also is a CLI Tool to interact with Passbolt using this module [here](https://speatzle/go-passbolt-cli). +There also is a CLI Tool to interact with Passbolt using this module [here](https://github.com/speatzle/go-passbolt-cli). This module tries to support the latest Passbolt Community/PRO server release, PRO Features such as folders are supported. Older versions of Passbolt such as v2 are unsupported (it's a password manager, please update it) From f3a8d4a05f8be1bd03ca5042f4c4964ab1253583 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Thu, 16 Sep 2021 13:41:07 +0200 Subject: [PATCH 21/74] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 0878f3e..5e83527 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,9 @@ To use the API package, please read the [Passbolt API docs](https://help.passbol PR's are welcome. But be gentle: if it's something bigger or fundamental: please [create an issue](https://github.com/speatzle/go-passbolt/issues/new) and ask first. +Disclaimer: This project is community driven and not associated with Passbolt SA + + # Install `go get github.com/speatzle/go-passbolt` From 7e96b96c36f8835b779440279ce3cdd09d826bad Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Thu, 16 Sep 2021 14:17:03 +0200 Subject: [PATCH 22/74] Add Folder Get Options for Get Calls --- api/folders.go | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/api/folders.go b/api/folders.go index 961c788..49926eb 100644 --- a/api/folders.go +++ b/api/folders.go @@ -38,6 +38,20 @@ type GetFoldersOptions struct { FilterSearch string `url:"filter[search],omitempty"` } +// GetFolderOptions are all available query parameters +type GetFolderOptions struct { + ContainChildrenResources bool `url:"contain[children_resources],omitempty"` + ContainChildrenFolders bool `url:"contain[children_folders],omitempty"` + ContainCreator bool `url:"contain[creator],omitempty"` + ContainCreatorProfile bool `url:"contain[creator.profile],omitempty"` + ContainModifier bool `url:"contain[modifier],omitempty"` + ContainModiferProfile bool `url:"contain[modifier.profile],omitempty"` + ContainPermission bool `url:"contain[permission],omitempty"` + ContainPermissions bool `url:"contain[permissions],omitempty"` + ContainPermissionUserProfile bool `url:"contain[permissions.user.profile],omitempty"` + ContainPermissionGroup bool `url:"contain[permissions.group],omitempty"` +} + // GetFolders gets all Folders from the Passboltserver func (c *Client) GetFolders(ctx context.Context, opts *GetFoldersOptions) ([]Folder, error) { msg, err := c.DoCustomRequest(ctx, "GET", "/folders.json", "v2", nil, opts) @@ -68,8 +82,8 @@ func (c *Client) CreateFolder(ctx context.Context, folder Folder) (*Folder, erro } // GetFolder gets a Passbolt Folder -func (c *Client) GetFolder(ctx context.Context, folderID string) (*Folder, error) { - msg, err := c.DoCustomRequest(ctx, "GET", "/folders/"+folderID+".json", "v2", nil, nil) +func (c *Client) GetFolder(ctx context.Context, folderID string, opts *GetFolderOptions) (*Folder, error) { + msg, err := c.DoCustomRequest(ctx, "GET", "/folders/"+folderID+".json", "v2", nil, opts) if err != nil { return nil, err } From 9bceb71ed2f190b20743d55af7607ba582fa30ab Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Thu, 16 Sep 2021 14:18:01 +0200 Subject: [PATCH 23/74] Use Folder Get Function for Permissions --- helper/share.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/helper/share.go b/helper/share.go index 3294cea..4365f4c 100644 --- a/helper/share.go +++ b/helper/share.go @@ -126,12 +126,14 @@ func ShareFolderWithUsersAndGroups(ctx context.Context, c *api.Client, folderID // 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) + oldFolder, err := c.GetFolder(ctx, folderID, &api.GetFolderOptions{ + ContainPermissions: true, + }) if err != nil { return fmt.Errorf("Getting Folder Permissions: %w", err) } - permissionChanges, err := GeneratePermissionChanges(oldPermissions, changes) + permissionChanges, err := GeneratePermissionChanges(oldFolder.Permissions, changes) if err != nil { return fmt.Errorf("Generating Folder Permission Changes: %w", err) } From 940828965a2cdd2e9cdd8b0ac5b5d675fc3dcd27 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Thu, 16 Sep 2021 14:18:16 +0200 Subject: [PATCH 24/74] add missing nil --- helper/folder.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helper/folder.go b/helper/folder.go index 50428b9..6243ff0 100644 --- a/helper/folder.go +++ b/helper/folder.go @@ -21,7 +21,7 @@ func CreateFolder(ctx context.Context, c *api.Client, folderParentID, name strin // GetFolder Gets a Folder func GetFolder(ctx context.Context, c *api.Client, folderID string) (string, string, error) { - f, err := c.GetFolder(ctx, folderID) + f, err := c.GetFolder(ctx, folderID, nil) if err != nil { return "", "", fmt.Errorf("Getting Folder: %w", err) } From f6b82dcbe9871074b4ba5bc0a651eb4730cce54e Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Thu, 16 Sep 2021 14:18:38 +0200 Subject: [PATCH 25/74] remove non existant API function --- api/permissions.go | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/api/permissions.go b/api/permissions.go index d249c98..e60edf5 100644 --- a/api/permissions.go +++ b/api/permissions.go @@ -33,18 +33,3 @@ func (c *Client) GetResourcePermissions(ctx context.Context, resourceID string) } return permissions, nil } - -// GetFolderPermissions gets a Folders Permissions -func (c *Client) GetFolderPermissions(ctx context.Context, folderID string) ([]Permission, error) { - msg, err := c.DoCustomRequest(ctx, "GET", "/permissions/folder/"+folderID+".json", "v2", nil, nil) - if err != nil { - return nil, err - } - - var permissions []Permission - err = json.Unmarshal(msg.Body, &permissions) - if err != nil { - return nil, err - } - return permissions, nil -} From 27715fd266a608f4f382346c2223372fb81100d8 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Thu, 16 Sep 2021 14:20:51 +0200 Subject: [PATCH 26/74] Add function to offline decode Resources --- helper/resources.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/helper/resources.go b/helper/resources.go index 4457436..9d0361a 100644 --- a/helper/resources.go +++ b/helper/resources.go @@ -94,8 +94,14 @@ func GetResource(ctx context.Context, c *api.Client, resourceID string) (folderP if err != nil { return "", "", "", "", "", "", fmt.Errorf("Getting Resource Secret: %w", err) } + return GetResourceFromData(c, *resource, *secret, *rType) +} + +// GetResourceFromData Decrypts Resources using only local data, the Resource object must inlude the secret +func GetResourceFromData(c *api.Client, resource api.Resource, secret api.Secret, rType api.ResourceType) (folderParentID, name, username, uri, password, description string, err error) { var pw string var desc string + switch rType.Slug { case "password-string": pw, err = c.DecryptMessage(secret.Data) From c3f7f9ac1b9c10f5d334d8c4982f780b92c0752f Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Thu, 16 Sep 2021 14:55:52 +0200 Subject: [PATCH 27/74] Error When Group Membership already Exists --- helper/group.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/helper/group.go b/helper/group.go index 1540a7c..ab76118 100644 --- a/helper/group.go +++ b/helper/group.go @@ -120,6 +120,9 @@ func UpdateGroup(ctx context.Context, c *api.Client, groupID, name string, opera }) } else { // Membership Exists so we can modify or delete it + if !operation.Delete && membership.IsAdmin == operation.IsGroupManager { + return fmt.Errorf("Membership for User %v already Exists with Same Role", operation.UserID) + } request.GroupChanges = append(request.GroupChanges, api.GroupMembership{ ID: membership.ID, IsAdmin: operation.IsGroupManager, From 21c833b742d7694e52fe04fd784d6ccd874af6ef Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Mon, 20 Sep 2021 09:20:41 +0200 Subject: [PATCH 28/74] add support for MFA via callback --- api/api.go | 19 +++++++++++++++++++ api/client.go | 3 +++ 2 files changed, 22 insertions(+) diff --git a/api/api.go b/api/api.go index 15225d8..ff57891 100644 --- a/api/api.go +++ b/api/api.go @@ -32,6 +32,8 @@ func (c *Client) DoCustomRequest(ctx context.Context, method, path, version stri // DoCustomRequestAndReturnRawResponse Executes a Custom Request and returns a APIResponse and the Raw HTTP Response func (c *Client) DoCustomRequestAndReturnRawResponse(ctx context.Context, method, path, version string, body interface{}, opts interface{}) (*http.Response, *APIResponse, error) { + firstTime := true +start: u, err := addOptions(path, version, opts) if err != nil { return nil, nil, fmt.Errorf("Adding Request Options: %w", err) @@ -51,6 +53,23 @@ func (c *Client) DoCustomRequestAndReturnRawResponse(ctx context.Context, method if res.Header.Status == "success" { return r, &res, nil } else if res.Header.Status == "error" { + if res.Header.Code == 403 && res.Header.Message == "MFA authentication is required." { + if !firstTime { + // if we are here this probably means that the MFA callback is broken, to prevent a infinit loop lets error here + return r, &res, fmt.Errorf("Got MFA challenge twice in a row, is your MFA Callback broken? Bailing to prevent loop...:") + } + if c.mfaCallback != nil { + err = c.mfaCallback(c, &res) + if err != nil { + return r, &res, fmt.Errorf("MFA Callback: %w", err) + } + // ok, we got the MFA challange and the callback presumably handeld it so we can retry the original request + firstTime = false + goto start + } else { + return r, &res, fmt.Errorf("Got MFA Challange but the MFA callback is not defined") + } + } return r, &res, fmt.Errorf("%w: Message: %v, Body: %v", ErrAPIResponseErrorStatusCode, res.Header.Message, string(res.Body)) } else { return r, &res, fmt.Errorf("%w: Message: %v, Body: %v", ErrAPIResponseUnknownStatusCode, res.Header.Message, string(res.Body)) diff --git a/api/client.go b/api/client.go index 210e8eb..26bd2d7 100644 --- a/api/client.go +++ b/api/client.go @@ -29,6 +29,8 @@ type Client struct { userPublicKey string userID string + mfaCallback func(c *Client, res *APIResponse) error + // Enable Debug Logging Debug bool } @@ -147,6 +149,7 @@ func (c *Client) do(ctx context.Context, req *http.Request, v *APIResponse) (*ht if err != nil { return resp, fmt.Errorf("Unable to Parse JSON API Response with HTTP Status Code %v: %w", resp.StatusCode, err) } + return resp, nil } From f01926b1c5ba9189e7ee0ce0a78e512547567d33 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Mon, 20 Sep 2021 10:23:59 +0200 Subject: [PATCH 29/74] add MFA structs, Make Callback Public --- api/api.go | 4 ++-- api/client.go | 3 ++- api/mfa.go | 13 +++++++++++++ 3 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 api/mfa.go diff --git a/api/api.go b/api/api.go index ff57891..75835b2 100644 --- a/api/api.go +++ b/api/api.go @@ -58,8 +58,8 @@ start: // if we are here this probably means that the MFA callback is broken, to prevent a infinit loop lets error here return r, &res, fmt.Errorf("Got MFA challenge twice in a row, is your MFA Callback broken? Bailing to prevent loop...:") } - if c.mfaCallback != nil { - err = c.mfaCallback(c, &res) + if c.MFACallback != nil { + err = c.MFACallback(ctx, c, &res) if err != nil { return r, &res, fmt.Errorf("MFA Callback: %w", err) } diff --git a/api/client.go b/api/client.go index 26bd2d7..161446e 100644 --- a/api/client.go +++ b/api/client.go @@ -29,7 +29,8 @@ type Client struct { userPublicKey string userID string - mfaCallback func(c *Client, res *APIResponse) error + // used for solving MFA challanges. You can block this to for example wait for user input. You shouden't run any unrelated API Calls while you are in this callback. + MFACallback func(ctx context.Context, c *Client, res *APIResponse) error // Enable Debug Logging Debug bool diff --git a/api/mfa.go b/api/mfa.go new file mode 100644 index 0000000..113e3d3 --- /dev/null +++ b/api/mfa.go @@ -0,0 +1,13 @@ +package api + +type MFAChallange struct { + Provider MFAProviders `json:"providers,omitempty"` +} + +type MFAProviders struct { + TOTP string `json:"totp,omitempty"` +} + +type MFAChallangeResponse struct { + TOTP string `json:"totp,omitempty"` +} From 663f5f6b7617919acf0662b622c8cf3f4c493951 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Mon, 20 Sep 2021 10:24:52 +0200 Subject: [PATCH 30/74] add totp generator from: https://github.com/yitsushi/totp-cli/blob/master/security/otp.go --- helper/totp.go | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 helper/totp.go diff --git a/helper/totp.go b/helper/totp.go new file mode 100644 index 0000000..42cf7b9 --- /dev/null +++ b/helper/totp.go @@ -0,0 +1,60 @@ +package helper + +import ( + "crypto/hmac" + "crypto/sha1" + "encoding/base32" + "encoding/binary" + "fmt" + "math" + "strings" + "time" +) + +const ( + mask1 = 0xf + mask2 = 0x7f + mask3 = 0xff + timeSplitInSeconds = 30 + shift24 = 24 + shift16 = 16 + shift8 = 8 + codeLength = 6 +) + +// GenerateOTPCode generates a 6 digit TOTP from the secret Token. +func GenerateOTPCode(token string, when time.Time) (string, error) { + timer := uint64(math.Floor(float64(when.Unix()) / float64(timeSplitInSeconds))) + // Remove spaces, some providers are giving us in a readable format + // so they add spaces in there. If it's not removed while pasting in, + // remove it now. + token = strings.Replace(token, " ", "", -1) + + // It should be uppercase always + token = strings.ToUpper(token) + + secretBytes, err := base32.StdEncoding.WithPadding(base32.NoPadding).DecodeString(token) + if err != nil { + return "", fmt.Errorf("Decoding token string: %w", err) + } + + buf := make([]byte, 8) + mac := hmac.New(sha1.New, secretBytes) + + binary.BigEndian.PutUint64(buf, timer) + _, _ = mac.Write(buf) + sum := mac.Sum(nil) + + // http://tools.ietf.org/html/rfc4226#section-5.4 + offset := sum[len(sum)-1] & mask1 + value := int64(((int(sum[offset]) & mask2) << shift24) | + ((int(sum[offset+1] & mask3)) << shift16) | + ((int(sum[offset+2] & mask3)) << shift8) | + (int(sum[offset+3]) & mask3)) + + modulo := int32(value % int64(math.Pow10(codeLength))) + + format := fmt.Sprintf("%%0%dd", codeLength) + + return fmt.Sprintf(format, modulo), nil +} From 7fdad5269b3669ec418bb814b34176dfcb4f86ca Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Mon, 20 Sep 2021 11:20:18 +0200 Subject: [PATCH 31/74] move csrf token to custom request for mfa --- api/api.go | 11 ++++++++++- api/auth.go | 22 +++++++++++----------- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/api/api.go b/api/api.go index 75835b2..c098021 100644 --- a/api/api.go +++ b/api/api.go @@ -50,6 +50,15 @@ start: return r, &res, fmt.Errorf("Doing Request: %w", err) } + // Because of MFA i need to do the csrf token stuff here + if c.csrfToken.Name == "" { + for _, cookie := range r.Cookies() { + if cookie.Name == "csrfToken" { + c.csrfToken = *cookie + } + } + } + if res.Header.Status == "success" { return r, &res, nil } else if res.Header.Status == "error" { @@ -59,7 +68,7 @@ start: return r, &res, fmt.Errorf("Got MFA challenge twice in a row, is your MFA Callback broken? Bailing to prevent loop...:") } if c.MFACallback != nil { - err = c.MFACallback(ctx, c, &res) + c.mfaToken, err = c.MFACallback(ctx, c, &res) if err != nil { return r, &res, fmt.Errorf("MFA Callback: %w", err) } diff --git a/api/auth.go b/api/auth.go index 5b9c2fd..2f85fbc 100644 --- a/api/auth.go +++ b/api/auth.go @@ -56,6 +56,7 @@ func (c *Client) CheckSession(ctx context.Context) bool { // Login gets a Session and CSRF Token from Passbolt and Stores them in the Clients Cookie Jar func (c *Client) Login(ctx context.Context) error { + c.csrfToken = http.Cookie{} if c.userPrivateKey == "" { return fmt.Errorf("Client has no Private Key") @@ -119,24 +120,23 @@ func (c *Client) Login(ctx context.Context) error { return fmt.Errorf("Cannot Find Session Cookie!") } - // Do Mfa Here if ever - // You have to get a make GET Request to get the CSRF Token which is Required for Write Operations - msg, apiMsg, err := c.DoCustomRequestAndReturnRawResponse(ctx, "GET", "/users/me.json", "v2", nil, nil) + apiMsg, err := c.DoCustomRequest(ctx, "GET", "/users/me.json", "v2", nil, nil) if err != nil { - c.log("is MFA Enabled? That is not yet Supported!") return fmt.Errorf("Getting CSRF Token: %w", err) } - for _, cookie := range msg.Cookies() { - if cookie.Name == "csrfToken" { - c.csrfToken = *cookie + // Because of MFA, the custom Request Functin now Fetches the CSRF token, we still need the user for his public key + /* + for _, cookie := range msg.Cookies() { + if cookie.Name == "csrfToken" { + c.csrfToken = *cookie + } } - } - if c.csrfToken.Name == "" { - return fmt.Errorf("Cannot Find csrfToken Cookie!") - } + if c.csrfToken.Name == "" { + return fmt.Errorf("Cannot Find csrfToken Cookie!") + }*/ // Get Users Own Public Key from Server var user User From 151bd9643ba525f54d1c6cf346939193b44b2891 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Mon, 20 Sep 2021 11:20:43 +0200 Subject: [PATCH 32/74] add mfa Cookie to storage --- api/client.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/api/client.go b/api/client.go index 161446e..aeb2559 100644 --- a/api/client.go +++ b/api/client.go @@ -22,6 +22,7 @@ type Client struct { sessionToken http.Cookie csrfToken http.Cookie + mfaToken http.Cookie // for some reason []byte is used for Passwords in gopenpgp instead of string like they do for keys... userPassword []byte @@ -29,8 +30,10 @@ type Client struct { userPublicKey string userID string - // used for solving MFA challanges. You can block this to for example wait for user input. You shouden't run any unrelated API Calls while you are in this callback. - MFACallback func(ctx context.Context, c *Client, res *APIResponse) error + // used for solving MFA challanges. You can block this to for example wait for user input. + // You shouden't run any unrelated API Calls while you are in this callback. + // 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) // Enable Debug Logging Debug bool @@ -111,6 +114,9 @@ func (c *Client) newRequest(method, path string, body interface{}) (*http.Reques req.Header.Set("X-CSRF-Token", c.csrfToken.Value) req.AddCookie(&c.sessionToken) req.AddCookie(&c.csrfToken) + if c.mfaToken.Name != "" { + req.AddCookie(&c.mfaToken) + } // Debugging c.log("Request URL: %v", req.URL.String()) From 8bccb80cb25681294d7905dd3a1aa32f26252039 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Mon, 20 Sep 2021 11:22:02 +0200 Subject: [PATCH 33/74] add MFA TOTP Reference Implementation --- helper/mfa.go | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 helper/mfa.go diff --git a/helper/mfa.go b/helper/mfa.go new file mode 100644 index 0000000..d57623d --- /dev/null +++ b/helper/mfa.go @@ -0,0 +1,54 @@ +package helper + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "net/http" + "time" + + "github.com/speatzle/go-passbolt/api" +) + +// AddMFACallbackTOTP adds a MFA callback to the client that generates OTP Codes on demand using a Token with configurable retries and delay +func AddMFACallbackTOTP(c *api.Client, retrys uint, retryDelay, offset time.Duration, token string) { + c.MFACallback = func(ctx context.Context, c *api.Client, res *api.APIResponse) (http.Cookie, error) { + challange := api.MFAChallange{} + err := json.Unmarshal(res.Body, &challange) + if err != nil { + return http.Cookie{}, fmt.Errorf("Parsing MFA Challange") + } + if challange.Provider.TOTP == "" { + return http.Cookie{}, fmt.Errorf("Server Provided no TOTP Provider") + } + for i := uint(0); i < retrys+1; i++ { + var code string + code, err = GenerateOTPCode(token, time.Now().Add(offset)) + if err != nil { + return http.Cookie{}, fmt.Errorf("Error Generating MFA Code: %w", err) + } + req := api.MFAChallangeResponse{ + TOTP: code, + } + var raw *http.Response + raw, _, err = c.DoCustomRequestAndReturnRawResponse(ctx, "POST", "mfa/verify/totp.json", "v2", req, nil) + if err != nil { + if errors.Unwrap(err) != api.ErrAPIResponseErrorStatusCode { + return http.Cookie{}, fmt.Errorf("Doing MFA Challange Response: %w", err) + } + // MFA failed, so lets wait just let the loop try again + time.Sleep(retryDelay) + } else { + // MFA worked so lets find the cookie and return it + for _, cookie := range raw.Cookies() { + if cookie.Name == "passbolt_mfa" { + return *cookie, nil + } + } + return http.Cookie{}, fmt.Errorf("Unable to find Passbolt MFA Cookie") + } + } + return http.Cookie{}, fmt.Errorf("Failed MFA Challange 3 times: %w", err) + } +} From 43193345fa1388165cbe6fcbe1f954834a42c803 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Wed, 22 Sep 2021 10:11:55 +0200 Subject: [PATCH 34/74] Added ServerVerification Functions, Minor Cleanup --- api/auth.go | 69 +-------------------------------------------------- api/client.go | 32 ++++++++++++++++++++++++ api/misc.go | 28 +++++++++++++++++++++ api/verify.go | 64 +++++++++++++++++++++++++++++++++++++++++++++++ go.mod | 1 + go.sum | 2 ++ 6 files changed, 128 insertions(+), 68 deletions(-) create mode 100644 api/verify.go diff --git a/api/auth.go b/api/auth.go index 2f85fbc..5830459 100644 --- a/api/auth.go +++ b/api/auth.go @@ -6,19 +6,12 @@ import ( "fmt" "net/http" "net/url" - "strconv" "strings" "github.com/ProtonMail/gopenpgp/v2/crypto" "github.com/ProtonMail/gopenpgp/v2/helper" ) -// PublicKeyReponse the Body of a Public Key Api Request -type PublicKeyReponse struct { - Fingerprint string `json:"fingerprint"` - Keydata string `json:"keydata"` -} - // Login is used for login type Login struct { Auth *GPGAuth `json:"gpg_auth"` @@ -30,24 +23,6 @@ type GPGAuth struct { Token string `json:"user_token_result,omitempty"` } -// TODO add Server Verification Function - -// GetPublicKey gets the Public Key and Fingerprint of the Passbolt instance -func (c *Client) GetPublicKey(ctx context.Context) (string, string, error) { - msg, err := c.DoCustomRequest(ctx, "GET", "auth/verify.json", "v2", nil, nil) - if err != nil { - return "", "", fmt.Errorf("Doing Request: %w", err) - } - - var body PublicKeyReponse - err = json.Unmarshal(msg.Body, &body) - if err != nil { - return "", "", fmt.Errorf("Parsing JSON: %w", err) - } - // TODO check if that Fingerpirnt is actually from the Publickey - return body.Keydata, body.Fingerprint, nil -} - // CheckSession Check to see if you have a Valid Session func (c *Client) CheckSession(ctx context.Context) bool { _, err := c.DoCustomRequest(ctx, "GET", "auth/is-authenticated.json", "v2", nil, nil) @@ -120,24 +95,12 @@ func (c *Client) Login(ctx context.Context) error { return fmt.Errorf("Cannot Find Session Cookie!") } - // You have to get a make GET Request to get the CSRF Token which is Required for Write Operations + // Because of MFA, the custom Request Function now Fetches the CSRF token, we still need the user for his public key apiMsg, err := c.DoCustomRequest(ctx, "GET", "/users/me.json", "v2", nil, nil) if err != nil { return fmt.Errorf("Getting CSRF Token: %w", err) } - // Because of MFA, the custom Request Functin now Fetches the CSRF token, we still need the user for his public key - /* - for _, cookie := range msg.Cookies() { - if cookie.Name == "csrfToken" { - c.csrfToken = *cookie - } - } - - if c.csrfToken.Name == "" { - return fmt.Errorf("Cannot Find csrfToken Cookie!") - }*/ - // Get Users Own Public Key from Server var user User err = json.Unmarshal(apiMsg.Body, &user) @@ -176,33 +139,3 @@ func (c *Client) Logout(ctx context.Context) error { c.csrfToken = http.Cookie{} return nil } - -// GetUserID Gets the ID of the Current User -func (c *Client) GetUserID() string { - return c.userID -} - -func checkAuthTokenFormat(authToken string) error { - splitAuthToken := strings.Split(authToken, "|") - if len(splitAuthToken) != 4 { - return fmt.Errorf("Auth Token Has Wrong amount of Fields") - } - - if splitAuthToken[0] != splitAuthToken[3] { - return fmt.Errorf("Auth Token Version Fields Don't match") - } - - if !strings.HasPrefix(splitAuthToken[0], "gpgauth") { - return fmt.Errorf("Auth Token Version does not start with 'gpgauth'") - } - - length, err := strconv.Atoi(splitAuthToken[1]) - if err != nil { - return fmt.Errorf("Cannot Convert Auth Token Length Field to int: %w", err) - } - - if len(splitAuthToken[2]) != length { - return fmt.Errorf("Auth Token Data Length does not Match Length Field") - } - return nil -} diff --git a/api/client.go b/api/client.go index aeb2559..732cc32 100644 --- a/api/client.go +++ b/api/client.go @@ -39,6 +39,12 @@ type Client struct { Debug bool } +// PublicKeyReponse the Body of a Public Key Api Request +type PublicKeyReponse struct { + Fingerprint string `json:"fingerprint"` + Keydata string `json:"keydata"` +} + // NewClient Returns a new Passbolt Client. // if httpClient is nil http.DefaultClient will be used. // if UserAgent is "" "goPassboltClient/1.0" will be used. @@ -183,3 +189,29 @@ func addOptions(s, version string, opt interface{}) (string, error) { u.RawQuery = vs.Encode() return u.String(), nil } + +// GetUserID Gets the ID of the Current User +func (c *Client) GetUserID() string { + return c.userID +} + +// GetPublicKey gets the Public Key and Fingerprint of the Passbolt instance +func (c *Client) GetPublicKey(ctx context.Context) (string, string, error) { + msg, err := c.DoCustomRequest(ctx, "GET", "auth/verify.json", "v2", nil, nil) + if err != nil { + return "", "", fmt.Errorf("Doing Request: %w", err) + } + + var body PublicKeyReponse + err = json.Unmarshal(msg.Body, &body) + if err != nil { + return "", "", fmt.Errorf("Parsing JSON: %w", err) + } + + // Lets get the actual Fingerprint instead of trusting the Server + privateKeyObj, err := crypto.NewKeyFromArmored(c.userPrivateKey) + if err != nil { + return "", "", fmt.Errorf("Parsing Server Key: %w", err) + } + return body.Keydata, privateKeyObj.GetFingerprint(), nil +} diff --git a/api/misc.go b/api/misc.go index 9990219..99ee9fb 100644 --- a/api/misc.go +++ b/api/misc.go @@ -1,7 +1,10 @@ package api import ( + "fmt" "math/rand" + "strconv" + "strings" ) const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" @@ -13,3 +16,28 @@ func randStringBytesRmndr(length int) string { } return string(b) } + +func checkAuthTokenFormat(authToken string) error { + splitAuthToken := strings.Split(authToken, "|") + if len(splitAuthToken) != 4 { + return fmt.Errorf("Auth Token Has Wrong amount of Fields") + } + + if splitAuthToken[0] != splitAuthToken[3] { + return fmt.Errorf("Auth Token Version Fields Don't match") + } + + if !strings.HasPrefix(splitAuthToken[0], "gpgauth") { + return fmt.Errorf("Auth Token Version does not start with 'gpgauth'") + } + + length, err := strconv.Atoi(splitAuthToken[1]) + if err != nil { + return fmt.Errorf("Cannot Convert Auth Token Length Field to int: %w", err) + } + + if len(splitAuthToken[2]) != length { + return fmt.Errorf("Auth Token Data Length does not Match Length Field") + } + return nil +} diff --git a/api/verify.go b/api/verify.go new file mode 100644 index 0000000..c82732c --- /dev/null +++ b/api/verify.go @@ -0,0 +1,64 @@ +package api + +import ( + "context" + "fmt" + "strings" + + "github.com/ProtonMail/gopenpgp/v2/crypto" + "github.com/google/uuid" +) + +type GPGVerifyContainer struct { + Req GPGVerify `json:"gpg_auth"` +} + +// GPGVerify is used for verification +type GPGVerify struct { + KeyID string `json:"keyid"` + Token string `json:"server_verify_token,omitempty"` +} + +func (c *Client) SetupServerVerification(ctx context.Context) (string, string, error) { + serverKey, _, err := c.GetPublicKey(ctx) + if err != nil { + return "", "", fmt.Errorf("Getting Server Key: %w", err) + } + uuid, err := uuid.NewRandom() + if err != nil { + return "", "", fmt.Errorf("Generating UUID: %w", err) + } + token := "gpgauthv1.3.0|36|" + uuid.String() + "|gpgauthv1.3.0" + encToken, err := c.EncryptMessageWithPublicKey(serverKey, token) + if err != nil { + return "", "", fmt.Errorf("Encrypting Challange: %w", err) + } + err = c.VerifyServer(ctx, token, encToken) + if err != nil { + return "", "", fmt.Errorf("Initial Verification: %w", err) + } + return token, encToken, err +} + +func (c *Client) VerifyServer(ctx context.Context, token, encToken string) error { + privateKeyObj, err := crypto.NewKeyFromArmored(c.userPrivateKey) + if err != nil { + return fmt.Errorf("Parsing User Private Key: %w", err) + } + + data := GPGVerifyContainer{ + Req: GPGVerify{ + Token: encToken, + KeyID: privateKeyObj.GetFingerprint(), + }, + } + raw, _, err := c.DoCustomRequestAndReturnRawResponse(ctx, "POST", "/auth/verify.json", "v2", data, nil) + if err != nil && !strings.Contains(err.Error(), "The authentication failed.") { + return fmt.Errorf("Sending Verification Challange: %w", err) + } + + if raw.Header.Get("X-GPGAuth-Verify-Response") != token { + return fmt.Errorf("Server Response did not Match Saved Token") + } + return nil +} diff --git a/go.mod b/go.mod index 4166a1c..4454320 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/ProtonMail/go-crypto v0.0.0-20210707164159-52430bf6b52c // indirect github.com/ProtonMail/gopenpgp/v2 v2.2.2 github.com/google/go-querystring v1.1.0 + github.com/google/uuid v1.3.0 // indirect github.com/sirupsen/logrus v1.8.1 // indirect golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf // indirect diff --git a/go.sum b/go.sum index 7d914dc..3665dab 100644 --- a/go.sum +++ b/go.sum @@ -13,6 +13,8 @@ github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= From 6eb5734169d9710f7cb0a96cb84fd4378f825a3e Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Wed, 22 Sep 2021 10:15:14 +0200 Subject: [PATCH 35/74] add comment, tidy go mod --- api/verify.go | 1 + go.mod | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/api/verify.go b/api/verify.go index c82732c..638ee42 100644 --- a/api/verify.go +++ b/api/verify.go @@ -9,6 +9,7 @@ import ( "github.com/google/uuid" ) +// GPGVerifyContainer is used for verification type GPGVerifyContainer struct { Req GPGVerify `json:"gpg_auth"` } diff --git a/go.mod b/go.mod index 4454320..9317705 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/ProtonMail/go-crypto v0.0.0-20210707164159-52430bf6b52c // indirect github.com/ProtonMail/gopenpgp/v2 v2.2.2 github.com/google/go-querystring v1.1.0 - github.com/google/uuid v1.3.0 // indirect + github.com/google/uuid v1.3.0 github.com/sirupsen/logrus v1.8.1 // indirect golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf // indirect From 0e7cca97a2ae42a9e2b76be8265ae74fab63c8b2 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Wed, 22 Sep 2021 10:29:08 +0200 Subject: [PATCH 36/74] Update README for MFA and Server Verification --- README.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/README.md b/README.md index 5e83527..68516de 100644 --- a/README.md +++ b/README.md @@ -305,6 +305,30 @@ rClient, err := api.NewClient(nil, "", "https://localhost", "", "") privkey, err := SetupAccount(ctx, rClient, userID, token, "password123") ``` +## Verification + +You can Verify that the Server hasen't changed, for that you need to initially setup the Verification and save the returned values. Then you can Verify that the serverkey hasen't changed since you setup the Verification. + +```go +// Setup the Verification +token, encToken, err := client.SetupServerVerification(ctx) +if err != nil { + panic(err) +} +// You Need to save these +fmt.Println("Token: ", token) +fmt.Println("enc Token: ", encToken) +// Now you can Verify the Server +err = client.VerifyServer(ctx, token, encToken) +if err != nil { + panic(err) +} +``` + +## MFA + +go-passbolt now supports MFA! You can set it up using the Client's `MFACallback` function, it will provide everything you need to complete any MFA challanges. When your done you just need to return the new MFA Cookie (usually called passbolt_mfa). The helper package has a example implementation for a noninteractive TOTP Setup under helper/mfa.go in the function `AddMFACallbackTOTP`. + ## Other These examples are just the main use cases of these Modules, many more API calls are supported. Look at the [reference](https://pkg.go.dev/github.com/speatzle/go-passbolt) for more information. From 8e4637492dcb0a6bbb60b4192588888e59044b24 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Wed, 22 Sep 2021 13:09:11 +0200 Subject: [PATCH 37/74] Add missing Comments --- README.md | 2 +- api/verify.go | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 68516de..50ecbf6 100644 --- a/README.md +++ b/README.md @@ -307,7 +307,7 @@ privkey, err := SetupAccount(ctx, rClient, userID, token, "password123") ## Verification -You can Verify that the Server hasen't changed, for that you need to initially setup the Verification and save the returned values. Then you can Verify that the serverkey hasen't changed since you setup the Verification. +You can Verify that the Server hasen't changed, for that you need to initially setup the Verification and save the returned values. Then you can Verify that the serverkey hasen't changed since you setup the Verification. Note this Only Works if the client is not logged in. ```go // Setup the Verification diff --git a/api/verify.go b/api/verify.go index 638ee42..b8d42d4 100644 --- a/api/verify.go +++ b/api/verify.go @@ -20,6 +20,7 @@ type GPGVerify struct { Token string `json:"server_verify_token,omitempty"` } +// SetupServerVerification sets up Server Verification, Only works before login func (c *Client) SetupServerVerification(ctx context.Context) (string, string, error) { serverKey, _, err := c.GetPublicKey(ctx) if err != nil { @@ -41,6 +42,7 @@ func (c *Client) SetupServerVerification(ctx context.Context) (string, string, e return token, encToken, err } +// VerifyServer verifys that the Server is still the same one as during the Setup, Only works before login func (c *Client) VerifyServer(ctx context.Context, token, encToken string) error { privateKeyObj, err := crypto.NewKeyFromArmored(c.userPrivateKey) if err != nil { From f790467d288fdb166b0512ea3c9ed8cd8cc19881 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Thu, 18 Nov 2021 09:09:34 +0100 Subject: [PATCH 38/74] fix Base url path being overwritten --- api/api.go | 4 ++-- api/client.go | 27 ++++++++++----------------- 2 files changed, 12 insertions(+), 19 deletions(-) diff --git a/api/api.go b/api/api.go index c098021..409657e 100644 --- a/api/api.go +++ b/api/api.go @@ -34,9 +34,9 @@ func (c *Client) DoCustomRequest(ctx context.Context, method, path, version stri func (c *Client) DoCustomRequestAndReturnRawResponse(ctx context.Context, method, path, version string, body interface{}, opts interface{}) (*http.Response, *APIResponse, error) { firstTime := true start: - u, err := addOptions(path, version, opts) + u, err := generateURL(*c.baseURL, path, version, opts) if err != nil { - return nil, nil, fmt.Errorf("Adding Request Options: %w", err) + return nil, nil, fmt.Errorf("Generating Path: %w", err) } req, err := c.newRequest(method, u, body) diff --git a/api/client.go b/api/client.go index 732cc32..150d0c7 100644 --- a/api/client.go +++ b/api/client.go @@ -9,6 +9,7 @@ import ( "io/ioutil" "net/http" "net/url" + "path" "github.com/ProtonMail/gopenpgp/v2/crypto" "github.com/google/go-querystring/query" @@ -93,13 +94,7 @@ func NewClient(httpClient *http.Client, UserAgent, BaseURL, UserPrivateKey, User return c, err } -func (c *Client) newRequest(method, path string, body interface{}) (*http.Request, error) { - rel, err := url.Parse(path) - if err != nil { - return nil, fmt.Errorf("Parsing URL: %w", err) - } - u := c.baseURL.ResolveReference(rel) - +func (c *Client) newRequest(method, url string, body interface{}) (*http.Request, error) { var buf io.ReadWriter if body != nil { buf = new(bytes.Buffer) @@ -108,7 +103,8 @@ func (c *Client) newRequest(method, path string, body interface{}) (*http.Reques return nil, fmt.Errorf("JSON Encoding Request: %w", err) } } - req, err := http.NewRequest(method, u.String(), buf) + + req, err := http.NewRequest(method, url, buf) if err != nil { return nil, fmt.Errorf("Creating HTTP Request: %w", err) } @@ -173,21 +169,18 @@ func (c *Client) log(msg string, args ...interface{}) { fmt.Printf("[go-passbolt] "+msg+"\n", args...) } -func addOptions(s, version string, opt interface{}) (string, error) { - u, err := url.Parse(s) - if err != nil { - return s, fmt.Errorf("Parsing URL: %w", err) - } +func generateURL(base url.URL, p, version string, opt interface{}) (string, error) { + base.Path = path.Join(base.Path, p) vs, err := query.Values(opt) if err != nil { - return s, fmt.Errorf("Getting URL Query Values: %w", err) + return "", fmt.Errorf("Getting URL Query Values: %w", err) } if version != "" { vs.Add("api-version", version) } - u.RawQuery = vs.Encode() - return u.String(), nil + base.RawQuery = vs.Encode() + return base.String(), nil } // GetUserID Gets the ID of the Current User @@ -197,7 +190,7 @@ func (c *Client) GetUserID() string { // GetPublicKey gets the Public Key and Fingerprint of the Passbolt instance func (c *Client) GetPublicKey(ctx context.Context) (string, string, error) { - msg, err := c.DoCustomRequest(ctx, "GET", "auth/verify.json", "v2", nil, nil) + msg, err := c.DoCustomRequest(ctx, "GET", "/auth/verify.json", "v2", nil, nil) if err != nil { return "", "", fmt.Errorf("Doing Request: %w", err) } From b0187ae8218986ec906e5ca340a41c2026d63eb9 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Thu, 18 Nov 2021 12:20:52 +0100 Subject: [PATCH 39/74] add PHPSESSID to Session Cookie Whitelist --- api/auth.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/api/auth.go b/api/auth.go index 5830459..c3cbe83 100644 --- a/api/auth.go +++ b/api/auth.go @@ -89,6 +89,9 @@ func (c *Client) Login(ctx context.Context) error { // Session Cookie in older Passbolt Versions } else if cookie.Name == "CAKEPHP" { c.sessionToken = *cookie + // Session Cookie in Cloud version? + } else if cookie.Name == "PHPSESSID" { + c.sessionToken = *cookie } } if c.sessionToken.Name == "" { From d84e7e7ad7d29c45e3902807f868e9edccf01274 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Fri, 21 Jan 2022 11:48:25 +0100 Subject: [PATCH 40/74] Added New GRoup Contains and Type --- api/groups.go | 43 +++++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/api/groups.go b/api/groups.go index 84a9e4c..74c25a3 100644 --- a/api/groups.go +++ b/api/groups.go @@ -7,14 +7,30 @@ import ( //Group is a Group type Group struct { - ID string `json:"id,omitempty"` - Name string `json:"name,omitempty"` - Created *Time `json:"created,omitempty"` - CreatedBy string `json:"created_by,omitempty"` - Deleted bool `json:"deleted,omitempty"` - Modified *Time `json:"modified,omitempty"` - ModifiedBy string `json:"modified_by,omitempty"` + ID string `json:"id,omitempty"` + Name string `json:"name,omitempty"` + Created *Time `json:"created,omitempty"` + CreatedBy string `json:"created_by,omitempty"` + Deleted bool `json:"deleted,omitempty"` + Modified *Time `json:"modified,omitempty"` + ModifiedBy string `json:"modified_by,omitempty"` + // This does not Contain Profile for Users Anymore... GroupUsers []GroupMembership `json:"groups_users,omitempty"` + // This is new and undocumented but as all the data + Users []GroupUser `json:"users,omitempty"` +} + +type GroupUser struct { + User + JoinData GroupJoinData `json:"_join_data,omitempty"` +} + +type GroupJoinData struct { + ID string `json:"id,omitempty"` + GroupID string `json:"group_id,omitempty"` + UserID string `json:"user_id,omitempty"` + IsAdmin bool `json:"is_admin,omitempty"` + Created *Time `json:"created,omitempty"` } type GroupMembership struct { @@ -38,11 +54,14 @@ type GetGroupsOptions struct { FilterHasUsers []string `url:"filter[has_users],omitempty"` FilterHasManagers []string `url:"filter[has-managers],omitempty"` - ContainModifier bool `url:"contain[modifier],omitempty"` - ContainModifierProfile bool `url:"contain[modifier.profile],omitempty"` - ContainUser bool `url:"contain[user],omitempty"` - ContainGroupUser bool `url:"contain[group_user],omitempty"` - ContainMyGroupUser bool `url:"contain[my_group_user],omitempty"` + ContainModifier bool `url:"contain[modifier],omitempty"` + ContainModifierProfile bool `url:"contain[modifier.profile],omitempty"` + ContainMyGroupUser bool `url:"contain[my_group_user],omitempty"` + ContainUsers bool `url:"contain[users],omitempty"` + ContainGroupsUsers bool `url:"contain[groups_users],omitempty"` + ContainGroupsUsersUser bool `url:"contain[groups_users.user],omitempty"` + ContainGroupsUsersUserProfile bool `url:"contain[groups_users.user.profile],omitempty"` + ContainGroupsUsersUserGPGKey bool `url:"contain[groups_users.user.gpgkey],omitempty"` } // UpdateGroupDryRunResult is the Result of a Update Group DryRun From f3fe6eb1c503551ae58d18eef8a2b97ef113859a Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Fri, 21 Jan 2022 11:49:39 +0100 Subject: [PATCH 41/74] Update Group Helpers to use new Group Contains --- helper/group.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/helper/group.go b/helper/group.go index ab76118..8edffa5 100644 --- a/helper/group.go +++ b/helper/group.go @@ -49,7 +49,9 @@ func CreateGroup(ctx context.Context, c *api.Client, name string, operations []G func GetGroup(ctx context.Context, c *api.Client, groupID string) (string, []GroupMembership, error) { // for some reason the groups index api call does not give back the groups_users even though it is supposed to, so i have to do this... groups, err := c.GetGroups(ctx, &api.GetGroupsOptions{ - ContainGroupUser: true, + ContainGroupsUsers: true, + ContainGroupsUsersUser: true, + ContainGroupsUsersUserProfile: true, }) if err != nil { return "", nil, fmt.Errorf("Getting Groups: %w", err) @@ -77,7 +79,7 @@ func GetGroup(ctx context.Context, c *api.Client, groupID string) (string, []Gro func UpdateGroup(ctx context.Context, c *api.Client, groupID, name string, operations []GroupMembershipOperation) error { // for some reason the groups index api call does not give back the groups_users even though it is supposed to, so i have to do this... groups, err := c.GetGroups(ctx, &api.GetGroupsOptions{ - ContainGroupUser: true, + ContainGroupsUsers: true, }) if err != nil { return fmt.Errorf("Getting Groups: %w", err) From f1122a019c4f2c6f0e21fca78835f2a967488df5 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Fri, 21 Jan 2022 13:46:26 +0100 Subject: [PATCH 42/74] add uuid check before insering into url --- api/comments.go | 19 ++++++++++++++++++- api/favorites.go | 11 ++++++++++- api/folders.go | 21 +++++++++++++++++++-- api/gpgkey.go | 5 +++++ api/groups.go | 19 ++++++++++++++++++- api/misc.go | 10 ++++++++++ api/permissions.go | 5 +++++ api/resource_types.go | 5 +++++ api/resources.go | 21 +++++++++++++++++++-- api/secrets.go | 5 +++++ api/setup.go | 15 ++++++++++++++- api/share.go | 17 +++++++++++++++-- api/users.go | 21 +++++++++++++++++++-- 13 files changed, 162 insertions(+), 12 deletions(-) diff --git a/api/comments.go b/api/comments.go index 05f4c02..b4c5521 100644 --- a/api/comments.go +++ b/api/comments.go @@ -3,6 +3,7 @@ package api import ( "context" "encoding/json" + "fmt" ) // Comment is a Comment @@ -29,6 +30,10 @@ type GetCommentsOptions struct { // GetComments gets all Passbolt Comments an The Specified Resource func (c *Client) GetComments(ctx context.Context, resourceID string, opts *GetCommentsOptions) ([]Comment, error) { + err := checkUUIDFormat(resourceID) + if err != nil { + return nil, fmt.Errorf("Checking ID format: %w", err) + } msg, err := c.DoCustomRequest(ctx, "GET", "/comments/resource/"+resourceID+".json", "v2", nil, opts) if err != nil { return nil, err @@ -44,6 +49,10 @@ func (c *Client) GetComments(ctx context.Context, resourceID string, opts *GetCo // CreateComment Creates a new Passbolt Comment func (c *Client) CreateComment(ctx context.Context, resourceID string, comment Comment) (*Comment, error) { + err := checkUUIDFormat(resourceID) + if err != nil { + return nil, fmt.Errorf("Checking ID format: %w", err) + } msg, err := c.DoCustomRequest(ctx, "POST", "/comments/resource/"+resourceID+".json", "v2", comment, nil) if err != nil { return nil, err @@ -58,6 +67,10 @@ func (c *Client) CreateComment(ctx context.Context, resourceID string, comment C // UpdateComment Updates a existing Passbolt Comment func (c *Client) UpdateComment(ctx context.Context, commentID string, comment Comment) (*Comment, error) { + err := checkUUIDFormat(commentID) + if err != nil { + return nil, fmt.Errorf("Checking ID format: %w", err) + } msg, err := c.DoCustomRequest(ctx, "PUT", "/comments/"+commentID+".json", "v2", comment, nil) if err != nil { return nil, err @@ -72,7 +85,11 @@ func (c *Client) UpdateComment(ctx context.Context, commentID string, comment Co // DeleteComment Deletes a Passbolt Comment func (c *Client) DeleteComment(ctx context.Context, commentID string) error { - _, err := c.DoCustomRequest(ctx, "DELETE", "/comments/"+commentID+".json", "v2", nil, nil) + err := checkUUIDFormat(commentID) + if err != nil { + return fmt.Errorf("Checking ID format: %w", err) + } + _, err = c.DoCustomRequest(ctx, "DELETE", "/comments/"+commentID+".json", "v2", nil, nil) if err != nil { return err } diff --git a/api/favorites.go b/api/favorites.go index 11248cf..b9dac57 100644 --- a/api/favorites.go +++ b/api/favorites.go @@ -3,6 +3,7 @@ package api import ( "context" "encoding/json" + "fmt" ) // Favorite is a Favorite @@ -16,6 +17,10 @@ type Favorite struct { // CreateFavorite Creates a new Passbolt Favorite for the given Resource ID func (c *Client) CreateFavorite(ctx context.Context, resourceID string) (*Favorite, error) { + err := checkUUIDFormat(resourceID) + if err != nil { + return nil, fmt.Errorf("Checking ID format: %w", err) + } msg, err := c.DoCustomRequest(ctx, "POST", "/favorites/resource/"+resourceID+".json", "v2", nil, nil) if err != nil { return nil, err @@ -31,7 +36,11 @@ func (c *Client) CreateFavorite(ctx context.Context, resourceID string) (*Favori // DeleteFavorite Deletes a Passbolt Favorite func (c *Client) DeleteFavorite(ctx context.Context, favoriteID string) error { - _, err := c.DoCustomRequest(ctx, "DELETE", "/favorites/"+favoriteID+".json", "v2", nil, nil) + err := checkUUIDFormat(favoriteID) + if err != nil { + return fmt.Errorf("Checking ID format: %w", err) + } + _, err = c.DoCustomRequest(ctx, "DELETE", "/favorites/"+favoriteID+".json", "v2", nil, nil) if err != nil { return err } diff --git a/api/folders.go b/api/folders.go index 49926eb..22984a3 100644 --- a/api/folders.go +++ b/api/folders.go @@ -3,6 +3,7 @@ package api import ( "context" "encoding/json" + "fmt" ) // Folder is a Folder @@ -83,6 +84,10 @@ func (c *Client) CreateFolder(ctx context.Context, folder Folder) (*Folder, erro // GetFolder gets a Passbolt Folder func (c *Client) GetFolder(ctx context.Context, folderID string, opts *GetFolderOptions) (*Folder, error) { + err := checkUUIDFormat(folderID) + if err != nil { + return nil, fmt.Errorf("Checking ID format: %w", err) + } msg, err := c.DoCustomRequest(ctx, "GET", "/folders/"+folderID+".json", "v2", nil, opts) if err != nil { return nil, err @@ -98,6 +103,10 @@ func (c *Client) GetFolder(ctx context.Context, folderID string, opts *GetFolder // UpdateFolder Updates a existing Passbolt Folder func (c *Client) UpdateFolder(ctx context.Context, folderID string, folder Folder) (*Folder, error) { + err := checkUUIDFormat(folderID) + if err != nil { + return nil, fmt.Errorf("Checking ID format: %w", err) + } msg, err := c.DoCustomRequest(ctx, "PUT", "/folders/"+folderID+".json", "v2", folder, nil) if err != nil { return nil, err @@ -112,7 +121,11 @@ func (c *Client) UpdateFolder(ctx context.Context, folderID string, folder Folde // DeleteFolder Deletes a Passbolt Folder func (c *Client) DeleteFolder(ctx context.Context, folderID string) error { - _, err := c.DoCustomRequest(ctx, "DELETE", "/folders/"+folderID+".json", "v2", nil, nil) + err := checkUUIDFormat(folderID) + if err != nil { + return fmt.Errorf("Checking ID format: %w", err) + } + _, err = c.DoCustomRequest(ctx, "DELETE", "/folders/"+folderID+".json", "v2", nil, nil) if err != nil { return err } @@ -121,7 +134,11 @@ func (c *Client) DeleteFolder(ctx context.Context, folderID string) error { // MoveFolder Moves a Passbolt Folder func (c *Client) MoveFolder(ctx context.Context, folderID, folderParentID string) error { - _, err := c.DoCustomRequest(ctx, "PUT", "/move/folder/"+folderID+".json", "v2", Folder{ + err := checkUUIDFormat(folderID) + if err != nil { + return fmt.Errorf("Checking ID format: %w", err) + } + _, err = c.DoCustomRequest(ctx, "PUT", "/move/folder/"+folderID+".json", "v2", Folder{ FolderParentID: folderParentID, }, nil) if err != nil { diff --git a/api/gpgkey.go b/api/gpgkey.go index f4ce59e..b78109e 100644 --- a/api/gpgkey.go +++ b/api/gpgkey.go @@ -3,6 +3,7 @@ package api import ( "context" "encoding/json" + "fmt" ) // GPGKey is a GPGKey @@ -43,6 +44,10 @@ func (c *Client) GetGPGKeys(ctx context.Context, opts *GetGPGKeysOptions) ([]GPG // GetGPGKey gets a Passbolt GPGKey func (c *Client) GetGPGKey(ctx context.Context, gpgkeyID string) (*GPGKey, error) { + err := checkUUIDFormat(gpgkeyID) + if err != nil { + return nil, fmt.Errorf("Checking ID format: %w", err) + } msg, err := c.DoCustomRequest(ctx, "GET", "/gpgkeys/"+gpgkeyID+".json", "v2", nil, nil) if err != nil { return nil, err diff --git a/api/groups.go b/api/groups.go index 74c25a3..d04a518 100644 --- a/api/groups.go +++ b/api/groups.go @@ -3,6 +3,7 @@ package api import ( "context" "encoding/json" + "fmt" ) //Group is a Group @@ -124,6 +125,10 @@ func (c *Client) CreateGroup(ctx context.Context, group Group) (*Group, error) { // GetGroup gets a Passbolt Group func (c *Client) GetGroup(ctx context.Context, groupID string) (*Group, error) { + err := checkUUIDFormat(groupID) + if err != nil { + return nil, fmt.Errorf("Checking ID format: %w", err) + } msg, err := c.DoCustomRequest(ctx, "GET", "/groups/"+groupID+".json", "v2", nil, nil) if err != nil { return nil, err @@ -139,6 +144,10 @@ func (c *Client) GetGroup(ctx context.Context, groupID string) (*Group, error) { // UpdateGroup Updates a existing Passbolt Group func (c *Client) UpdateGroup(ctx context.Context, groupID string, update GroupUpdate) (*Group, error) { + err := checkUUIDFormat(groupID) + if err != nil { + return nil, fmt.Errorf("Checking ID format: %w", err) + } msg, err := c.DoCustomRequest(ctx, "PUT", "/groups/"+groupID+".json", "v2", update, nil) if err != nil { return nil, err @@ -153,6 +162,10 @@ func (c *Client) UpdateGroup(ctx context.Context, groupID string, update GroupUp // UpdateGroupDryRun Checks that a Passbolt Group update passes validation func (c *Client) UpdateGroupDryRun(ctx context.Context, groupID string, update GroupUpdate) (*UpdateGroupDryRunResult, error) { + err := checkUUIDFormat(groupID) + if err != nil { + return nil, fmt.Errorf("Checking ID format: %w", err) + } msg, err := c.DoCustomRequest(ctx, "PUT", "/groups/"+groupID+"/dry-run.json", "v2", update, nil) if err != nil { return nil, err @@ -167,7 +180,11 @@ func (c *Client) UpdateGroupDryRun(ctx context.Context, groupID string, update G // DeleteGroup Deletes a Passbolt Group func (c *Client) DeleteGroup(ctx context.Context, groupID string) error { - _, err := c.DoCustomRequest(ctx, "DELETE", "/groups/"+groupID+".json", "v2", nil, nil) + err := checkUUIDFormat(groupID) + if err != nil { + return fmt.Errorf("Checking ID format: %w", err) + } + _, err = c.DoCustomRequest(ctx, "DELETE", "/groups/"+groupID+".json", "v2", nil, nil) if err != nil { return err } diff --git a/api/misc.go b/api/misc.go index 99ee9fb..ce3ff71 100644 --- a/api/misc.go +++ b/api/misc.go @@ -3,12 +3,15 @@ package api import ( "fmt" "math/rand" + "regexp" "strconv" "strings" ) const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" +var isUUID = regexp.MustCompile("^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$") + func randStringBytesRmndr(length int) string { b := make([]byte, length) for i := range b { @@ -41,3 +44,10 @@ func checkAuthTokenFormat(authToken string) error { } return nil } + +func checkUUIDFormat(data string) error { + if !isUUID.MatchString(data) { + return fmt.Errorf("UUID is not in the valid format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx") + } + return nil +} diff --git a/api/permissions.go b/api/permissions.go index e60edf5..3bca4f9 100644 --- a/api/permissions.go +++ b/api/permissions.go @@ -3,6 +3,7 @@ package api import ( "context" "encoding/json" + "fmt" ) // Permission is a Permission @@ -21,6 +22,10 @@ type Permission struct { // GetResourcePermissions gets a Resources Permissions func (c *Client) GetResourcePermissions(ctx context.Context, resourceID string) ([]Permission, error) { + err := checkUUIDFormat(resourceID) + if err != nil { + return nil, fmt.Errorf("Checking ID format: %w", err) + } msg, err := c.DoCustomRequest(ctx, "GET", "/permissions/resource/"+resourceID+".json", "v2", nil, nil) if err != nil { return nil, err diff --git a/api/resource_types.go b/api/resource_types.go index 862180b..5b3a753 100644 --- a/api/resource_types.go +++ b/api/resource_types.go @@ -3,6 +3,7 @@ package api import ( "context" "encoding/json" + "fmt" ) //ResourceType is the Type of a Resource @@ -36,6 +37,10 @@ func (c *Client) GetResourceTypes(ctx context.Context, opts *GetResourceTypesOpt // GetResourceType gets a Passbolt Type func (c *Client) GetResourceType(ctx context.Context, typeID string) (*ResourceType, error) { + err := checkUUIDFormat(typeID) + if err != nil { + return nil, fmt.Errorf("Checking ID format: %w", err) + } msg, err := c.DoCustomRequest(ctx, "GET", "/resource-types/"+typeID+".json", "v2", nil, nil) if err != nil { return nil, err diff --git a/api/resources.go b/api/resources.go index 91ff97f..7fbabe6 100644 --- a/api/resources.go +++ b/api/resources.go @@ -3,6 +3,7 @@ package api import ( "context" "encoding/json" + "fmt" ) // Resource is a Resource. @@ -89,6 +90,10 @@ func (c *Client) CreateResource(ctx context.Context, resource Resource) (*Resour // GetResource gets a Passbolt Resource func (c *Client) GetResource(ctx context.Context, resourceID string) (*Resource, error) { + err := checkUUIDFormat(resourceID) + if err != nil { + return nil, fmt.Errorf("Checking ID format: %w", err) + } msg, err := c.DoCustomRequest(ctx, "GET", "/resources/"+resourceID+".json", "v2", nil, nil) if err != nil { return nil, err @@ -104,6 +109,10 @@ func (c *Client) GetResource(ctx context.Context, resourceID string) (*Resource, // UpdateResource Updates a existing Passbolt Resource func (c *Client) UpdateResource(ctx context.Context, resourceID string, resource Resource) (*Resource, error) { + err := checkUUIDFormat(resourceID) + if err != nil { + return nil, fmt.Errorf("Checking ID format: %w", err) + } msg, err := c.DoCustomRequest(ctx, "PUT", "/resources/"+resourceID+".json", "v2", resource, nil) if err != nil { return nil, err @@ -118,7 +127,11 @@ func (c *Client) UpdateResource(ctx context.Context, resourceID string, resource // DeleteResource Deletes a Passbolt Resource func (c *Client) DeleteResource(ctx context.Context, resourceID string) error { - _, err := c.DoCustomRequest(ctx, "DELETE", "/resources/"+resourceID+".json", "v2", nil, nil) + err := checkUUIDFormat(resourceID) + if err != nil { + return fmt.Errorf("Checking ID format: %w", err) + } + _, err = c.DoCustomRequest(ctx, "DELETE", "/resources/"+resourceID+".json", "v2", nil, nil) if err != nil { return err } @@ -127,7 +140,11 @@ func (c *Client) DeleteResource(ctx context.Context, resourceID string) error { // MoveResource Moves a Passbolt Resource func (c *Client) MoveResource(ctx context.Context, resourceID, folderParentID string) error { - _, err := c.DoCustomRequest(ctx, "PUT", "/move/resource/"+resourceID+".json", "v2", Resource{ + err := checkUUIDFormat(resourceID) + if err != nil { + return fmt.Errorf("Checking ID format: %w", err) + } + _, err = c.DoCustomRequest(ctx, "PUT", "/move/resource/"+resourceID+".json", "v2", Resource{ FolderParentID: folderParentID, }, nil) if err != nil { diff --git a/api/secrets.go b/api/secrets.go index 70979cd..1f63b23 100644 --- a/api/secrets.go +++ b/api/secrets.go @@ -3,6 +3,7 @@ package api import ( "context" "encoding/json" + "fmt" ) // Secret is a Secret @@ -23,6 +24,10 @@ type SecretDataTypePasswordAndDescription struct { // GetSecret gets a Passbolt Secret func (c *Client) GetSecret(ctx context.Context, resourceID string) (*Secret, error) { + err := checkUUIDFormat(resourceID) + if err != nil { + return nil, fmt.Errorf("Checking ID format: %w", err) + } msg, err := c.DoCustomRequest(ctx, "GET", "/secrets/resource/"+resourceID+".json", "v2", nil, nil) if err != nil { return nil, err diff --git a/api/setup.go b/api/setup.go index b7a941b..a0b23a7 100644 --- a/api/setup.go +++ b/api/setup.go @@ -3,6 +3,7 @@ package api import ( "context" "encoding/json" + "fmt" ) type SetupInstallResponse struct { @@ -21,6 +22,14 @@ type SetupCompleteRequest struct { // SetupInstall validates the userid and token used for Account setup, gives back the User Information func (c *Client) SetupInstall(ctx context.Context, userID, token string) (*SetupInstallResponse, error) { + err := checkUUIDFormat(userID) + if err != nil { + return nil, fmt.Errorf("Checking ID format: %w", err) + } + err = checkUUIDFormat(token) + if err != nil { + return nil, fmt.Errorf("Checking Token format: %w", err) + } msg, err := c.DoCustomRequest(ctx, "GET", "/setup/install/"+userID+"/"+token+".json", "v2", nil, nil) if err != nil { return nil, err @@ -36,7 +45,11 @@ func (c *Client) SetupInstall(ctx context.Context, userID, token string) (*Setup // SetupComplete Completes setup of a Passbolt Account func (c *Client) SetupComplete(ctx context.Context, userID string, request SetupCompleteRequest) error { - _, err := c.DoCustomRequest(ctx, "POST", "/setup/complete/"+userID+".json", "v2", request, nil) + err := checkUUIDFormat(userID) + if err != nil { + return fmt.Errorf("Checking ID format: %w", err) + } + _, err = c.DoCustomRequest(ctx, "POST", "/setup/complete/"+userID+".json", "v2", request, nil) if err != nil { return err } diff --git a/api/share.go b/api/share.go index 8e940f0..225a805 100644 --- a/api/share.go +++ b/api/share.go @@ -3,6 +3,7 @@ package api import ( "context" "encoding/json" + "fmt" ) // ResourceShareRequest is a ResourceShareRequest @@ -61,7 +62,11 @@ func (c *Client) SearchAROs(ctx context.Context, opts SearchAROsOptions) ([]ARO, // ShareResource Shares a Resource with AROs func (c *Client) ShareResource(ctx context.Context, resourceID string, shareRequest ResourceShareRequest) error { - _, err := c.DoCustomRequest(ctx, "PUT", "/share/resource/"+resourceID+".json", "v2", shareRequest, nil) + err := checkUUIDFormat(resourceID) + if err != nil { + return fmt.Errorf("Checking ID format: %w", err) + } + _, err = c.DoCustomRequest(ctx, "PUT", "/share/resource/"+resourceID+".json", "v2", shareRequest, nil) if err != nil { return err } @@ -71,8 +76,12 @@ func (c *Client) ShareResource(ctx context.Context, resourceID string, shareRequ // ShareFolder Shares a Folder with AROs func (c *Client) ShareFolder(ctx context.Context, folderID string, permissions []Permission) error { + err := checkUUIDFormat(folderID) + if err != nil { + return fmt.Errorf("Checking ID format: %w", err) + } f := Folder{Permissions: permissions} - _, err := c.DoCustomRequest(ctx, "PUT", "/share/folder/"+folderID+".json", "v2", f, nil) + _, err = c.DoCustomRequest(ctx, "PUT", "/share/folder/"+folderID+".json", "v2", f, nil) if err != nil { return err } @@ -82,6 +91,10 @@ func (c *Client) ShareFolder(ctx context.Context, folderID string, permissions [ // SimulateShareResource Simulates Shareing a Resource with AROs func (c *Client) SimulateShareResource(ctx context.Context, resourceID string, shareRequest ResourceShareRequest) (*ResourceShareSimulationResult, error) { + err := checkUUIDFormat(resourceID) + if err != nil { + return nil, fmt.Errorf("Checking ID format: %w", err) + } msg, err := c.DoCustomRequest(ctx, "POST", "/share/simulate/resource/"+resourceID+".json", "v2", shareRequest, nil) if err != nil { return nil, err diff --git a/api/users.go b/api/users.go index 9cea6c8..9ce240b 100644 --- a/api/users.go +++ b/api/users.go @@ -3,6 +3,7 @@ package api import ( "context" "encoding/json" + "fmt" ) const UserLocaleENUK = "en-UK" @@ -81,6 +82,10 @@ func (c *Client) GetMe(ctx context.Context) (*User, error) { // GetUser gets a Passbolt User func (c *Client) GetUser(ctx context.Context, userID string) (*User, error) { + err := checkUUIDFormat(userID) + if err != nil { + return nil, fmt.Errorf("Checking ID format: %w", err) + } msg, err := c.DoCustomRequest(ctx, "GET", "/users/"+userID+".json", "v2", nil, nil) if err != nil { return nil, err @@ -96,6 +101,10 @@ func (c *Client) GetUser(ctx context.Context, userID string) (*User, error) { // UpdateUser Updates a existing Passbolt User func (c *Client) UpdateUser(ctx context.Context, userID string, user User) (*User, error) { + err := checkUUIDFormat(userID) + if err != nil { + return nil, fmt.Errorf("Checking ID format: %w", err) + } msg, err := c.DoCustomRequest(ctx, "PUT", "/users/"+userID+".json", "v2", user, nil) if err != nil { return nil, err @@ -110,7 +119,11 @@ func (c *Client) UpdateUser(ctx context.Context, userID string, user User) (*Use // DeleteUser Deletes a Passbolt User func (c *Client) DeleteUser(ctx context.Context, userID string) error { - _, err := c.DoCustomRequest(ctx, "DELETE", "/users/"+userID+".json", "v2", nil, nil) + err := checkUUIDFormat(userID) + if err != nil { + return fmt.Errorf("Checking ID format: %w", err) + } + _, err = c.DoCustomRequest(ctx, "DELETE", "/users/"+userID+".json", "v2", nil, nil) if err != nil { return err } @@ -119,7 +132,11 @@ func (c *Client) DeleteUser(ctx context.Context, userID string) error { // DeleteUserDryrun Check if a Passbolt User is Deleteable func (c *Client) DeleteUserDryrun(ctx context.Context, userID string) error { - _, err := c.DoCustomRequest(ctx, "DELETE", "/users/"+userID+"/dry-run.json", "v2", nil, nil) + err := checkUUIDFormat(userID) + if err != nil { + return fmt.Errorf("Checking ID format: %w", err) + } + _, err = c.DoCustomRequest(ctx, "DELETE", "/users/"+userID+"/dry-run.json", "v2", nil, nil) if err != nil { return err } From 0a86e0c1e648ed7113ce8c446fe261149a123529 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Fri, 21 Jan 2022 13:49:28 +0100 Subject: [PATCH 43/74] Update Deps --- go.mod | 7 +++---- go.sum | 20 +++++++++++--------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/go.mod b/go.mod index 9317705..8c4192b 100644 --- a/go.mod +++ b/go.mod @@ -3,12 +3,11 @@ module github.com/speatzle/go-passbolt go 1.16 require ( - github.com/ProtonMail/go-crypto v0.0.0-20210707164159-52430bf6b52c // indirect - github.com/ProtonMail/gopenpgp/v2 v2.2.2 + github.com/ProtonMail/gopenpgp/v2 v2.4.2 github.com/google/go-querystring v1.1.0 github.com/google/uuid v1.3.0 github.com/sirupsen/logrus v1.8.1 // indirect - golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect - golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf // indirect + golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce // indirect + golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 // indirect golang.org/x/text v0.3.7 // indirect ) diff --git a/go.sum b/go.sum index 3665dab..b655c96 100644 --- a/go.sum +++ b/go.sum @@ -1,11 +1,10 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/ProtonMail/go-crypto v0.0.0-20210512092938-c05353c2d58c/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= -github.com/ProtonMail/go-crypto v0.0.0-20210707164159-52430bf6b52c h1:FP7mMdsXy0ybzar1sJeIcZtaJka0U/ZmLTW4wRpolYk= -github.com/ProtonMail/go-crypto v0.0.0-20210707164159-52430bf6b52c/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= +github.com/ProtonMail/go-crypto v0.0.0-20220113124808-70ae35bab23f h1:J2FzIrXN82q5uyUraeJpLIm7U6PffRwje2ORho5yIik= +github.com/ProtonMail/go-crypto v0.0.0-20220113124808-70ae35bab23f/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= github.com/ProtonMail/go-mime v0.0.0-20190923161245-9b5a4261663a h1:W6RrgN/sTxg1msqzFFb+G80MFmpjMw61IU+slm+wln4= github.com/ProtonMail/go-mime v0.0.0-20190923161245-9b5a4261663a/go.mod h1:NYt+V3/4rEeDuaev/zw1zCq8uqVEuPHzDPo3OZrlGJ4= -github.com/ProtonMail/gopenpgp/v2 v2.2.2 h1:u2m7xt+CZWj88qK1UUNBoXeJCFJwJCZ/Ff4ymGoxEXs= -github.com/ProtonMail/gopenpgp/v2 v2.2.2/go.mod h1:ajUlBGvxMH1UBZnaYO3d1FSVzjiC6kK9XlZYGiDCvpM= +github.com/ProtonMail/gopenpgp/v2 v2.4.2 h1:xPcQYAa3D3V2sDhJq0bYWwlWtxzTudxm1/XXHlSWcJo= +github.com/ProtonMail/gopenpgp/v2 v2.4.2/go.mod h1:0byYFEOo6x4F/1YqhN7Z6m015Cqnxllz3CGb5cjJueY= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -32,8 +31,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ= -golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce h1:Roh6XWxHFKrPgC/EQhVubSAGQ6Ozk6IdxHSzt1mR0EI= +golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= @@ -46,19 +45,22 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf h1:2ucpDCmfkl8Bd/FsLtiD653Wf96cW37s+iGx93zsu4k= -golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0= +golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= From 5bab492d89fad97b3cd2cee3c61433205123a86f Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Fri, 21 Jan 2022 14:12:30 +0100 Subject: [PATCH 44/74] fix cicd --- .github/workflows/.go.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/.go.yml b/.github/workflows/.go.yml index 400af22..3376e87 100644 --- a/.github/workflows/.go.yml +++ b/.github/workflows/.go.yml @@ -18,7 +18,7 @@ jobs: run: | git clone https://github.com/passbolt/passbolt_docker.git ../passbolt_docker cd ../passbolt_docker - docker-compose up -d + docker-compose up -d -f docker-compose/docker-compose-ce.yaml docker ps -a - name: "Test" run: | From 90b10d5570c259e435b9cc57c724e9c19b7be9de Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Fri, 21 Jan 2022 14:24:04 +0100 Subject: [PATCH 45/74] fix cicd for real --- .github/workflows/.go.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/.go.yml b/.github/workflows/.go.yml index 3376e87..94db8ce 100644 --- a/.github/workflows/.go.yml +++ b/.github/workflows/.go.yml @@ -18,7 +18,7 @@ jobs: run: | git clone https://github.com/passbolt/passbolt_docker.git ../passbolt_docker cd ../passbolt_docker - docker-compose up -d -f docker-compose/docker-compose-ce.yaml + docker-compose -f docker-compose/docker-compose-ce.yaml up -d docker ps -a - name: "Test" run: | From d18f38c066e0ffa5cbe91faefc8d5720556a2b99 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Fri, 21 Jan 2022 14:26:55 +0100 Subject: [PATCH 46/74] actually fix cicd for real --- .github/workflows/.go.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/.go.yml b/.github/workflows/.go.yml index 94db8ce..bdb7a79 100644 --- a/.github/workflows/.go.yml +++ b/.github/workflows/.go.yml @@ -22,8 +22,8 @@ jobs: docker ps -a - name: "Test" run: | - docker exec passbolt_docker_passbolt_1 sh -c '/usr/bin/wait-for.sh -t 30 localhost:443' - output=$(docker exec passbolt_docker_passbolt_1 sh -c 'su -m -c "/usr/share/php/passbolt/bin/cake \ + docker exec docker-compose_passbolt_1 sh -c '/usr/bin/wait-for.sh -t 30 localhost:443' + output=$(docker exec docker-compose_passbolt_1 sh -c 'su -m -c "/usr/share/php/passbolt/bin/cake \ passbolt register_user \ -u your@email.com \ -f yourname \ From 9cc8cc8b02e1e329681ae7483ffe44d3a8975ebb Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Fri, 21 Jan 2022 14:40:06 +0100 Subject: [PATCH 47/74] update regex --- api/misc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/misc.go b/api/misc.go index ce3ff71..50af15c 100644 --- a/api/misc.go +++ b/api/misc.go @@ -10,7 +10,7 @@ import ( const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" -var isUUID = regexp.MustCompile("^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$") +var isUUID = regexp.MustCompile("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$") func randStringBytesRmndr(length int) string { b := make([]byte, length) From bd2460467dc39ad9a13a784fad95bad65c2bbab5 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Fri, 4 Feb 2022 16:41:44 +0100 Subject: [PATCH 48/74] Add missing ResourceType for ContainResourceType --- api/resources.go | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/api/resources.go b/api/resources.go index 7fbabe6..3647c00 100644 --- a/api/resources.go +++ b/api/resources.go @@ -10,24 +10,25 @@ import ( // 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 Decription. type Resource struct { - ID string `json:"id,omitempty"` - Created *Time `json:"created,omitempty"` - CreatedBy string `json:"created_by,omitempty"` - Creator *User `json:"creator,omitempty"` - Deleted bool `json:"deleted,omitempty"` - Description string `json:"description,omitempty"` - Favorite *Favorite `json:"favorite,omitempty"` - Modified *Time `json:"modified,omitempty"` - ModifiedBy string `json:"modified_by,omitempty"` - Modifier *User `json:"modifier,omitempty"` - Name string `json:"name,omitempty"` - Permission *Permission `json:"permission,omitempty"` - URI string `json:"uri,omitempty"` - Username string `json:"username,omitempty"` - FolderParentID string `json:"folder_parent_id,omitempty"` - ResourceTypeID string `json:"resource_type_id,omitempty"` - Secrets []Secret `json:"secrets,omitempty"` - Tags []Tag `json:"tags,omitempty"` + ID string `json:"id,omitempty"` + Created *Time `json:"created,omitempty"` + CreatedBy string `json:"created_by,omitempty"` + Creator *User `json:"creator,omitempty"` + Deleted bool `json:"deleted,omitempty"` + Description string `json:"description,omitempty"` + Favorite *Favorite `json:"favorite,omitempty"` + Modified *Time `json:"modified,omitempty"` + ModifiedBy string `json:"modified_by,omitempty"` + Modifier *User `json:"modifier,omitempty"` + Name string `json:"name,omitempty"` + Permission *Permission `json:"permission,omitempty"` + URI string `json:"uri,omitempty"` + Username string `json:"username,omitempty"` + FolderParentID string `json:"folder_parent_id,omitempty"` + ResourceTypeID string `json:"resource_type_id,omitempty"` + ResourceType ResourceType `json:"resource_type,omitempty"` + Secrets []Secret `json:"secrets,omitempty"` + Tags []Tag `json:"tags,omitempty"` } // Tag is a Passbolt Password Tag From a3a55e219926087b279f42c8b9273ba67e3cad66 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Fri, 4 Feb 2022 16:42:04 +0100 Subject: [PATCH 49/74] fix typo --- api/resources.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/resources.go b/api/resources.go index 3647c00..b2659c8 100644 --- a/api/resources.go +++ b/api/resources.go @@ -8,7 +8,7 @@ import ( // 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, -// for now the only Field like that is the Decription. +// for now the only Field like that is the Description. type Resource struct { ID string `json:"id,omitempty"` Created *Time `json:"created,omitempty"` From 47ec05923235e69c65bea61dfa17e403178508d7 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Fri, 4 Feb 2022 16:47:21 +0100 Subject: [PATCH 50/74] update deps --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 8c4192b..5bf7e9d 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/google/go-querystring v1.1.0 github.com/google/uuid v1.3.0 github.com/sirupsen/logrus v1.8.1 // indirect - golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce // indirect - golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 // indirect + golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838 // indirect + golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a // indirect golang.org/x/text v0.3.7 // indirect ) diff --git a/go.sum b/go.sum index b655c96..64e8ac1 100644 --- a/go.sum +++ b/go.sum @@ -31,8 +31,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce h1:Roh6XWxHFKrPgC/EQhVubSAGQ6Ozk6IdxHSzt1mR0EI= -golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838 h1:71vQrMauZZhcTVK6KdYM+rklehEEwb3E+ZhaE5jrPrE= +golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= @@ -54,8 +54,8 @@ golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a h1:ppl5mZgokTT8uPkmYOyEUmPTr3ypaKkg5eFOGrAmxxE= +golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= From 69221b3a244efadec75ccfbf1a0b817554396dc8 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Tue, 22 Feb 2022 16:38:42 +0100 Subject: [PATCH 51/74] update module import path for repository transfer --- LICENSE | 2 +- README.md | 18 +++++++++--------- go.mod | 2 +- helper/folder.go | 2 +- helper/group.go | 2 +- helper/mfa.go | 2 +- helper/resources.go | 2 +- helper/setup.go | 2 +- helper/setup_test.go | 2 +- helper/share.go | 2 +- helper/user.go | 2 +- helper/util.go | 2 +- 12 files changed, 20 insertions(+), 20 deletions(-) diff --git a/LICENSE b/LICENSE index 173c5c9..e064fba 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021 speatzle +Copyright (c) 2021 Samuel Lorch Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 50ecbf6..ea15790 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ # go-passbolt -[![Go Reference](https://pkg.go.dev/badge/github.com/speatzle/go-passbolt.svg)](https://pkg.go.dev/github.com/speatzle/go-passbolt) +[![Go Reference](https://pkg.go.dev/badge/github.com/passbolt/go-passbolt.svg)](https://pkg.go.dev/github.com/passbolt/go-passbolt) A Go module to interact with [Passbolt](https://www.passbolt.com/), an open-source password manager for teams -There also is a CLI Tool to interact with Passbolt using this module [here](https://github.com/speatzle/go-passbolt-cli). +There also is a CLI Tool to interact with Passbolt using this module [here](https://github.com/passbolt/go-passbolt-cli). This module tries to support the latest Passbolt Community/PRO server release, PRO Features such as folders are supported. Older versions of Passbolt such as v2 are unsupported (it's a password manager, please update it) @@ -15,14 +15,14 @@ The helper package has simplified functions that use the API package to perform To use the API package, please read the [Passbolt API docs](https://help.passbolt.com/api). Sadly the docs aren't complete so many things here have been found by looking at the source of Passbolt or through trial and error. If you have a question just ask. -PR's are welcome. But be gentle: if it's something bigger or fundamental: please [create an issue](https://github.com/speatzle/go-passbolt/issues/new) and ask first. +PR's are welcome. But be gentle: if it's something bigger or fundamental: please [create an issue](https://github.com/passbolt/go-passbolt/issues/new) and ask first. Disclaimer: This project is community driven and not associated with Passbolt SA # Install -`go get github.com/speatzle/go-passbolt` +`go get github.com/passbolt/go-passbolt` # Examples ## Login @@ -36,7 +36,7 @@ import ( "context" "fmt" - "github.com/speatzle/go-passbolt/api" + "github.com/passbolt/go-passbolt/api" ) const address = "https://passbolt.example.com" @@ -70,7 +70,7 @@ You can do this using the `client.CheckSession()` function. ## Create a Resource -Creating a resource using the helper package is simple. First, add `"github.com/speatzle/go-passbolt/helper"` to your imports. +Creating a resource using the helper package is simple. First, add `"github.com/passbolt/go-passbolt/helper"` to your imports. Then you can simply: @@ -331,7 +331,7 @@ go-passbolt now supports MFA! You can set it up using the Client's `MFACallback` ## Other -These examples are just the main use cases of these Modules, many more API calls are supported. Look at the [reference](https://pkg.go.dev/github.com/speatzle/go-passbolt) for more information. +These examples are just the main use cases of these Modules, many more API calls are supported. Look at the [reference](https://pkg.go.dev/github.com/passbolt/go-passbolt) for more information. ## Full Example @@ -350,8 +350,8 @@ import ( "context" "fmt" - "github.com/speatzle/go-passbolt/api" - "github.com/speatzle/go-passbolt/helper" + "github.com/passbolt/go-passbolt/api" + "github.com/passbolt/go-passbolt/helper" ) const address = "https://passbolt.example.com" diff --git a/go.mod b/go.mod index 5bf7e9d..6c552cb 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/speatzle/go-passbolt +module github.com/passbolt/go-passbolt go 1.16 diff --git a/helper/folder.go b/helper/folder.go index 6243ff0..170edaf 100644 --- a/helper/folder.go +++ b/helper/folder.go @@ -4,7 +4,7 @@ import ( "context" "fmt" - "github.com/speatzle/go-passbolt/api" + "github.com/passbolt/go-passbolt/api" ) // CreateFolder Creates a new Folder diff --git a/helper/group.go b/helper/group.go index 8edffa5..329be41 100644 --- a/helper/group.go +++ b/helper/group.go @@ -4,7 +4,7 @@ import ( "context" "fmt" - "github.com/speatzle/go-passbolt/api" + "github.com/passbolt/go-passbolt/api" ) // GroupMembershipOperation creates/modifies/deletes a group membership diff --git a/helper/mfa.go b/helper/mfa.go index d57623d..0c834c2 100644 --- a/helper/mfa.go +++ b/helper/mfa.go @@ -8,7 +8,7 @@ import ( "net/http" "time" - "github.com/speatzle/go-passbolt/api" + "github.com/passbolt/go-passbolt/api" ) // AddMFACallbackTOTP adds a MFA callback to the client that generates OTP Codes on demand using a Token with configurable retries and delay diff --git a/helper/resources.go b/helper/resources.go index 9d0361a..bdfd9ee 100644 --- a/helper/resources.go +++ b/helper/resources.go @@ -5,7 +5,7 @@ import ( "encoding/json" "fmt" - "github.com/speatzle/go-passbolt/api" + "github.com/passbolt/go-passbolt/api" ) // CreateResource Creates a Resource where the Password and Description are Encrypted and Returns the Resources ID diff --git a/helper/setup.go b/helper/setup.go index 02c0e50..f82c04b 100644 --- a/helper/setup.go +++ b/helper/setup.go @@ -5,7 +5,7 @@ import ( "fmt" "strings" - "github.com/speatzle/go-passbolt/api" + "github.com/passbolt/go-passbolt/api" "github.com/ProtonMail/gopenpgp/v2/crypto" "github.com/ProtonMail/gopenpgp/v2/helper" diff --git a/helper/setup_test.go b/helper/setup_test.go index d1f749d..8ec3d2f 100644 --- a/helper/setup_test.go +++ b/helper/setup_test.go @@ -8,7 +8,7 @@ import ( "os" "testing" - "github.com/speatzle/go-passbolt/api" + "github.com/passbolt/go-passbolt/api" ) var client *api.Client diff --git a/helper/share.go b/helper/share.go index 4365f4c..f28dbbd 100644 --- a/helper/share.go +++ b/helper/share.go @@ -4,7 +4,7 @@ import ( "context" "fmt" - "github.com/speatzle/go-passbolt/api" + "github.com/passbolt/go-passbolt/api" ) // ShareOperation defines how Resources are to be Shared With Users/Groups diff --git a/helper/user.go b/helper/user.go index b7d5723..9327fab 100644 --- a/helper/user.go +++ b/helper/user.go @@ -4,7 +4,7 @@ import ( "context" "fmt" - "github.com/speatzle/go-passbolt/api" + "github.com/passbolt/go-passbolt/api" ) // CreateUser Creates a new User diff --git a/helper/util.go b/helper/util.go index 5dd8300..78b1960 100644 --- a/helper/util.go +++ b/helper/util.go @@ -3,7 +3,7 @@ package helper import ( "fmt" - "github.com/speatzle/go-passbolt/api" + "github.com/passbolt/go-passbolt/api" ) func getPublicKeyByUserID(userID string, Users []api.User) (string, error) { From 1b30521b5b482dcb63836590945afd67d3d62381 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Thu, 7 Apr 2022 16:07:08 +0200 Subject: [PATCH 52/74] fix mfa challange detection for servers where the default language is not English --- api/api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/api.go b/api/api.go index 409657e..96a0f41 100644 --- a/api/api.go +++ b/api/api.go @@ -62,7 +62,7 @@ start: if res.Header.Status == "success" { return r, &res, nil } else if res.Header.Status == "error" { - if res.Header.Code == 403 && res.Header.Message == "MFA authentication is required." { + if res.Header.Code == 403 && res.Header.URL == "/mfa/verify/error.json" { if !firstTime { // if we are here this probably means that the MFA callback is broken, to prevent a infinit loop lets error here return r, &res, fmt.Errorf("Got MFA challenge twice in a row, is your MFA Callback broken? Bailing to prevent loop...:") From 9e181e6c8338d957031e21d75933569ffb1190ce Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Wed, 13 Apr 2022 11:14:32 +0200 Subject: [PATCH 53/74] fix Resource FilterIsSharedWithGroup Type --- api/resources.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/resources.go b/api/resources.go index b2659c8..c25ddaf 100644 --- a/api/resources.go +++ b/api/resources.go @@ -41,7 +41,7 @@ type Tag struct { // GetResourcesOptions are all available query parameters type GetResourcesOptions struct { FilterIsFavorite bool `url:"filter[is-favorite],omitempty"` - FilterIsSharedWithGroup []string `url:"filter[is-shared-with-group][],omitempty"` + FilterIsSharedWithGroup string `url:"filter[is-shared-with-group],omitempty"` FilterIsOwnedByMe bool `url:"filter[is-owned-by-me],omitempty"` FilterIsSharedWithMe bool `url:"filter[is-shared-with-me],omitempty"` FilterHasID []string `url:"filter[has-id][],omitempty"` From 4d482e6bf20e5c14236174782c8c2fd0f797321e Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Fri, 30 Dec 2022 17:02:16 +0100 Subject: [PATCH 54/74] update deps, update minimum go to 1.18 --- go.mod | 19 +++++++++++++------ go.sum | 59 ++++++++++++++++++++++------------------------------------ 2 files changed, 35 insertions(+), 43 deletions(-) diff --git a/go.mod b/go.mod index 6c552cb..deeb695 100644 --- a/go.mod +++ b/go.mod @@ -1,13 +1,20 @@ module github.com/passbolt/go-passbolt -go 1.16 +go 1.18 require ( - github.com/ProtonMail/gopenpgp/v2 v2.4.2 + github.com/ProtonMail/gopenpgp/v2 v2.5.0 github.com/google/go-querystring v1.1.0 github.com/google/uuid v1.3.0 - github.com/sirupsen/logrus v1.8.1 // indirect - golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838 // indirect - golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a // indirect - golang.org/x/text v0.3.7 // indirect +) + +require ( + github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4 // indirect + github.com/ProtonMail/go-mime v0.0.0-20221031134845-8fd9bc37cf08 // indirect + github.com/cloudflare/circl v1.3.1 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/stretchr/testify v1.7.0 // indirect + golang.org/x/crypto v0.4.0 // indirect + golang.org/x/sys v0.3.0 // indirect + golang.org/x/text v0.5.0 // indirect ) diff --git a/go.sum b/go.sum index 64e8ac1..b46ce4b 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,14 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/ProtonMail/go-crypto v0.0.0-20220113124808-70ae35bab23f h1:J2FzIrXN82q5uyUraeJpLIm7U6PffRwje2ORho5yIik= -github.com/ProtonMail/go-crypto v0.0.0-20220113124808-70ae35bab23f/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= -github.com/ProtonMail/go-mime v0.0.0-20190923161245-9b5a4261663a h1:W6RrgN/sTxg1msqzFFb+G80MFmpjMw61IU+slm+wln4= -github.com/ProtonMail/go-mime v0.0.0-20190923161245-9b5a4261663a/go.mod h1:NYt+V3/4rEeDuaev/zw1zCq8uqVEuPHzDPo3OZrlGJ4= -github.com/ProtonMail/gopenpgp/v2 v2.4.2 h1:xPcQYAa3D3V2sDhJq0bYWwlWtxzTudxm1/XXHlSWcJo= -github.com/ProtonMail/gopenpgp/v2 v2.4.2/go.mod h1:0byYFEOo6x4F/1YqhN7Z6m015Cqnxllz3CGb5cjJueY= +github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4 h1:ra2OtmuW0AE5csawV4YXMNGNQQXvLRps3z2Z59OPO+I= +github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4/go.mod h1:UBYPn8k0D56RtnR8RFQMjmh4KrZzWJ5o7Z9SYjossQ8= +github.com/ProtonMail/go-mime v0.0.0-20221031134845-8fd9bc37cf08 h1:dS7r5z4iGS0qCjM7UwWdsEMzQesUQbGcXdSm2/tWboA= +github.com/ProtonMail/go-mime v0.0.0-20221031134845-8fd9bc37cf08/go.mod h1:qRZgbeASl2a9OwmsV85aWwRqic0NHPh+9ewGAzb4cgM= +github.com/ProtonMail/gopenpgp/v2 v2.5.0 h1:L+98m3xj/YerdWqpWNkZmwNjg1Bs0lKRGQyuONjOImw= +github.com/ProtonMail/gopenpgp/v2 v2.5.0/go.mod h1:N+W/oc/o6yqdevBw8vpgZ3JMyaMA/IwQIdnaGLCoA+w= +github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= +github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I= +github.com/cloudflare/circl v1.3.1 h1:4OVCZRL62ijwEwxnF6I7hLwxvIYi3VaZt8TflkqtrtA= +github.com/cloudflare/circl v1.3.1/go.mod h1:+CauBF6R70Jqcyl8N2hC8pAXYbWkGIezuSbuGLtRhnw= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -14,62 +18,43 @@ github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= -github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838 h1:71vQrMauZZhcTVK6KdYM+rklehEEwb3E+ZhaE5jrPrE= -golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8= +golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80= golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20200801112145-973feb4309de/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a h1:ppl5mZgokTT8uPkmYOyEUmPTr3ypaKkg5eFOGrAmxxE= -golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= +golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 8a5cbff8391d31ff80750c2082f3f3e11613b5dd Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Fri, 30 Dec 2022 17:14:14 +0100 Subject: [PATCH 55/74] update keysize in test --- helper/setup.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helper/setup.go b/helper/setup.go index f82c04b..7fdc006 100644 --- a/helper/setup.go +++ b/helper/setup.go @@ -31,7 +31,7 @@ func SetupAccount(ctx context.Context, c *api.Client, userID, token, password st keyName := install.Profile.FirstName + " " + install.Profile.LastName + " " + install.Username - privateKey, err := helper.GenerateKey(keyName, install.Username, []byte(password), "rsa", 2048) + privateKey, err := helper.GenerateKey(keyName, install.Username, []byte(password), "rsa", 4096) if err != nil { return "", fmt.Errorf("Generating Private Key: %w", err) } From ced16f247900fdc1913358a275455e93280bb28c Mon Sep 17 00:00:00 2001 From: lenforiee Date: Wed, 19 Apr 2023 21:17:57 +0200 Subject: [PATCH 56/74] infinit -> infinite --- api/api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/api.go b/api/api.go index 96a0f41..b2958c4 100644 --- a/api/api.go +++ b/api/api.go @@ -64,7 +64,7 @@ start: } else if res.Header.Status == "error" { if res.Header.Code == 403 && res.Header.URL == "/mfa/verify/error.json" { if !firstTime { - // if we are here this probably means that the MFA callback is broken, to prevent a infinit loop lets error here + // if we are here this probably means that the MFA callback is broken, to prevent a infinite loop lets error here return r, &res, fmt.Errorf("Got MFA challenge twice in a row, is your MFA Callback broken? Bailing to prevent loop...:") } if c.MFACallback != nil { From fd895a9d46ed99469b39f7debf32faf0919debc2 Mon Sep 17 00:00:00 2001 From: lenforiee Date: Wed, 19 Apr 2023 21:24:12 +0200 Subject: [PATCH 57/74] challange -> challenge --- README.md | 32 +++++++++++++++++--------------- api/api.go | 4 ++-- api/client.go | 2 +- api/mfa.go | 4 ++-- api/verify.go | 4 ++-- helper/mfa.go | 14 +++++++------- 6 files changed, 31 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index ea15790..0ffc1fe 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # go-passbolt + [![Go Reference](https://pkg.go.dev/badge/github.com/passbolt/go-passbolt.svg)](https://pkg.go.dev/github.com/passbolt/go-passbolt) A Go module to interact with [Passbolt](https://www.passbolt.com/), an open-source password manager for teams @@ -7,11 +8,11 @@ There also is a CLI Tool to interact with Passbolt using this module [here](http This module tries to support the latest Passbolt Community/PRO server release, PRO Features such as folders are supported. Older versions of Passbolt such as v2 are unsupported (it's a password manager, please update it) -This module is divided into two packages: API and helper. +This module is divided into two packages: API and helper. -In the API package, you will find everything to directly interact with the API. +In the API package, you will find everything to directly interact with the API. -The helper package has simplified functions that use the API package to perform common but complicated tasks such as sharing a password. +The helper package has simplified functions that use the API package to perform common but complicated tasks such as sharing a password. To use the API package, please read the [Passbolt API docs](https://help.passbolt.com/api). Sadly the docs aren't complete so many things here have been found by looking at the source of Passbolt or through trial and error. If you have a question just ask. @@ -19,12 +20,12 @@ PR's are welcome. But be gentle: if it's something bigger or fundamental: please Disclaimer: This project is community driven and not associated with Passbolt SA - # Install `go get github.com/passbolt/go-passbolt` # Examples + ## Login First, you will need to create a client and then log in on the server using the client: @@ -214,24 +215,25 @@ err = helper.UpdateUser( "lastname", // LastName ) ``` + Note: These helpers will only update fields that are not "". Helper update functions also exists for Folders. ## Sharing -As sharing resources is very complicated there are multiple helper functions. +As sharing resources is very complicated there are multiple helper functions. During sharing you will encounter the [permission type](https://github.com/passbolt/passbolt_api/blob/858971516c5e61e1f1be37b007693f0869a70486/src/Model/Entity/Permission.php#L43-L45). The `permissionType` can be: -| Code | Meaning | -| --- | --- | -| `1` | "Read-only" | -| `7` | "Can update" | -| `15` | "Owner" | -| `-1` | Delete existing permission | +| Code | Meaning | +| ---- | -------------------------- | +| `1` | "Read-only" | +| `7` | "Can update" | +| `15` | "Owner" | +| `-1` | Delete existing permission | The `ShareResourceWithUsersAndGroups` function shares the resource with all provided users and groups with the given `permissionType`. @@ -294,6 +296,7 @@ err := client.MoveFolder(ctx, "folder id", "parent folder id") ## Setup You can setup a Account using a Invite Link like this: + ```go // Get the UserID and Token from the Invite Link userID, token, err := ParseInviteUrl(url) @@ -307,7 +310,7 @@ privkey, err := SetupAccount(ctx, rClient, userID, token, "password123") ## Verification -You can Verify that the Server hasen't changed, for that you need to initially setup the Verification and save the returned values. Then you can Verify that the serverkey hasen't changed since you setup the Verification. Note this Only Works if the client is not logged in. +You can Verify that the Server hasen't changed, for that you need to initially setup the Verification and save the returned values. Then you can Verify that the serverkey hasen't changed since you setup the Verification. Note this Only Works if the client is not logged in. ```go // Setup the Verification @@ -327,18 +330,17 @@ if err != nil { ## MFA -go-passbolt now supports MFA! You can set it up using the Client's `MFACallback` function, it will provide everything you need to complete any MFA challanges. When your done you just need to return the new MFA Cookie (usually called passbolt_mfa). The helper package has a example implementation for a noninteractive TOTP Setup under helper/mfa.go in the function `AddMFACallbackTOTP`. +go-passbolt now supports MFA! You can set it up using the Client's `MFACallback` function, it will provide everything you need to complete any MFA challenges. When your done you just need to return the new MFA Cookie (usually called passbolt_mfa). The helper package has a example implementation for a noninteractive TOTP Setup under helper/mfa.go in the function `AddMFACallbackTOTP`. ## Other These examples are just the main use cases of these Modules, many more API calls are supported. Look at the [reference](https://pkg.go.dev/github.com/passbolt/go-passbolt) for more information. - ## Full Example This example: -1. Creates a resource; +1. Creates a resource; 2. Searches for a user named "Test User"; 3. Checks that it's not itself; and, 4. Shares the password with the "Test User" if necessary: diff --git a/api/api.go b/api/api.go index b2958c4..baba63e 100644 --- a/api/api.go +++ b/api/api.go @@ -72,11 +72,11 @@ start: if err != nil { return r, &res, fmt.Errorf("MFA Callback: %w", err) } - // ok, we got the MFA challange and the callback presumably handeld it so we can retry the original request + // ok, we got the MFA challenge and the callback presumably handeld it so we can retry the original request firstTime = false goto start } else { - return r, &res, fmt.Errorf("Got MFA Challange but the MFA callback is not defined") + return r, &res, fmt.Errorf("Got MFA Challenge but the MFA callback is not defined") } } return r, &res, fmt.Errorf("%w: Message: %v, Body: %v", ErrAPIResponseErrorStatusCode, res.Header.Message, string(res.Body)) diff --git a/api/client.go b/api/client.go index 150d0c7..e08609f 100644 --- a/api/client.go +++ b/api/client.go @@ -31,7 +31,7 @@ type Client struct { userPublicKey string userID string - // used for solving MFA challanges. You can block this to for example wait for user input. + // used for solving MFA challenges. You can block this to for example wait for user input. // You shouden't run any unrelated API Calls while you are in this callback. // 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) diff --git a/api/mfa.go b/api/mfa.go index 113e3d3..ceffe22 100644 --- a/api/mfa.go +++ b/api/mfa.go @@ -1,6 +1,6 @@ package api -type MFAChallange struct { +type MFAChallenge struct { Provider MFAProviders `json:"providers,omitempty"` } @@ -8,6 +8,6 @@ type MFAProviders struct { TOTP string `json:"totp,omitempty"` } -type MFAChallangeResponse struct { +type MFAChallengeResponse struct { TOTP string `json:"totp,omitempty"` } diff --git a/api/verify.go b/api/verify.go index b8d42d4..89a15b8 100644 --- a/api/verify.go +++ b/api/verify.go @@ -33,7 +33,7 @@ func (c *Client) SetupServerVerification(ctx context.Context) (string, string, e token := "gpgauthv1.3.0|36|" + uuid.String() + "|gpgauthv1.3.0" encToken, err := c.EncryptMessageWithPublicKey(serverKey, token) if err != nil { - return "", "", fmt.Errorf("Encrypting Challange: %w", err) + return "", "", fmt.Errorf("Encrypting Challenge: %w", err) } err = c.VerifyServer(ctx, token, encToken) if err != nil { @@ -57,7 +57,7 @@ func (c *Client) VerifyServer(ctx context.Context, token, encToken string) error } raw, _, err := c.DoCustomRequestAndReturnRawResponse(ctx, "POST", "/auth/verify.json", "v2", data, nil) if err != nil && !strings.Contains(err.Error(), "The authentication failed.") { - return fmt.Errorf("Sending Verification Challange: %w", err) + return fmt.Errorf("Sending Verification Challenge: %w", err) } if raw.Header.Get("X-GPGAuth-Verify-Response") != token { diff --git a/helper/mfa.go b/helper/mfa.go index 0c834c2..6f553aa 100644 --- a/helper/mfa.go +++ b/helper/mfa.go @@ -14,12 +14,12 @@ import ( // AddMFACallbackTOTP adds a MFA callback to the client that generates OTP Codes on demand using a Token with configurable retries and delay func AddMFACallbackTOTP(c *api.Client, retrys uint, retryDelay, offset time.Duration, token string) { c.MFACallback = func(ctx context.Context, c *api.Client, res *api.APIResponse) (http.Cookie, error) { - challange := api.MFAChallange{} - err := json.Unmarshal(res.Body, &challange) + challenge := api.MFAChallenge{} + err := json.Unmarshal(res.Body, &challenge) if err != nil { - return http.Cookie{}, fmt.Errorf("Parsing MFA Challange") + return http.Cookie{}, fmt.Errorf("Parsing MFA Challenge") } - if challange.Provider.TOTP == "" { + if challenge.Provider.TOTP == "" { return http.Cookie{}, fmt.Errorf("Server Provided no TOTP Provider") } for i := uint(0); i < retrys+1; i++ { @@ -28,14 +28,14 @@ func AddMFACallbackTOTP(c *api.Client, retrys uint, retryDelay, offset time.Dura if err != nil { return http.Cookie{}, fmt.Errorf("Error Generating MFA Code: %w", err) } - req := api.MFAChallangeResponse{ + req := api.MFAChallengeResponse{ TOTP: code, } var raw *http.Response raw, _, err = c.DoCustomRequestAndReturnRawResponse(ctx, "POST", "mfa/verify/totp.json", "v2", req, nil) if err != nil { if errors.Unwrap(err) != api.ErrAPIResponseErrorStatusCode { - return http.Cookie{}, fmt.Errorf("Doing MFA Challange Response: %w", err) + return http.Cookie{}, fmt.Errorf("Doing MFA Challenge Response: %w", err) } // MFA failed, so lets wait just let the loop try again time.Sleep(retryDelay) @@ -49,6 +49,6 @@ func AddMFACallbackTOTP(c *api.Client, retrys uint, retryDelay, offset time.Dura return http.Cookie{}, fmt.Errorf("Unable to find Passbolt MFA Cookie") } } - return http.Cookie{}, fmt.Errorf("Failed MFA Challange 3 times: %w", err) + return http.Cookie{}, fmt.Errorf("Failed MFA Challenge 3 times: %w", err) } } From a86ae886f2a7eaa8c7dfb12782658b0abd70227c Mon Sep 17 00:00:00 2001 From: lenforiee Date: Wed, 19 Apr 2023 21:24:53 +0200 Subject: [PATCH 58/74] handeld -> handled --- api/api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/api.go b/api/api.go index baba63e..b45148f 100644 --- a/api/api.go +++ b/api/api.go @@ -72,7 +72,7 @@ start: if err != nil { return r, &res, fmt.Errorf("MFA Callback: %w", err) } - // ok, we got the MFA challenge and the callback presumably handeld it so we can retry the original request + // ok, we got the MFA challenge and the callback presumably handled it so we can retry the original request firstTime = false goto start } else { From 1b178b66345eed6ef660f6eec5cbafda2adbed4c Mon Sep 17 00:00:00 2001 From: lenforiee Date: Wed, 19 Apr 2023 21:25:44 +0200 Subject: [PATCH 59/74] containes -> contains --- helper/group.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helper/group.go b/helper/group.go index 329be41..fa70ca2 100644 --- a/helper/group.go +++ b/helper/group.go @@ -14,7 +14,7 @@ type GroupMembershipOperation struct { Delete bool } -// GroupMembership containes who and what kind of membership they have with a group +// GroupMembership contains who and what kind of membership they have with a group type GroupMembership struct { UserID string Username string From ce38d65e450f44190c2ba4495eb563521c3687c5 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Thu, 10 Aug 2023 20:25:19 +0200 Subject: [PATCH 60/74] Fix Create Resource Type ID Fix determining the id for the resource type password-and-description. --- helper/resources.go | 1 + 1 file changed, 1 insertion(+) diff --git a/helper/resources.go b/helper/resources.go index bdfd9ee..0d30014 100644 --- a/helper/resources.go +++ b/helper/resources.go @@ -18,6 +18,7 @@ func CreateResource(ctx context.Context, c *api.Client, folderParentID, name, us for _, tmp := range types { if tmp.Slug == "password-and-description" { rType = &tmp + break } } if rType == nil { From 876631e7c265118a9b7434638a35012f1bc48a74 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Thu, 10 Aug 2023 20:40:04 +0200 Subject: [PATCH 61/74] update deps --- go.mod | 14 +++++++------- go.sum | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index deeb695..7f1ffab 100644 --- a/go.mod +++ b/go.mod @@ -3,18 +3,18 @@ module github.com/passbolt/go-passbolt go 1.18 require ( - github.com/ProtonMail/gopenpgp/v2 v2.5.0 + github.com/ProtonMail/gopenpgp/v2 v2.7.2 github.com/google/go-querystring v1.1.0 github.com/google/uuid v1.3.0 ) require ( - github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4 // indirect - github.com/ProtonMail/go-mime v0.0.0-20221031134845-8fd9bc37cf08 // indirect - github.com/cloudflare/circl v1.3.1 // indirect + github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95 // indirect + github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f // indirect + github.com/cloudflare/circl v1.3.3 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/stretchr/testify v1.7.0 // indirect - golang.org/x/crypto v0.4.0 // indirect - golang.org/x/sys v0.3.0 // indirect - golang.org/x/text v0.5.0 // indirect + golang.org/x/crypto v0.12.0 // indirect + golang.org/x/sys v0.11.0 // indirect + golang.org/x/text v0.12.0 // indirect ) diff --git a/go.sum b/go.sum index b46ce4b..0530f7a 100644 --- a/go.sum +++ b/go.sum @@ -1,14 +1,23 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4 h1:ra2OtmuW0AE5csawV4YXMNGNQQXvLRps3z2Z59OPO+I= github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4/go.mod h1:UBYPn8k0D56RtnR8RFQMjmh4KrZzWJ5o7Z9SYjossQ8= +github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95 h1:KLq8BE0KwCL+mmXnjLWEAOYO+2l2AE4YMmqG1ZpZHBs= +github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= github.com/ProtonMail/go-mime v0.0.0-20221031134845-8fd9bc37cf08 h1:dS7r5z4iGS0qCjM7UwWdsEMzQesUQbGcXdSm2/tWboA= github.com/ProtonMail/go-mime v0.0.0-20221031134845-8fd9bc37cf08/go.mod h1:qRZgbeASl2a9OwmsV85aWwRqic0NHPh+9ewGAzb4cgM= +github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f h1:tCbYj7/299ekTTXpdwKYF8eBlsYsDVoggDAuAjoK66k= +github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f/go.mod h1:gcr0kNtGBqin9zDW9GOHcVntrwnjrK+qdJ06mWYBybw= github.com/ProtonMail/gopenpgp/v2 v2.5.0 h1:L+98m3xj/YerdWqpWNkZmwNjg1Bs0lKRGQyuONjOImw= github.com/ProtonMail/gopenpgp/v2 v2.5.0/go.mod h1:N+W/oc/o6yqdevBw8vpgZ3JMyaMA/IwQIdnaGLCoA+w= +github.com/ProtonMail/gopenpgp/v2 v2.7.2 h1:mIwxSUPezxNYq0RA5106VPWyKC+Ly3FvBUnBJh/7GWw= +github.com/ProtonMail/gopenpgp/v2 v2.7.2/go.mod h1:IhkNEDaxec6NyzSI0PlxapinnwPVIESk8/76da3Ct3g= github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= +github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I= github.com/cloudflare/circl v1.3.1 h1:4OVCZRL62ijwEwxnF6I7hLwxvIYi3VaZt8TflkqtrtA= github.com/cloudflare/circl v1.3.1/go.mod h1:+CauBF6R70Jqcyl8N2hC8pAXYbWkGIezuSbuGLtRhnw= +github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs= +github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -26,33 +35,69 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8= golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= +golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= +golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= From adaffbce7e753cb67311cbca31321a0917f13dfa Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Fri, 24 Nov 2023 11:03:35 +0100 Subject: [PATCH 62/74] update deps --- go.mod | 18 +++++++++--------- go.sum | 57 ++++++++++++++++----------------------------------------- 2 files changed, 25 insertions(+), 50 deletions(-) diff --git a/go.mod b/go.mod index 7f1ffab..68a63ff 100644 --- a/go.mod +++ b/go.mod @@ -1,20 +1,20 @@ module github.com/passbolt/go-passbolt -go 1.18 +go 1.21 require ( - github.com/ProtonMail/gopenpgp/v2 v2.7.2 + github.com/ProtonMail/gopenpgp/v2 v2.7.4 github.com/google/go-querystring v1.1.0 - github.com/google/uuid v1.3.0 + github.com/google/uuid v1.4.0 + github.com/santhosh-tekuri/jsonschema v1.2.4 ) require ( - github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95 // indirect + github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c // indirect github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f // indirect - github.com/cloudflare/circl v1.3.3 // indirect + github.com/cloudflare/circl v1.3.6 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/stretchr/testify v1.7.0 // indirect - golang.org/x/crypto v0.12.0 // indirect - golang.org/x/sys v0.11.0 // indirect - golang.org/x/text v0.12.0 // indirect + golang.org/x/crypto v0.15.0 // indirect + golang.org/x/sys v0.14.0 // indirect + golang.org/x/text v0.14.0 // indirect ) diff --git a/go.sum b/go.sum index 0530f7a..237bfac 100644 --- a/go.sum +++ b/go.sum @@ -1,23 +1,14 @@ -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4 h1:ra2OtmuW0AE5csawV4YXMNGNQQXvLRps3z2Z59OPO+I= -github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4/go.mod h1:UBYPn8k0D56RtnR8RFQMjmh4KrZzWJ5o7Z9SYjossQ8= -github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95 h1:KLq8BE0KwCL+mmXnjLWEAOYO+2l2AE4YMmqG1ZpZHBs= github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= -github.com/ProtonMail/go-mime v0.0.0-20221031134845-8fd9bc37cf08 h1:dS7r5z4iGS0qCjM7UwWdsEMzQesUQbGcXdSm2/tWboA= -github.com/ProtonMail/go-mime v0.0.0-20221031134845-8fd9bc37cf08/go.mod h1:qRZgbeASl2a9OwmsV85aWwRqic0NHPh+9ewGAzb4cgM= +github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c h1:kMFnB0vCcX7IL/m9Y5LO+KQYv+t1CQOiFe6+SV2J7bE= +github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f h1:tCbYj7/299ekTTXpdwKYF8eBlsYsDVoggDAuAjoK66k= github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f/go.mod h1:gcr0kNtGBqin9zDW9GOHcVntrwnjrK+qdJ06mWYBybw= -github.com/ProtonMail/gopenpgp/v2 v2.5.0 h1:L+98m3xj/YerdWqpWNkZmwNjg1Bs0lKRGQyuONjOImw= -github.com/ProtonMail/gopenpgp/v2 v2.5.0/go.mod h1:N+W/oc/o6yqdevBw8vpgZ3JMyaMA/IwQIdnaGLCoA+w= -github.com/ProtonMail/gopenpgp/v2 v2.7.2 h1:mIwxSUPezxNYq0RA5106VPWyKC+Ly3FvBUnBJh/7GWw= -github.com/ProtonMail/gopenpgp/v2 v2.7.2/go.mod h1:IhkNEDaxec6NyzSI0PlxapinnwPVIESk8/76da3Ct3g= -github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= +github.com/ProtonMail/gopenpgp/v2 v2.7.4 h1:Vz/8+HViFFnf2A6XX8JOvZMrA6F5puwNvvF21O1mRlo= +github.com/ProtonMail/gopenpgp/v2 v2.7.4/go.mod h1:IhkNEDaxec6NyzSI0PlxapinnwPVIESk8/76da3Ct3g= github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= -github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I= -github.com/cloudflare/circl v1.3.1 h1:4OVCZRL62ijwEwxnF6I7hLwxvIYi3VaZt8TflkqtrtA= -github.com/cloudflare/circl v1.3.1/go.mod h1:+CauBF6R70Jqcyl8N2hC8pAXYbWkGIezuSbuGLtRhnw= -github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs= github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= +github.com/cloudflare/circl v1.3.6 h1:/xbKIqSHbZXHwkhbrhrt2YOHIwYJlXH94E3tI/gDlUg= +github.com/cloudflare/circl v1.3.6/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -25,34 +16,26 @@ github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= +github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/santhosh-tekuri/jsonschema v1.2.4 h1:hNhW8e7t+H1vgY+1QeEQpveR6D4+OwKPXCfD2aieJis= +github.com/santhosh-tekuri/jsonschema v1.2.4/go.mod h1:TEAUOeZSmIxTTuHatJzrvARHiuO9LYd+cIxzgEHCQI4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= -golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8= -golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= -golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= -golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= +golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= @@ -63,43 +46,35 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= -golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= -golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 605db2b047798212670c85645a59cde87c821721 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Fri, 24 Nov 2023 11:07:38 +0100 Subject: [PATCH 63/74] Add Secret Json Schema Validation --- api/resource_types.go | 19 ++++++++++++------- helper/resources.go | 10 ++++++++++ helper/share.go | 16 ++++++++++++++++ helper/util.go | 30 ++++++++++++++++++++++++++++++ 4 files changed, 68 insertions(+), 7 deletions(-) diff --git a/api/resource_types.go b/api/resource_types.go index 5b3a753..0f091d0 100644 --- a/api/resource_types.go +++ b/api/resource_types.go @@ -6,14 +6,19 @@ import ( "fmt" ) -//ResourceType is the Type of a Resource +// ResourceType is the Type of a Resource type ResourceType struct { - ID string `json:"id,omitempty"` - Slug string `json:"slug,omitempty"` - Description string `json:"description,omitempty"` - Definition json.RawMessage `json:"definition,omitempty"` - Created *Time `json:"created,omitempty"` - Modified *Time `json:"modified,omitempty"` + ID string `json:"id,omitempty"` + Slug string `json:"slug,omitempty"` + Description string `json:"description,omitempty"` + Definition string `json:"definition,omitempty"` + Created *Time `json:"created,omitempty"` + Modified *Time `json:"modified,omitempty"` +} + +type ResourceTypeSchema struct { + Resource json.RawMessage `json:"resource"` + Secret json.RawMessage `json:"secret"` } // GetResourceTypesOptions is a placeholder for future options diff --git a/helper/resources.go b/helper/resources.go index 0d30014..72d9744 100644 --- a/helper/resources.go +++ b/helper/resources.go @@ -42,6 +42,11 @@ func CreateResource(ctx context.Context, c *api.Client, folderParentID, name, us 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) @@ -223,6 +228,11 @@ func UpdateResource(ctx context.Context, c *api.Client, resourceID, name, userna return fmt.Errorf("Unknown ResourceType: %v", rType.Slug) } + err = validateSecretData(rType, secretData) + if err != nil { + return fmt.Errorf("Validating Secret Data: %w", err) + } + newResource.Secrets = []api.Secret{} for _, user := range users { var encSecretData string diff --git a/helper/share.go b/helper/share.go index f28dbbd..d9514b3 100644 --- a/helper/share.go +++ b/helper/share.go @@ -63,6 +63,22 @@ func ShareResource(ctx context.Context, c *api.Client, resourceID string, change return fmt.Errorf("Decrypting Resource Secret: %w", err) } + // Secret Validation + resource, err := c.GetResource(ctx, resourceID) + if err != nil { + return fmt.Errorf("Getting Resource: %w", err) + } + + rType, err := c.GetResourceType(ctx, resource.ResourceTypeID) + if err != nil { + return fmt.Errorf("Getting ResourceType: %w", err) + } + + err = validateSecretData(rType, secretData) + if err != nil { + return fmt.Errorf("Validating Secret Data: %w", err) + } + simulationResult, err := c.SimulateShareResource(ctx, resourceID, shareRequest) if err != nil { return fmt.Errorf("Simulate Share Resource: %w", err) diff --git a/helper/util.go b/helper/util.go index 78b1960..1692870 100644 --- a/helper/util.go +++ b/helper/util.go @@ -1,9 +1,13 @@ package helper import ( + "bytes" + "encoding/json" "fmt" + "strings" "github.com/passbolt/go-passbolt/api" + "github.com/santhosh-tekuri/jsonschema" ) func getPublicKeyByUserID(userID string, Users []api.User) (string, error) { @@ -32,3 +36,29 @@ func getSecretByResourceID(secrets []api.Secret, resourceID string) (*api.Secret } return nil, fmt.Errorf("Cannot Find Secret for id %v", resourceID) } + +func validateSecretData(rType *api.ResourceType, secretData string) error { + var schemaDefinition api.ResourceTypeSchema + err := json.Unmarshal([]byte(rType.Definition), &schemaDefinition) + if err != nil { + return fmt.Errorf("Unmarshal Json Schema: %w", err) + } + + comp := jsonschema.NewCompiler() + + err = comp.AddResource("secret.json", bytes.NewReader(schemaDefinition.Secret)) + if err != nil { + return fmt.Errorf("Adding Json Schema: %w", err) + } + + schema, err := comp.Compile("secret.json") + if err != nil { + return fmt.Errorf("Compiling Json Schema: %w", err) + } + + err = schema.Validate(strings.NewReader(secretData)) + if err != nil { + return fmt.Errorf("Validating Secret Data: %w", err) + } + return nil +} From 8dbb07720dbbb76a099d97d151c385514e251705 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Fri, 24 Nov 2023 11:08:49 +0100 Subject: [PATCH 64/74] Add totp and password-description-totp Support --- api/secrets.go | 19 ++++++++++++++ helper/resources.go | 62 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+) diff --git a/api/secrets.go b/api/secrets.go index 1f63b23..8bcad6c 100644 --- a/api/secrets.go +++ b/api/secrets.go @@ -22,6 +22,25 @@ type SecretDataTypePasswordAndDescription struct { Description string `json:"description,omitempty"` } +type SecretDataTOTP struct { + Algorithm string `json:"algorithm"` + SecretKey string `json:"secret_key"` + Digits int `json:"digits"` + Period int `json:"period"` +} + +// SecretDataTypeTOTP is the format a secret of resource type "totp" is stored in +type SecretDataTypeTOTP struct { + TOTP SecretDataTOTP `json:"totp"` +} + +// SecretDataTypePasswordDescriptionTOTP is the format a secret of resource type "password-description-totp" is stored in +type SecretDataTypePasswordDescriptionTOTP struct { + Password string `json:"password"` + Description string `json:"description,omitempty"` + TOTP SecretDataTOTP `json:"totp"` +} + // GetSecret gets a Passbolt Secret func (c *Client) GetSecret(ctx context.Context, resourceID string) (*Secret, error) { err := checkUUIDFormat(resourceID) diff --git a/helper/resources.go b/helper/resources.go index 72d9744..6a4f786 100644 --- a/helper/resources.go +++ b/helper/resources.go @@ -128,6 +128,21 @@ func GetResourceFromData(c *api.Client, resource api.Resource, secret api.Secret } pw = secretData.Password desc = secretData.Description + case "password-description-totp": + rawSecretData, err := c.DecryptMessage(secret.Data) + if err != nil { + return "", "", "", "", "", "", fmt.Errorf("Decrypting Secret Data: %w", err) + } + + var secretData api.SecretDataTypePasswordDescriptionTOTP + err = json.Unmarshal([]byte(rawSecretData), &secretData) + if err != nil { + return "", "", "", "", "", "", fmt.Errorf("Parsing Decrypted Secret Data: %w", err) + } + pw = secretData.Password + desc = secretData.Description + case "totp": + // nothing fits into the interface in this case default: return "", "", "", "", "", "", fmt.Errorf("Unknown ResourceType: %v", rType.Slug) } @@ -224,6 +239,53 @@ func UpdateResource(ctx context.Context, c *api.Client, resourceID, name, userna return fmt.Errorf("Marshalling Secret Data: %w", err) } secretData = string(res) + case "password-description-totp": + secret, err := c.GetSecret(ctx, resourceID) + if err != nil { + return fmt.Errorf("Getting Secret: %w", err) + } + oldSecretData, err := c.DecryptMessage(secret.Data) + if err != nil { + return fmt.Errorf("Decrypting Secret: %w", err) + } + var oldSecret api.SecretDataTypePasswordDescriptionTOTP + err = json.Unmarshal([]byte(oldSecretData), &secretData) + if err != nil { + return fmt.Errorf("Parsing Decrypted Secret Data: %w", err) + } + if password != "" { + oldSecret.Password = password + } + if description != "" { + oldSecret.Description = description + } + + res, err := json.Marshal(&oldSecret) + if err != nil { + return fmt.Errorf("Marshalling Secret Data: %w", err) + } + secretData = string(res) + case "totp": + secret, err := c.GetSecret(ctx, resourceID) + if err != nil { + return fmt.Errorf("Getting Secret: %w", err) + } + oldSecretData, err := c.DecryptMessage(secret.Data) + if err != nil { + return fmt.Errorf("Decrypting Secret: %w", err) + } + var oldSecret api.SecretDataTypeTOTP + err = json.Unmarshal([]byte(oldSecretData), &secretData) + if err != nil { + return fmt.Errorf("Parsing Decrypted Secret Data: %w", err) + } + // since we don't have totp parameters we don't do anything + + res, err := json.Marshal(&oldSecret) + if err != nil { + return fmt.Errorf("Marshalling Secret Data: %w", err) + } + secretData = string(res) default: return fmt.Errorf("Unknown ResourceType: %v", rType.Slug) } From a6a98a6887607bcf73a70bfdbed710e755560536 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Fri, 24 Nov 2023 11:18:19 +0100 Subject: [PATCH 65/74] fix ci --- .github/workflows/.go.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/.go.yml b/.github/workflows/.go.yml index bdb7a79..94fc487 100644 --- a/.github/workflows/.go.yml +++ b/.github/workflows/.go.yml @@ -13,7 +13,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-go@v2 with: - go-version: 1.17 + go-version: 1.21 - name: "Setup Passbolt" run: | git clone https://github.com/passbolt/passbolt_docker.git ../passbolt_docker From 360cc3748efa3462f94f222cb77a024871a83ded Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Fri, 24 Nov 2023 11:28:38 +0100 Subject: [PATCH 66/74] Enable Debug Output for tests --- helper/setup_test.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/helper/setup_test.go b/helper/setup_test.go index 8ec3d2f..1d99e2f 100644 --- a/helper/setup_test.go +++ b/helper/setup_test.go @@ -31,6 +31,9 @@ func TestMain(m *testing.M) { panic(fmt.Errorf("Creating Registration Client: %w", err)) } + // Debug Output + rc.Debug = true + ctx := context.TODO() privkey, err := SetupAccount(ctx, rc, userID, token, "password123") @@ -43,6 +46,9 @@ func TestMain(m *testing.M) { panic(fmt.Errorf("Setup Client: %w", err)) } + // Debug Output + c.Debug = true + c.Login(ctx) if err != nil { panic(fmt.Errorf("Login Client: %w", err)) From e13f484bcbacb8830afe9aa7ad4fb77ee3efbaed Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Fri, 24 Nov 2023 13:32:47 +0100 Subject: [PATCH 67/74] Add Workaround for inconsistent API Response --- api/resource_types.go | 12 ++++++------ helper/util.go | 17 ++++++++++++++++- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/api/resource_types.go b/api/resource_types.go index 0f091d0..3bdc1bc 100644 --- a/api/resource_types.go +++ b/api/resource_types.go @@ -8,12 +8,12 @@ import ( // ResourceType is the Type of a Resource type ResourceType struct { - ID string `json:"id,omitempty"` - Slug string `json:"slug,omitempty"` - Description string `json:"description,omitempty"` - Definition string `json:"definition,omitempty"` - Created *Time `json:"created,omitempty"` - Modified *Time `json:"modified,omitempty"` + ID string `json:"id,omitempty"` + Slug string `json:"slug,omitempty"` + Description string `json:"description,omitempty"` + Definition json.RawMessage `json:"definition,omitempty"` + Created *Time `json:"created,omitempty"` + Modified *Time `json:"modified,omitempty"` } type ResourceTypeSchema struct { diff --git a/helper/util.go b/helper/util.go index 1692870..4d7a144 100644 --- a/helper/util.go +++ b/helper/util.go @@ -41,7 +41,22 @@ func validateSecretData(rType *api.ResourceType, secretData string) error { var schemaDefinition api.ResourceTypeSchema err := json.Unmarshal([]byte(rType.Definition), &schemaDefinition) if err != nil { - return fmt.Errorf("Unmarshal Json Schema: %w", err) + // Workaround for inconsistant API Responses where sometime the Schema is embedded directly and sometimes it's escaped as a string + if err.Error() == "json: cannot unmarshal string into Go value of type api.ResourceTypeSchema" { + var tmp string + err = json.Unmarshal([]byte(rType.Definition), &tmp) + if err != nil { + return fmt.Errorf("Workaround Unmarshal Json Schema String: %w", err) + } + + err = json.Unmarshal([]byte(tmp), &schemaDefinition) + if err != nil { + return fmt.Errorf("Workaround Unmarshal Json Schema: %w", err) + } + + } else { + return fmt.Errorf("Unmarshal Json Schema: %w", err) + } } comp := jsonschema.NewCompiler() From 1499a806253b6e7df681f7e7df9daca092ba3cb8 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Tue, 13 Aug 2024 11:11:42 +0200 Subject: [PATCH 68/74] Update docker compose command --- .github/workflows/.go.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/.go.yml b/.github/workflows/.go.yml index 94fc487..5e7ca0e 100644 --- a/.github/workflows/.go.yml +++ b/.github/workflows/.go.yml @@ -18,7 +18,7 @@ jobs: run: | git clone https://github.com/passbolt/passbolt_docker.git ../passbolt_docker cd ../passbolt_docker - docker-compose -f docker-compose/docker-compose-ce.yaml up -d + docker compose -f docker-compose/docker-compose-ce.yaml up -d docker ps -a - name: "Test" run: | From 1bbe7dc952c47107293ae6160b93ebb063a0d428 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Tue, 13 Aug 2024 11:16:30 +0200 Subject: [PATCH 69/74] Update Docker Container Name --- .github/workflows/.go.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/.go.yml b/.github/workflows/.go.yml index 5e7ca0e..07957c8 100644 --- a/.github/workflows/.go.yml +++ b/.github/workflows/.go.yml @@ -22,8 +22,8 @@ jobs: docker ps -a - name: "Test" run: | - docker exec docker-compose_passbolt_1 sh -c '/usr/bin/wait-for.sh -t 30 localhost:443' - output=$(docker exec docker-compose_passbolt_1 sh -c 'su -m -c "/usr/share/php/passbolt/bin/cake \ + docker exec docker-compose-passbolt-1 sh -c '/usr/bin/wait-for.sh -t 30 localhost:443' + output=$(docker exec docker-compose-passbolt-1 sh -c 'su -m -c "/usr/share/php/passbolt/bin/cake \ passbolt register_user \ -u your@email.com \ -f yourname \ From 390b7be866a4bc17c2c84c915952f38288fd3206 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Fri, 9 Aug 2024 19:23:09 +0200 Subject: [PATCH 70/74] Fix MFA detection with custom APP_BASE --- api/api.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/api/api.go b/api/api.go index b45148f..601a358 100644 --- a/api/api.go +++ b/api/api.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "net/http" + "strings" ) // APIResponse is the Struct representation of a Json Response @@ -62,7 +63,7 @@ start: if res.Header.Status == "success" { return r, &res, nil } else if res.Header.Status == "error" { - if res.Header.Code == 403 && res.Header.URL == "/mfa/verify/error.json" { + if res.Header.Code == 403 && strings.HasSuffix(res.Header.URL, "/mfa/verify/error.json") { if !firstTime { // if we are here this probably means that the MFA callback is broken, to prevent a infinite loop lets error here return r, &res, fmt.Errorf("Got MFA challenge twice in a row, is your MFA Callback broken? Bailing to prevent loop...:") From 2b3bb4838500cbd067a49d85b49cfe315bee5509 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Tue, 13 Aug 2024 11:34:35 +0200 Subject: [PATCH 71/74] update deps --- go.mod | 14 +++++++------- go.sum | 14 ++++++++++++++ 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 68a63ff..c89055d 100644 --- a/go.mod +++ b/go.mod @@ -3,18 +3,18 @@ module github.com/passbolt/go-passbolt go 1.21 require ( - github.com/ProtonMail/gopenpgp/v2 v2.7.4 + github.com/ProtonMail/gopenpgp/v2 v2.7.5 github.com/google/go-querystring v1.1.0 - github.com/google/uuid v1.4.0 + github.com/google/uuid v1.6.0 github.com/santhosh-tekuri/jsonschema v1.2.4 ) require ( - github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c // indirect + github.com/ProtonMail/go-crypto v1.0.0 // indirect github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f // indirect - github.com/cloudflare/circl v1.3.6 // indirect + github.com/cloudflare/circl v1.3.9 // indirect github.com/pkg/errors v0.9.1 // indirect - golang.org/x/crypto v0.15.0 // indirect - golang.org/x/sys v0.14.0 // indirect - golang.org/x/text v0.14.0 // indirect + golang.org/x/crypto v0.26.0 // indirect + golang.org/x/sys v0.24.0 // indirect + golang.org/x/text v0.17.0 // indirect ) diff --git a/go.sum b/go.sum index 237bfac..0129c57 100644 --- a/go.sum +++ b/go.sum @@ -1,14 +1,20 @@ github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c h1:kMFnB0vCcX7IL/m9Y5LO+KQYv+t1CQOiFe6+SV2J7bE= github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= +github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78= +github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f h1:tCbYj7/299ekTTXpdwKYF8eBlsYsDVoggDAuAjoK66k= github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f/go.mod h1:gcr0kNtGBqin9zDW9GOHcVntrwnjrK+qdJ06mWYBybw= github.com/ProtonMail/gopenpgp/v2 v2.7.4 h1:Vz/8+HViFFnf2A6XX8JOvZMrA6F5puwNvvF21O1mRlo= github.com/ProtonMail/gopenpgp/v2 v2.7.4/go.mod h1:IhkNEDaxec6NyzSI0PlxapinnwPVIESk8/76da3Ct3g= +github.com/ProtonMail/gopenpgp/v2 v2.7.5 h1:STOY3vgES59gNgoOt2w0nyHBjKViB/qSg7NjbQWPJkA= +github.com/ProtonMail/gopenpgp/v2 v2.7.5/go.mod h1:IhkNEDaxec6NyzSI0PlxapinnwPVIESk8/76da3Ct3g= 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.6 h1:/xbKIqSHbZXHwkhbrhrt2YOHIwYJlXH94E3tI/gDlUg= github.com/cloudflare/circl v1.3.6/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= +github.com/cloudflare/circl v1.3.9 h1:QFrlgFYf2Qpi8bSpVPK1HBvWpx16v/1TZivyo7pGuBE= +github.com/cloudflare/circl v1.3.9/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -18,6 +24,8 @@ github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -34,6 +42,8 @@ golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2Uz golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= +golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= +golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -56,6 +66,8 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= +golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= @@ -69,6 +81,8 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= +golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= From c86afac97c1d918772d3b5f68920686b2b16e693 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Tue, 13 Aug 2024 11:47:33 +0200 Subject: [PATCH 72/74] go mod tidy --- go.sum | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/go.sum b/go.sum index 0129c57..3e8584d 100644 --- a/go.sum +++ b/go.sum @@ -1,18 +1,12 @@ github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= -github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c h1:kMFnB0vCcX7IL/m9Y5LO+KQYv+t1CQOiFe6+SV2J7bE= -github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78= github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f h1:tCbYj7/299ekTTXpdwKYF8eBlsYsDVoggDAuAjoK66k= github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f/go.mod h1:gcr0kNtGBqin9zDW9GOHcVntrwnjrK+qdJ06mWYBybw= -github.com/ProtonMail/gopenpgp/v2 v2.7.4 h1:Vz/8+HViFFnf2A6XX8JOvZMrA6F5puwNvvF21O1mRlo= -github.com/ProtonMail/gopenpgp/v2 v2.7.4/go.mod h1:IhkNEDaxec6NyzSI0PlxapinnwPVIESk8/76da3Ct3g= github.com/ProtonMail/gopenpgp/v2 v2.7.5 h1:STOY3vgES59gNgoOt2w0nyHBjKViB/qSg7NjbQWPJkA= github.com/ProtonMail/gopenpgp/v2 v2.7.5/go.mod h1:IhkNEDaxec6NyzSI0PlxapinnwPVIESk8/76da3Ct3g= 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.6 h1:/xbKIqSHbZXHwkhbrhrt2YOHIwYJlXH94E3tI/gDlUg= -github.com/cloudflare/circl v1.3.6/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= github.com/cloudflare/circl v1.3.9 h1:QFrlgFYf2Qpi8bSpVPK1HBvWpx16v/1TZivyo7pGuBE= github.com/cloudflare/circl v1.3.9/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -22,8 +16,6 @@ github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= -github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= -github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -40,8 +32,6 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= -golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -64,8 +54,6 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= -golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -79,8 +67,6 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= From c1904ca20a7a1e800e96e86054b9f439741084e2 Mon Sep 17 00:00:00 2001 From: Nelson Isioma Date: Sun, 23 Feb 2025 13:47:41 +0100 Subject: [PATCH 73/74] fix: correctly generates otp code from token with extra padding --- helper/totp.go | 3 +++ helper/totp_test.go | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 helper/totp_test.go diff --git a/helper/totp.go b/helper/totp.go index 42cf7b9..d32b9c2 100644 --- a/helper/totp.go +++ b/helper/totp.go @@ -33,6 +33,9 @@ func GenerateOTPCode(token string, when time.Time) (string, error) { // It should be uppercase always token = strings.ToUpper(token) + // Remove all the extra "=" padding at the end + token = strings.TrimRight(token, "=") + secretBytes, err := base32.StdEncoding.WithPadding(base32.NoPadding).DecodeString(token) if err != nil { return "", fmt.Errorf("Decoding token string: %w", err) diff --git a/helper/totp_test.go b/helper/totp_test.go new file mode 100644 index 0000000..aad32b6 --- /dev/null +++ b/helper/totp_test.go @@ -0,0 +1,36 @@ +package helper + +import ( + "testing" + "time" +) + +var testCases = []struct { + description string + token string + expectErr bool +}{ + {"generates otpcode from token with padding", "PGWXXL7B66MMSRBAWSKEKIYD3P675KRJ===", false}, + {"generates otpcode from token without padding", "JBSWY3DPEHPK3PXPJBSWY3DPEHPK3PXP", false}, + {"invalid token format", "INVALIDTOKEN123", true}, +} + +func TestGenerateOTPCode(t *testing.T) { + for _, tc := range testCases { + t.Run(tc.description, func(t *testing.T) { + code, err := GenerateOTPCode(tc.token, time.Now()) + + if tc.expectErr { + if err == nil { + t.Errorf("Expected error for input '%s', but got none", tc.token) + } + } else { + if err != nil { + t.Errorf("GenerateOTPCode returned an error: %s", err.Error()) + } else if len(code) != 6 { + t.Errorf("Expected 6-digit OTP, got: %s", code) + } + } + }) + } +} From 3762c5e27891b47a003bf40703fcdeed94e83656 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Sun, 2 Mar 2025 22:59:35 +0100 Subject: [PATCH 74/74] update deps --- .github/workflows/.go.yml | 2 +- go.mod | 14 +++++++------- go.sum | 13 +++++++++++++ 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/.github/workflows/.go.yml b/.github/workflows/.go.yml index 07957c8..4611250 100644 --- a/.github/workflows/.go.yml +++ b/.github/workflows/.go.yml @@ -13,7 +13,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-go@v2 with: - go-version: 1.21 + go-version: 1.23 - name: "Setup Passbolt" run: | git clone https://github.com/passbolt/passbolt_docker.git ../passbolt_docker diff --git a/go.mod b/go.mod index c89055d..64273dd 100644 --- a/go.mod +++ b/go.mod @@ -1,20 +1,20 @@ module github.com/passbolt/go-passbolt -go 1.21 +go 1.23.0 require ( - github.com/ProtonMail/gopenpgp/v2 v2.7.5 + github.com/ProtonMail/gopenpgp/v2 v2.8.3 github.com/google/go-querystring v1.1.0 github.com/google/uuid v1.6.0 github.com/santhosh-tekuri/jsonschema v1.2.4 ) require ( - github.com/ProtonMail/go-crypto v1.0.0 // indirect + github.com/ProtonMail/go-crypto v1.1.6 // indirect github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f // indirect - github.com/cloudflare/circl v1.3.9 // indirect + github.com/cloudflare/circl v1.6.0 // indirect github.com/pkg/errors v0.9.1 // indirect - golang.org/x/crypto v0.26.0 // indirect - golang.org/x/sys v0.24.0 // indirect - golang.org/x/text v0.17.0 // indirect + golang.org/x/crypto v0.35.0 // indirect + golang.org/x/sys v0.30.0 // indirect + golang.org/x/text v0.22.0 // indirect ) diff --git a/go.sum b/go.sum index 3e8584d..24212a3 100644 --- a/go.sum +++ b/go.sum @@ -1,14 +1,21 @@ github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78= github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= +github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw= +github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f h1:tCbYj7/299ekTTXpdwKYF8eBlsYsDVoggDAuAjoK66k= github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f/go.mod h1:gcr0kNtGBqin9zDW9GOHcVntrwnjrK+qdJ06mWYBybw= github.com/ProtonMail/gopenpgp/v2 v2.7.5 h1:STOY3vgES59gNgoOt2w0nyHBjKViB/qSg7NjbQWPJkA= 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/go.mod h1:LiuOTbnJit8w9ZzOoLscj0kmdALY7hfoCVh5Qlb0bcg= 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.9 h1:QFrlgFYf2Qpi8bSpVPK1HBvWpx16v/1TZivyo7pGuBE= github.com/cloudflare/circl v1.3.9/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU= +github.com/cloudflare/circl v1.6.0 h1:cr5JKic4HI+LkINy2lg3W2jF8sHCVTBncJr5gIIq7qk= +github.com/cloudflare/circl v1.6.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -34,6 +41,8 @@ golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2Uz golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= +golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs= +golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -56,6 +65,8 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= @@ -69,6 +80,8 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=