From 06ba312c2865d6dff1be19b1b712f33feee591df Mon Sep 17 00:00:00 2001 From: James Elliott Date: Mon, 25 Apr 2022 18:49:18 +1000 Subject: [PATCH] fix(commands): invalid opaque id service name (#3235) This fixes the service type being openid_connect instead of openid as expected. This also allows bulk generating opaque identifiers for users. --- internal/commands/const.go | 6 +- internal/commands/storage.go | 17 +++++- internal/commands/storage_run.go | 91 ++++++++++++++++++++++++++++++- internal/suites/suite_cli_test.go | 26 ++++----- 4 files changed, 123 insertions(+), 17 deletions(-) diff --git a/internal/commands/const.go b/internal/commands/const.go index 3bcb27a87..bba2a615d 100644 --- a/internal/commands/const.go +++ b/internal/commands/const.go @@ -113,5 +113,9 @@ var ( ) const ( - identifierServiceOpenIDConnect = "openid_connect" + identifierServiceOpenIDConnect = "openid" +) + +var ( + validIdentifierServices = []string{identifierServiceOpenIDConnect} ) diff --git a/internal/commands/storage.go b/internal/commands/storage.go index debfdc6bf..5ceae4c34 100644 --- a/internal/commands/storage.go +++ b/internal/commands/storage.go @@ -71,6 +71,7 @@ func newStorageUserIdentifiersCmd() (cmd *cobra.Command) { cmd.AddCommand( newStorageUserIdentifiersExportCmd(), newStorageUserIdentifiersImportCmd(), + newStorageUserIdentifiersGenerateCmd(), newStorageUserIdentifiersAddCmd(), ) @@ -101,6 +102,20 @@ func newStorageUserIdentifiersImportCmd() (cmd *cobra.Command) { return cmd } +func newStorageUserIdentifiersGenerateCmd() (cmd *cobra.Command) { + cmd = &cobra.Command{ + Use: "generate", + Short: "Generate opaque identifiers in bulk", + RunE: storageUserIdentifiersGenerate, + } + + cmd.Flags().StringSlice("users", nil, "The list of users to generate the opaque identifiers for") + cmd.Flags().StringSlice("services", []string{identifierServiceOpenIDConnect}, "The list of services to generate the opaque identifiers for, valid values are: openid") + cmd.Flags().StringSlice("sectors", []string{""}, "The list of sectors to generate identifiers for") + + return cmd +} + func newStorageUserIdentifiersAddCmd() (cmd *cobra.Command) { cmd = &cobra.Command{ Use: "add [username]", @@ -110,7 +125,7 @@ func newStorageUserIdentifiersAddCmd() (cmd *cobra.Command) { } cmd.Flags().String("identifier", "", "The optional version 4 UUID to use, if not set a random one will be used") - cmd.Flags().String("service", identifierServiceOpenIDConnect, "The service to add the identifier for, valid values are: openid_connect") + cmd.Flags().String("service", identifierServiceOpenIDConnect, "The service to add the identifier for, valid values are: openid") cmd.Flags().String("sector", "", "The sector identifier to use (should usually be blank)") return cmd diff --git a/internal/commands/storage_run.go b/internal/commands/storage_run.go index 318728842..c425b1490 100644 --- a/internal/commands/storage_run.go +++ b/internal/commands/storage_run.go @@ -2,6 +2,7 @@ package commands import ( "context" + "database/sql" "encoding/base32" "errors" "fmt" @@ -759,6 +760,92 @@ func storageUserIdentifiersImport(cmd *cobra.Command, _ []string) (err error) { return nil } +func containsIdentifier(identifier model.UserOpaqueIdentifier, identifiers []model.UserOpaqueIdentifier) bool { + for i := 0; i < len(identifiers); i++ { + if identifier.Service == identifiers[i].Service && identifier.SectorID == identifiers[i].SectorID && identifier.Username == identifiers[i].Username { + return true + } + } + + return false +} + +func storageUserIdentifiersGenerate(cmd *cobra.Command, _ []string) (err error) { + var ( + provider storage.Provider + + ctx = context.Background() + + users, services, sectors []string + ) + + provider = getStorageProvider() + + identifiers, err := provider.LoadUserOpaqueIdentifiers(ctx) + if err != nil && !errors.Is(err, sql.ErrNoRows) { + return fmt.Errorf("can't load the existing identifiers: %w", err) + } + + if users, err = cmd.Flags().GetStringSlice("users"); err != nil { + return err + } + + if services, err = cmd.Flags().GetStringSlice("services"); err != nil { + return err + } + + if sectors, err = cmd.Flags().GetStringSlice("sectors"); err != nil { + return err + } + + if len(users) == 0 { + return fmt.Errorf("must supply at least one user") + } + + if len(sectors) == 0 { + sectors = append(sectors, "") + } + + if !utils.IsStringSliceContainsAll(services, validIdentifierServices) { + return fmt.Errorf("one or more the service names '%s' is invalid, the valid values are: '%s'", strings.Join(services, "', '"), strings.Join(validIdentifierServices, "', '")) + } + + var added, duplicates int + + for _, service := range services { + for _, sector := range sectors { + for _, username := range users { + identifier := model.UserOpaqueIdentifier{ + Service: service, + SectorID: sector, + Username: username, + } + + if containsIdentifier(identifier, identifiers) { + duplicates++ + + continue + } + + identifier.Identifier, err = uuid.NewRandom() + if err != nil { + return fmt.Errorf("failed to generate a uuid: %w", err) + } + + if err = provider.SaveUserOpaqueIdentifier(ctx, identifier); err != nil { + return fmt.Errorf("failed to save identifier: %w", err) + } + + added++ + } + } + } + + fmt.Printf("Successfully added %d opaque identifiers and %d duplicates were skipped\n", added, duplicates) + + return nil +} + func storageUserIdentifiersAdd(cmd *cobra.Command, args []string) (err error) { var ( provider storage.Provider @@ -774,8 +861,8 @@ func storageUserIdentifiersAdd(cmd *cobra.Command, args []string) (err error) { if service == "" { service = identifierServiceOpenIDConnect - } else if service != identifierServiceOpenIDConnect { - return fmt.Errorf("the service name '%s' is invalid, the valid values are: 'openid_connect'", service) + } else if !utils.IsStringInSlice(service, validIdentifierServices) { + return fmt.Errorf("the service name '%s' is invalid, the valid values are: '%s'", service, strings.Join(validIdentifierServices, "', '")) } if sector, err = cmd.Flags().GetString("sector"); err != nil { diff --git a/internal/suites/suite_cli_test.go b/internal/suites/suite_cli_test.go index a3aef5900..9ba3893da 100644 --- a/internal/suites/suite_cli_test.go +++ b/internal/suites/suite_cli_test.go @@ -412,11 +412,11 @@ func (s *CLISuite) TestStorage04ShouldManageUniqueID() { output, err = s.Exec("authelia-backend", []string{"authelia", s.testArg, s.coverageArg, "storage", "user", "identifiers", "add", "john", "--service=webauthn", "--sector=''", "--identifier=1097c8f8-83f2-4506-8138-5f40e83a1285", "--config=/config/configuration.storage.yml"}) s.Assert().EqualError(err, "exit status 1") - s.Assert().Contains(output, "Error: the service name 'webauthn' is invalid, the valid values are: 'openid_connect'") + s.Assert().Contains(output, "Error: the service name 'webauthn' is invalid, the valid values are: 'openid'") - output, err = s.Exec("authelia-backend", []string{"authelia", s.testArg, s.coverageArg, "storage", "user", "identifiers", "add", "john", "--service=openid_connect", "--sector=''", "--identifier=1097c8f8-83f2-4506-8138-5f40e83a1285", "--config=/config/configuration.storage.yml"}) + output, err = s.Exec("authelia-backend", []string{"authelia", s.testArg, s.coverageArg, "storage", "user", "identifiers", "add", "john", "--service=openid", "--sector=''", "--identifier=1097c8f8-83f2-4506-8138-5f40e83a1285", "--config=/config/configuration.storage.yml"}) s.Assert().NoError(err) - s.Assert().Contains(output, "Added User Opaque Identifier:\n\tService: openid_connect\n\tSector: \n\tUsername: john\n\tIdentifier: 1097c8f8-83f2-4506-8138-5f40e83a1285\n\n") + s.Assert().Contains(output, "Added User Opaque Identifier:\n\tService: openid\n\tSector: \n\tUsername: john\n\tIdentifier: 1097c8f8-83f2-4506-8138-5f40e83a1285\n\n") output, err = s.Exec("authelia-backend", []string{"authelia", s.testArg, s.coverageArg, "storage", "user", "identifiers", "export", "--file=/a/no/path/fileout.yml", "--config=/config/configuration.storage.yml"}) s.Assert().EqualError(err, "exit status 1") @@ -434,27 +434,27 @@ func (s *CLISuite) TestStorage04ShouldManageUniqueID() { s.Assert().EqualError(err, "exit status 1") s.Assert().Contains(output, "Error: must specify a file that doesn't exist but '/tmp/out/1.yml' exists") - output, err = s.Exec("authelia-backend", []string{"authelia", s.testArg, s.coverageArg, "storage", "user", "identifiers", "add", "john", "--service=openid_connect", "--sector=''", "--identifier=1097c8f8-83f2-4506-8138-5f40e83a1285", "--config=/config/configuration.storage.yml"}) + output, err = s.Exec("authelia-backend", []string{"authelia", s.testArg, s.coverageArg, "storage", "user", "identifiers", "add", "john", "--service=openid", "--sector=''", "--identifier=1097c8f8-83f2-4506-8138-5f40e83a1285", "--config=/config/configuration.storage.yml"}) s.Assert().EqualError(err, "exit status 1") s.Assert().Contains(output, "Error: error inserting user opaque id for user 'john' with opaque id '1097c8f8-83f2-4506-8138-5f40e83a1285':") - output, err = s.Exec("authelia-backend", []string{"authelia", s.testArg, s.coverageArg, "storage", "user", "identifiers", "add", "john", "--service=openid_connect", "--sector=''", "--config=/config/configuration.storage.yml"}) + output, err = s.Exec("authelia-backend", []string{"authelia", s.testArg, s.coverageArg, "storage", "user", "identifiers", "add", "john", "--service=openid", "--sector=''", "--config=/config/configuration.storage.yml"}) s.Assert().EqualError(err, "exit status 1") s.Assert().Contains(output, "Error: error inserting user opaque id for user 'john' with opaque id") - output, err = s.Exec("authelia-backend", []string{"authelia", s.testArg, s.coverageArg, "storage", "user", "identifiers", "add", "john", "--service=openid_connect", "--sector='openidconnect.com'", "--identifier=1097c8f8-83f2-4506-8138-5f40e83a1285", "--config=/config/configuration.storage.yml"}) + output, err = s.Exec("authelia-backend", []string{"authelia", s.testArg, s.coverageArg, "storage", "user", "identifiers", "add", "john", "--service=openid", "--sector='openidconnect.com'", "--identifier=1097c8f8-83f2-4506-8138-5f40e83a1285", "--config=/config/configuration.storage.yml"}) s.Assert().EqualError(err, "exit status 1") s.Assert().Contains(output, "Error: error inserting user opaque id for user 'john' with opaque id '1097c8f8-83f2-4506-8138-5f40e83a1285':") - output, err = s.Exec("authelia-backend", []string{"authelia", s.testArg, s.coverageArg, "storage", "user", "identifiers", "add", "john", "--service=openid_connect", "--sector='openidconnect.net'", "--identifier=b0e17f48-933c-4cba-8509-ee9bfadf8ce5", "--config=/config/configuration.storage.yml"}) + output, err = s.Exec("authelia-backend", []string{"authelia", s.testArg, s.coverageArg, "storage", "user", "identifiers", "add", "john", "--service=openid", "--sector='openidconnect.net'", "--identifier=b0e17f48-933c-4cba-8509-ee9bfadf8ce5", "--config=/config/configuration.storage.yml"}) s.Assert().NoError(err) - s.Assert().Contains(output, "Added User Opaque Identifier:\n\tService: openid_connect\n\tSector: openidconnect.net\n\tUsername: john\n\tIdentifier: b0e17f48-933c-4cba-8509-ee9bfadf8ce5\n\n") + s.Assert().Contains(output, "Added User Opaque Identifier:\n\tService: openid\n\tSector: openidconnect.net\n\tUsername: john\n\tIdentifier: b0e17f48-933c-4cba-8509-ee9bfadf8ce5\n\n") - output, err = s.Exec("authelia-backend", []string{"authelia", s.testArg, s.coverageArg, "storage", "user", "identifiers", "add", "john", "--service=openid_connect", "--sector='bad-uuid.com'", "--identifier=d49564dc-b7a1-11ec-8429-fcaa147128ea", "--config=/config/configuration.storage.yml"}) + output, err = s.Exec("authelia-backend", []string{"authelia", s.testArg, s.coverageArg, "storage", "user", "identifiers", "add", "john", "--service=openid", "--sector='bad-uuid.com'", "--identifier=d49564dc-b7a1-11ec-8429-fcaa147128ea", "--config=/config/configuration.storage.yml"}) s.Assert().EqualError(err, "exit status 1") s.Assert().Contains(output, "Error: the identifier providerd 'd49564dc-b7a1-11ec-8429-fcaa147128ea' is a version 1 UUID but only version 4 UUID's accepted as identifiers") - output, err = s.Exec("authelia-backend", []string{"authelia", s.testArg, s.coverageArg, "storage", "user", "identifiers", "add", "john", "--service=openid_connect", "--sector='bad-uuid.com'", "--identifier=asdmklasdm", "--config=/config/configuration.storage.yml"}) + output, err = s.Exec("authelia-backend", []string{"authelia", s.testArg, s.coverageArg, "storage", "user", "identifiers", "add", "john", "--service=openid", "--sector='bad-uuid.com'", "--identifier=asdmklasdm", "--config=/config/configuration.storage.yml"}) s.Assert().EqualError(err, "exit status 1") s.Assert().Contains(output, "Error: the identifier provided 'asdmklasdm' is invalid as it must be a version 4 UUID but parsing it had an error: invalid UUID length: 10") @@ -471,7 +471,7 @@ func (s *CLISuite) TestStorage04ShouldManageUniqueID() { s.Assert().Equal("1097c8f8-83f2-4506-8138-5f40e83a1285", export.Identifiers[0].Identifier.String()) s.Assert().Equal("john", export.Identifiers[0].Username) s.Assert().Equal("", export.Identifiers[0].SectorID) - s.Assert().Equal("openid_connect", export.Identifiers[0].Service) + s.Assert().Equal("openid", export.Identifiers[0].Service) output, err = s.Exec("authelia-backend", []string{"authelia", s.testArg, s.coverageArg, "storage", "user", "identifiers", "export", "--file=/tmp/out/2.yml", "--config=/config/configuration.storage.yml"}) s.Assert().NoError(err) @@ -490,13 +490,13 @@ func (s *CLISuite) TestStorage04ShouldManageUniqueID() { s.Assert().Equal("1097c8f8-83f2-4506-8138-5f40e83a1285", export.Identifiers[0].Identifier.String()) s.Assert().Equal("john", export.Identifiers[0].Username) s.Assert().Equal("", export.Identifiers[0].SectorID) - s.Assert().Equal("openid_connect", export.Identifiers[0].Service) + s.Assert().Equal("openid", export.Identifiers[0].Service) s.Assert().Equal(2, export.Identifiers[1].ID) s.Assert().Equal("b0e17f48-933c-4cba-8509-ee9bfadf8ce5", export.Identifiers[1].Identifier.String()) s.Assert().Equal("john", export.Identifiers[1].Username) s.Assert().Equal("openidconnect.net", export.Identifiers[1].SectorID) - s.Assert().Equal("openid_connect", export.Identifiers[1].Service) + s.Assert().Equal("openid", export.Identifiers[1].Service) } func (s *CLISuite) TestStorage05ShouldChangeEncryptionKey() {