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.
pull/3242/head
James Elliott 2022-04-25 18:49:18 +10:00 committed by GitHub
parent 861bcc898f
commit 06ba312c28
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 123 additions and 17 deletions

View File

@ -113,5 +113,9 @@ var (
) )
const ( const (
identifierServiceOpenIDConnect = "openid_connect" identifierServiceOpenIDConnect = "openid"
)
var (
validIdentifierServices = []string{identifierServiceOpenIDConnect}
) )

View File

@ -71,6 +71,7 @@ func newStorageUserIdentifiersCmd() (cmd *cobra.Command) {
cmd.AddCommand( cmd.AddCommand(
newStorageUserIdentifiersExportCmd(), newStorageUserIdentifiersExportCmd(),
newStorageUserIdentifiersImportCmd(), newStorageUserIdentifiersImportCmd(),
newStorageUserIdentifiersGenerateCmd(),
newStorageUserIdentifiersAddCmd(), newStorageUserIdentifiersAddCmd(),
) )
@ -101,6 +102,20 @@ func newStorageUserIdentifiersImportCmd() (cmd *cobra.Command) {
return cmd 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) { func newStorageUserIdentifiersAddCmd() (cmd *cobra.Command) {
cmd = &cobra.Command{ cmd = &cobra.Command{
Use: "add [username]", 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("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)") cmd.Flags().String("sector", "", "The sector identifier to use (should usually be blank)")
return cmd return cmd

View File

@ -2,6 +2,7 @@ package commands
import ( import (
"context" "context"
"database/sql"
"encoding/base32" "encoding/base32"
"errors" "errors"
"fmt" "fmt"
@ -759,6 +760,92 @@ func storageUserIdentifiersImport(cmd *cobra.Command, _ []string) (err error) {
return nil 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) { func storageUserIdentifiersAdd(cmd *cobra.Command, args []string) (err error) {
var ( var (
provider storage.Provider provider storage.Provider
@ -774,8 +861,8 @@ func storageUserIdentifiersAdd(cmd *cobra.Command, args []string) (err error) {
if service == "" { if service == "" {
service = identifierServiceOpenIDConnect service = identifierServiceOpenIDConnect
} else if service != identifierServiceOpenIDConnect { } else if !utils.IsStringInSlice(service, validIdentifierServices) {
return fmt.Errorf("the service name '%s' is invalid, the valid values are: 'openid_connect'", service) 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 { if sector, err = cmd.Flags().GetString("sector"); err != nil {

View File

@ -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"}) 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().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().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"}) 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") s.Assert().EqualError(err, "exit status 1")
@ -434,27 +434,27 @@ func (s *CLISuite) TestStorage04ShouldManageUniqueID() {
s.Assert().EqualError(err, "exit status 1") 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") 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().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':") 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().EqualError(err, "exit status 1")
s.Assert().Contains(output, "Error: error inserting user opaque id for user 'john' with opaque id") 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().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':") 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().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().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") 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().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") 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("1097c8f8-83f2-4506-8138-5f40e83a1285", export.Identifiers[0].Identifier.String())
s.Assert().Equal("john", export.Identifiers[0].Username) s.Assert().Equal("john", export.Identifiers[0].Username)
s.Assert().Equal("", export.Identifiers[0].SectorID) 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"}) 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) 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("1097c8f8-83f2-4506-8138-5f40e83a1285", export.Identifiers[0].Identifier.String())
s.Assert().Equal("john", export.Identifiers[0].Username) s.Assert().Equal("john", export.Identifiers[0].Username)
s.Assert().Equal("", export.Identifiers[0].SectorID) 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(2, export.Identifiers[1].ID)
s.Assert().Equal("b0e17f48-933c-4cba-8509-ee9bfadf8ce5", export.Identifiers[1].Identifier.String()) s.Assert().Equal("b0e17f48-933c-4cba-8509-ee9bfadf8ce5", export.Identifiers[1].Identifier.String())
s.Assert().Equal("john", export.Identifiers[1].Username) s.Assert().Equal("john", export.Identifiers[1].Username)
s.Assert().Equal("openidconnect.net", export.Identifiers[1].SectorID) 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() { func (s *CLISuite) TestStorage05ShouldChangeEncryptionKey() {