summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames Elliott <james-d-elliott@users.noreply.github.com>2022-10-19 18:17:55 +1100
committerGitHub <noreply@github.com>2022-10-19 18:17:55 +1100
commit24e41aed845d5f06a26444bb154e22e1b41bba8d (patch)
tree6182ec17b43a182bfa194196c887438701b55013
parent52102eea8c7379e0d34d9025ea72bebdcf639673 (diff)
feat(commands): add webauthn device commands (#3671)
-rw-r--r--docs/content/en/reference/cli/authelia/authelia_storage_user.md1
-rw-r--r--docs/content/en/reference/cli/authelia/authelia_storage_user_webauthn.md65
-rw-r--r--docs/content/en/reference/cli/authelia/authelia_storage_user_webauthn_delete.md78
-rw-r--r--docs/content/en/reference/cli/authelia/authelia_storage_user_webauthn_list.md72
-rw-r--r--internal/commands/const.go37
-rw-r--r--internal/commands/storage.go53
-rw-r--r--internal/commands/storage_run.go181
-rw-r--r--internal/mocks/storage.go28
-rw-r--r--internal/storage/provider.go2
-rw-r--r--internal/storage/sql_provider.go36
-rw-r--r--internal/storage/sql_provider_backend_postgres.go3
-rw-r--r--internal/storage/sql_provider_queries.go26
12 files changed, 575 insertions, 7 deletions
diff --git a/docs/content/en/reference/cli/authelia/authelia_storage_user.md b/docs/content/en/reference/cli/authelia/authelia_storage_user.md
index 14561dddb..90fc3c917 100644
--- a/docs/content/en/reference/cli/authelia/authelia_storage_user.md
+++ b/docs/content/en/reference/cli/authelia/authelia_storage_user.md
@@ -62,4 +62,5 @@ authelia storage user --help
* [authelia storage](authelia_storage.md) - Manage the Authelia storage
* [authelia storage user identifiers](authelia_storage_user_identifiers.md) - Manage user opaque identifiers
* [authelia storage user totp](authelia_storage_user_totp.md) - Manage TOTP configurations
+* [authelia storage user webauthn](authelia_storage_user_webauthn.md) - Manage Webauthn devices
diff --git a/docs/content/en/reference/cli/authelia/authelia_storage_user_webauthn.md b/docs/content/en/reference/cli/authelia/authelia_storage_user_webauthn.md
new file mode 100644
index 000000000..65ee8c777
--- /dev/null
+++ b/docs/content/en/reference/cli/authelia/authelia_storage_user_webauthn.md
@@ -0,0 +1,65 @@
+---
+title: "authelia storage user webauthn"
+description: "Reference for the authelia storage user webauthn command."
+lead: ""
+date: 2022-06-15T17:51:47+10:00
+draft: false
+images: []
+menu:
+ reference:
+ parent: "cli-authelia"
+weight: 330
+toc: true
+---
+
+## authelia storage user webauthn
+
+Manage Webauthn devices
+
+### Synopsis
+
+Manage Webauthn devices.
+
+This subcommand allows interacting with Webauthn devices.
+
+### Examples
+
+```
+authelia storage user webauthn --help
+```
+
+### Options
+
+```
+ -h, --help help for webauthn
+```
+
+### Options inherited from parent commands
+
+```
+ -c, --config strings configuration files to load (default [configuration.yml])
+ --encryption-key string the storage encryption key to use
+ --mysql.database string the MySQL database name (default "authelia")
+ --mysql.host string the MySQL hostname
+ --mysql.password string the MySQL password
+ --mysql.port int the MySQL port (default 3306)
+ --mysql.username string the MySQL username (default "authelia")
+ --postgres.database string the PostgreSQL database name (default "authelia")
+ --postgres.host string the PostgreSQL hostname
+ --postgres.password string the PostgreSQL password
+ --postgres.port int the PostgreSQL port (default 5432)
+ --postgres.schema string the PostgreSQL schema name (default "public")
+ --postgres.ssl.certificate string the PostgreSQL ssl certificate file location
+ --postgres.ssl.key string the PostgreSQL ssl key file location
+ --postgres.ssl.mode string the PostgreSQL ssl mode (default "disable")
+ --postgres.ssl.root_certificate string the PostgreSQL ssl root certificate file location
+ --postgres.username string the PostgreSQL username (default "authelia")
+ --sqlite.path string the SQLite database path
+```
+
+### SEE ALSO
+
+* [authelia storage user](authelia_storage_user.md) - Manages user settings
+* [authelia storage user webauthn delete](authelia_storage_user_webauthn_delete.md) - Delete a WebAuthn device
+* [authelia storage user webauthn list](authelia_storage_user_webauthn_list.md) - List WebAuthn devices
+
diff --git a/docs/content/en/reference/cli/authelia/authelia_storage_user_webauthn_delete.md b/docs/content/en/reference/cli/authelia/authelia_storage_user_webauthn_delete.md
new file mode 100644
index 000000000..cf2545624
--- /dev/null
+++ b/docs/content/en/reference/cli/authelia/authelia_storage_user_webauthn_delete.md
@@ -0,0 +1,78 @@
+---
+title: "authelia storage user webauthn delete"
+description: "Reference for the authelia storage user webauthn delete command."
+lead: ""
+date: 2022-06-15T17:51:47+10:00
+draft: false
+images: []
+menu:
+ reference:
+ parent: "cli-authelia"
+weight: 330
+toc: true
+---
+
+## authelia storage user webauthn delete
+
+Delete a WebAuthn device
+
+### Synopsis
+
+Delete a WebAuthn device.
+
+This subcommand allows deleting a WebAuthn device directly from the database.
+
+```
+authelia storage user webauthn delete [username] [flags]
+```
+
+### Examples
+
+```
+authelia storage user webauthn delete john --all
+authelia storage user webauthn delete john --all --config config.yml
+authelia storage user webauthn delete john --all --encryption-key b3453fde-ecc2-4a1f-9422-2707ddbed495 --postgres.host postgres --postgres.password autheliapw
+authelia storage user webauthn delete john --description Primary
+authelia storage user webauthn delete john --description Primary --config config.yml
+authelia storage user webauthn delete john --description Primary --encryption-key b3453fde-ecc2-4a1f-9422-2707ddbed495 --postgres.host postgres --postgres.password autheliapw
+authelia storage user webauthn delete --kid abc123
+authelia storage user webauthn delete --kid abc123 --config config.yml
+authelia storage user webauthn delete --kid abc123 --encryption-key b3453fde-ecc2-4a1f-9422-2707ddbed495 --postgres.host postgres --postgres.password autheliapw
+```
+
+### Options
+
+```
+ --all delete all of the users webauthn devices
+ --description string delete a users webauthn device by description
+ -h, --help help for delete
+ --kid string delete a users webauthn device by key id
+```
+
+### Options inherited from parent commands
+
+```
+ -c, --config strings configuration files to load (default [configuration.yml])
+ --encryption-key string the storage encryption key to use
+ --mysql.database string the MySQL database name (default "authelia")
+ --mysql.host string the MySQL hostname
+ --mysql.password string the MySQL password
+ --mysql.port int the MySQL port (default 3306)
+ --mysql.username string the MySQL username (default "authelia")
+ --postgres.database string the PostgreSQL database name (default "authelia")
+ --postgres.host string the PostgreSQL hostname
+ --postgres.password string the PostgreSQL password
+ --postgres.port int the PostgreSQL port (default 5432)
+ --postgres.schema string the PostgreSQL schema name (default "public")
+ --postgres.ssl.certificate string the PostgreSQL ssl certificate file location
+ --postgres.ssl.key string the PostgreSQL ssl key file location
+ --postgres.ssl.mode string the PostgreSQL ssl mode (default "disable")
+ --postgres.ssl.root_certificate string the PostgreSQL ssl root certificate file location
+ --postgres.username string the PostgreSQL username (default "authelia")
+ --sqlite.path string the SQLite database path
+```
+
+### SEE ALSO
+
+* [authelia storage user webauthn](authelia_storage_user_webauthn.md) - Manage Webauthn devices
+
diff --git a/docs/content/en/reference/cli/authelia/authelia_storage_user_webauthn_list.md b/docs/content/en/reference/cli/authelia/authelia_storage_user_webauthn_list.md
new file mode 100644
index 000000000..185e8af15
--- /dev/null
+++ b/docs/content/en/reference/cli/authelia/authelia_storage_user_webauthn_list.md
@@ -0,0 +1,72 @@
+---
+title: "authelia storage user webauthn list"
+description: "Reference for the authelia storage user webauthn list command."
+lead: ""
+date: 2022-06-15T17:51:47+10:00
+draft: false
+images: []
+menu:
+ reference:
+ parent: "cli-authelia"
+weight: 330
+toc: true
+---
+
+## authelia storage user webauthn list
+
+List WebAuthn devices
+
+### Synopsis
+
+List WebAuthn devices.
+
+This subcommand allows listing WebAuthn devices.
+
+```
+authelia storage user webauthn list [username] [flags]
+```
+
+### Examples
+
+```
+authelia storage user webauthn list
+authelia storage user webauthn list john
+authelia storage user webauthn list --config config.yml
+authelia storage user webauthn list john --config config.yml
+authelia storage user webauthn list --encryption-key b3453fde-ecc2-4a1f-9422-2707ddbed495 --postgres.host postgres --postgres.password autheliapw
+authelia storage user webauthn list john --encryption-key b3453fde-ecc2-4a1f-9422-2707ddbed495 --postgres.host postgres --postgres.password autheliapw
+```
+
+### Options
+
+```
+ -h, --help help for list
+```
+
+### Options inherited from parent commands
+
+```
+ -c, --config strings configuration files to load (default [configuration.yml])
+ --encryption-key string the storage encryption key to use
+ --mysql.database string the MySQL database name (default "authelia")
+ --mysql.host string the MySQL hostname
+ --mysql.password string the MySQL password
+ --mysql.port int the MySQL port (default 3306)
+ --mysql.username string the MySQL username (default "authelia")
+ --postgres.database string the PostgreSQL database name (default "authelia")
+ --postgres.host string the PostgreSQL hostname
+ --postgres.password string the PostgreSQL password
+ --postgres.port int the PostgreSQL port (default 5432)
+ --postgres.schema string the PostgreSQL schema name (default "public")
+ --postgres.ssl.certificate string the PostgreSQL ssl certificate file location
+ --postgres.ssl.key string the PostgreSQL ssl key file location
+ --postgres.ssl.mode string the PostgreSQL ssl mode (default "disable")
+ --postgres.ssl.root_certificate string the PostgreSQL ssl root certificate file location
+ --postgres.username string the PostgreSQL username (default "authelia")
+ --sqlite.path string the SQLite database path
+```
+
+### SEE ALSO
+
+* [authelia storage user webauthn](authelia_storage_user_webauthn.md) - Manage Webauthn devices
+
diff --git a/internal/commands/const.go b/internal/commands/const.go
index b4fb7986b..13bb8aa24 100644
--- a/internal/commands/const.go
+++ b/internal/commands/const.go
@@ -176,6 +176,43 @@ This subcommand allows manually adding an opaque identifier for a user to the da
authelia storage user identifiers add john --identifier f0919359-9d15-4e15-bcba-83b41620a073 --config config.yml
authelia storage user identifiers add john --identifier f0919359-9d15-4e15-bcba-83b41620a073 --encryption-key b3453fde-ecc2-4a1f-9422-2707ddbed495 --postgres.host postgres --postgres.password autheliapw`
+ cmdAutheliaStorageUserWebAuthnShort = "Manage Webauthn devices"
+
+ cmdAutheliaStorageUserWebAuthnLong = `Manage Webauthn devices.
+
+This subcommand allows interacting with Webauthn devices.`
+
+ cmdAutheliaStorageUserWebAuthnExample = `authelia storage user webauthn --help`
+
+ cmdAutheliaStorageUserWebAuthnListShort = "List WebAuthn devices"
+
+ cmdAutheliaStorageUserWebAuthnListLong = `List WebAuthn devices.
+
+This subcommand allows listing WebAuthn devices.`
+
+ cmdAutheliaStorageUserWebAuthnListExample = `authelia storage user webauthn list
+authelia storage user webauthn list john
+authelia storage user webauthn list --config config.yml
+authelia storage user webauthn list john --config config.yml
+authelia storage user webauthn list --encryption-key b3453fde-ecc2-4a1f-9422-2707ddbed495 --postgres.host postgres --postgres.password autheliapw
+authelia storage user webauthn list john --encryption-key b3453fde-ecc2-4a1f-9422-2707ddbed495 --postgres.host postgres --postgres.password autheliapw`
+
+ cmdAutheliaStorageUserWebAuthnDeleteShort = "Delete a WebAuthn device"
+
+ cmdAutheliaStorageUserWebAuthnDeleteLong = `Delete a WebAuthn device.
+
+This subcommand allows deleting a WebAuthn device directly from the database.`
+
+ cmdAutheliaStorageUserWebAuthnDeleteExample = `authelia storage user webauthn delete john --all
+authelia storage user webauthn delete john --all --config config.yml
+authelia storage user webauthn delete john --all --encryption-key b3453fde-ecc2-4a1f-9422-2707ddbed495 --postgres.host postgres --postgres.password autheliapw
+authelia storage user webauthn delete john --description Primary
+authelia storage user webauthn delete john --description Primary --config config.yml
+authelia storage user webauthn delete john --description Primary --encryption-key b3453fde-ecc2-4a1f-9422-2707ddbed495 --postgres.host postgres --postgres.password autheliapw
+authelia storage user webauthn delete --kid abc123
+authelia storage user webauthn delete --kid abc123 --config config.yml
+authelia storage user webauthn delete --kid abc123 --encryption-key b3453fde-ecc2-4a1f-9422-2707ddbed495 --postgres.host postgres --postgres.password autheliapw`
+
cmdAutheliaStorageUserTOTPShort = "Manage TOTP configurations"
cmdAutheliaStorageUserTOTPLong = `Manage TOTP configurations.
diff --git a/internal/commands/storage.go b/internal/commands/storage.go
index adfd1c28c..92190149a 100644
--- a/internal/commands/storage.go
+++ b/internal/commands/storage.go
@@ -117,6 +117,7 @@ func newStorageUserCmd() (cmd *cobra.Command) {
cmd.AddCommand(
newStorageUserIdentifiersCmd(),
newStorageUserTOTPCmd(),
+ newStorageUserWebAuthnCmd(),
)
return cmd
@@ -211,6 +212,58 @@ func newStorageUserIdentifiersAddCmd() (cmd *cobra.Command) {
return cmd
}
+func newStorageUserWebAuthnCmd() (cmd *cobra.Command) {
+ cmd = &cobra.Command{
+ Use: "webauthn",
+ Short: cmdAutheliaStorageUserWebAuthnShort,
+ Long: cmdAutheliaStorageUserWebAuthnLong,
+ Example: cmdAutheliaStorageUserWebAuthnExample,
+
+ DisableAutoGenTag: true,
+ }
+
+ cmd.AddCommand(
+ newStorageUserWebAuthnListCmd(),
+ newStorageUserWebAuthnDeleteCmd(),
+ )
+
+ return cmd
+}
+
+func newStorageUserWebAuthnListCmd() (cmd *cobra.Command) {
+ cmd = &cobra.Command{
+ Use: "list [username]",
+ Short: cmdAutheliaStorageUserWebAuthnListShort,
+ Long: cmdAutheliaStorageUserWebAuthnListLong,
+ Example: cmdAutheliaStorageUserWebAuthnListExample,
+ RunE: storageWebAuthnListRunE,
+ Args: cobra.MaximumNArgs(1),
+
+ DisableAutoGenTag: true,
+ }
+
+ return cmd
+}
+
+func newStorageUserWebAuthnDeleteCmd() (cmd *cobra.Command) {
+ cmd = &cobra.Command{
+ Use: "delete [username]",
+ Short: cmdAutheliaStorageUserWebAuthnDeleteShort,
+ Long: cmdAutheliaStorageUserWebAuthnDeleteLong,
+ Example: cmdAutheliaStorageUserWebAuthnDeleteExample,
+ RunE: storageWebAuthnDeleteRunE,
+ Args: cobra.MaximumNArgs(1),
+
+ DisableAutoGenTag: true,
+ }
+
+ cmd.Flags().Bool("all", false, "delete all of the users webauthn devices")
+ cmd.Flags().String("description", "", "delete a users webauthn device by description")
+ cmd.Flags().String("kid", "", "delete a users webauthn device by key id")
+
+ return cmd
+}
+
func newStorageUserTOTPCmd() (cmd *cobra.Command) {
cmd = &cobra.Command{
Use: "totp",
diff --git a/internal/commands/storage_run.go b/internal/commands/storage_run.go
index 50b40a876..4b11c5e72 100644
--- a/internal/commands/storage_run.go
+++ b/internal/commands/storage_run.go
@@ -205,6 +205,187 @@ func storageSchemaEncryptionChangeKeyRunE(cmd *cobra.Command, args []string) (er
return nil
}
+func storageWebAuthnListRunE(cmd *cobra.Command, args []string) (err error) {
+ if len(args) == 0 || args[0] == "" {
+ return storageWebAuthnListAllRunE(cmd, args)
+ }
+
+ var (
+ provider storage.Provider
+ ctx = context.Background()
+ )
+
+ provider = getStorageProvider()
+
+ defer func() {
+ _ = provider.Close()
+ }()
+
+ var devices []model.WebauthnDevice
+
+ user := args[0]
+
+ devices, err = provider.LoadWebauthnDevicesByUsername(ctx, user)
+
+ switch {
+ case len(devices) == 0 || (err != nil && errors.Is(err, storage.ErrNoWebauthnDevice)):
+ return fmt.Errorf("user '%s' has no webauthn devices", user)
+ case err != nil:
+ return fmt.Errorf("can't list devices for user '%s': %w", user, err)
+ default:
+ fmt.Printf("Webauthn Devices for user '%s':\n\n", user)
+ fmt.Printf("ID\tKID\tDescription\n")
+
+ for _, device := range devices {
+ fmt.Printf("%d\t%s\t%s", device.ID, device.KID, device.Description)
+ }
+ }
+
+ return nil
+}
+
+func storageWebAuthnListAllRunE(_ *cobra.Command, _ []string) (err error) {
+ var (
+ provider storage.Provider
+ ctx = context.Background()
+ )
+
+ provider = getStorageProvider()
+
+ defer func() {
+ _ = provider.Close()
+ }()
+
+ var devices []model.WebauthnDevice
+
+ limit := 10
+
+ output := strings.Builder{}
+
+ for page := 0; true; page++ {
+ if devices, err = provider.LoadWebauthnDevices(ctx, limit, page); err != nil {
+ return fmt.Errorf("failed to list devices: %w", err)
+ }
+
+ if page == 0 && len(devices) == 0 {
+ return errors.New("no webauthn devices in database")
+ }
+
+ for _, device := range devices {
+ output.WriteString(fmt.Sprintf("%d\t%s\t%s\t%s\n", device.ID, device.KID, device.Description, device.Username))
+ }
+
+ if len(devices) < limit {
+ break
+ }
+ }
+
+ fmt.Printf("Webauthn Devices:\n\nID\tKID\tDescription\tUsername\n")
+ fmt.Println(output.String())
+
+ return nil
+}
+
+func storageWebAuthnDeleteRunE(cmd *cobra.Command, args []string) (err error) {
+ var (
+ provider storage.Provider
+ ctx = context.Background()
+ )
+
+ provider = getStorageProvider()
+
+ defer func() {
+ _ = provider.Close()
+ }()
+
+ var (
+ all, byKID bool
+ description, kid, user string
+ )
+
+ if all, byKID, description, kid, user, err = storageWebAuthnDeleteGetAndValidateConfig(cmd, args); err != nil {
+ return err
+ }
+
+ if byKID {
+ if err = provider.DeleteWebauthnDevice(ctx, kid); err != nil {
+ return fmt.Errorf("failed to delete WebAuthn device with kid '%s': %w", kid, err)
+ }
+
+ fmt.Printf("Deleted WebAuthn device with kid '%s'", kid)
+ } else {
+ err = provider.DeleteWebauthnDeviceByUsername(ctx, user, description)
+
+ if all {
+ if err != nil {
+ return fmt.Errorf("failed to delete all WebAuthn devices with username '%s': %w", user, err)
+ }
+
+ fmt.Printf("Deleted all WebAuthn devices for user '%s'", user)
+ } else {
+ if err != nil {
+ return fmt.Errorf("failed to delete WebAuthn device with username '%s' and description '%s': %w", user, description, err)
+ }
+
+ fmt.Printf("Deleted WebAuthn device with username '%s' and description '%s'", user, description)
+ }
+ }
+
+ return nil
+}
+
+func storageWebAuthnDeleteGetAndValidateConfig(cmd *cobra.Command, args []string) (all, byKID bool, description, kid, user string, err error) {
+ if len(args) != 0 {
+ user = args[0]
+ }
+
+ flags := 0
+
+ if cmd.Flags().Changed("all") {
+ if all, err = cmd.Flags().GetBool("all"); err != nil {
+ return
+ }
+
+ flags++
+ }
+
+ if cmd.Flags().Changed("description") {
+ if description, err = cmd.Flags().GetString("description"); err != nil {
+ return
+ }
+
+ flags++
+ }
+
+ if byKID = cmd.Flags().Changed("kid"); byKID {
+ if kid, err = cmd.Flags().GetString("kid"); err != nil {
+ return
+ }
+
+ flags++
+ }
+
+ if flags > 1 {
+ err = fmt.Errorf("must only supply one of the flags --all, --description, and --kid but %d were specified", flags)
+
+ return
+ }
+
+ if flags == 0 {
+ err = fmt.Errorf("must supply one of the flags --all, --description, or --kid")
+
+ return
+ }
+
+ if !byKID && len(user) == 0 {
+ err = fmt.Errorf("must supply the username or the --kid flag")
+
+ return
+ }
+
+ return
+}
+
func storageTOTPGenerateRunE(cmd *cobra.Command, args []string) (err error) {
var (
provider storage.Provider
diff --git a/internal/mocks/storage.go b/internal/mocks/storage.go
index 2f6160d76..980469a35 100644
--- a/internal/mocks/storage.go
+++ b/internal/mocks/storage.go
@@ -166,6 +166,34 @@ func (mr *MockStorageMockRecorder) DeleteTOTPConfiguration(arg0, arg1 interface{
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteTOTPConfiguration", reflect.TypeOf((*MockStorage)(nil).DeleteTOTPConfiguration), arg0, arg1)
}
+// DeleteWebauthnDevice mocks base method.
+func (m *MockStorage) DeleteWebauthnDevice(arg0 context.Context, arg1 string) error {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "DeleteWebauthnDevice", arg0, arg1)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// DeleteWebauthnDevice indicates an expected call of DeleteWebauthnDevice.
+func (mr *MockStorageMockRecorder) DeleteWebauthnDevice(arg0, arg1 interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteWebauthnDevice", reflect.TypeOf((*MockStorage)(nil).DeleteWebauthnDevice), arg0, arg1)
+}
+
+// DeleteWebauthnDeviceByUsername mocks base method.
+func (m *MockStorage) DeleteWebauthnDeviceByUsername(arg0 context.Context, arg1, arg2 string) error {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "DeleteWebauthnDeviceByUsername", arg0, arg1, arg2)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// DeleteWebauthnDeviceByUsername indicates an expected call of DeleteWebauthnDeviceByUsername.
+func (mr *MockStorageMockRecorder) DeleteWebauthnDeviceByUsername(arg0, arg1, arg2 interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteWebauthnDeviceByUsername", reflect.TypeOf((*MockStorage)(nil).DeleteWebauthnDeviceByUsername), arg0, arg1, arg2)
+}
+
// FindIdentityVerification mocks base method.
func (m *MockStorage) FindIdentityVerification(arg0 context.Context, arg1 string) (bool, error) {
m.ctrl.T.Helper()
diff --git a/internal/storage/provider.go b/internal/storage/provider.go
index 7696966a5..fdf415a89 100644
--- a/internal/storage/provider.go
+++ b/internal/storage/provider.go
@@ -39,6 +39,8 @@ type Provider interface {
SaveWebauthnDevice(ctx context.Context, device model.WebauthnDevice) (err error)
UpdateWebauthnDeviceSignIn(ctx context.Context, id int, rpid string, lastUsedAt *time.Time, signCount uint32, cloneWarning bool) (err error)
+ DeleteWebauthnDevice(ctx context.Context, kid string) (err error)
+ DeleteWebauthnDeviceByUsername(ctx context.Context, username, description string) (err error)
LoadWebauthnDevices(ctx context.Context, limit, page int) (devices []model.WebauthnDevice, err error)
LoadWebauthnDevicesByUsername(ctx context.Context, username string) (devices []model.WebauthnDevice, err error)
diff --git a/internal/storage/sql_provider.go b/internal/storage/sql_provider.go
index bb5ac9895..6991e7021 100644
--- a/internal/storage/sql_provider.go
+++ b/internal/storage/sql_provider.go
@@ -56,6 +56,10 @@ func NewSQLProvider(config *schema.Configuration, name, driverName, dataSourceNa
sqlUpdateWebauthnDeviceRecordSignIn: fmt.Sprintf(queryFmtUpdateWebauthnDeviceRecordSignIn, tableWebauthnDevices),
sqlUpdateWebauthnDeviceRecordSignInByUsername: fmt.Sprintf(queryFmtUpdateWebauthnDeviceRecordSignInByUsername, tableWebauthnDevices),
+ sqlDeleteWebauthnDevice: fmt.Sprintf(queryFmtDeleteWebauthnDevice, tableWebauthnDevices),
+ sqlDeleteWebauthnDeviceByUsername: fmt.Sprintf(queryFmtDeleteWebauthnDeviceByUsername, tableWebauthnDevices),
+ sqlDeleteWebauthnDeviceByUsernameAndDescription: fmt.Sprintf(queryFmtDeleteWebauthnDeviceByUsernameAndDescription, tableWebauthnDevices),
+
sqlUpsertDuoDevice: fmt.Sprintf(queryFmtUpsertDuoDevice, tableDuoDevices),
sqlDeleteDuoDevice: fmt.Sprintf(queryFmtDeleteDuoDevice, tableDuoDevices),
sqlSelectDuoDevice: fmt.Sprintf(queryFmtSelectDuoDevice, tableDuoDevices),
@@ -169,6 +173,10 @@ type SQLProvider struct {
sqlUpdateWebauthnDeviceRecordSignIn string
sqlUpdateWebauthnDeviceRecordSignInByUsername string
+ sqlDeleteWebauthnDevice string
+ sqlDeleteWebauthnDeviceByUsername string
+ sqlDeleteWebauthnDeviceByUsernameAndDescription string
+
// Table: duo_devices.
sqlUpsertDuoDevice string
sqlDeleteDuoDevice string
@@ -841,6 +849,34 @@ func (p *SQLProvider) UpdateWebauthnDeviceSignIn(ctx context.Context, id int, rp
return nil
}
+// DeleteWebauthnDevice deletes a registered Webauthn device.
+func (p *SQLProvider) DeleteWebauthnDevice(ctx context.Context, kid string) (err error) {
+ if _, err = p.db.ExecContext(ctx, p.sqlDeleteWebauthnDevice, kid); err != nil {
+ return fmt.Errorf("error deleting webauthn device with kid '%s': %w", kid, err)
+ }
+
+ return nil
+}
+
+// DeleteWebauthnDeviceByUsername deletes registered Webauthn devices by username or username and description.
+func (p *SQLProvider) DeleteWebauthnDeviceByUsername(ctx context.Context, username, description string) (err error) {
+ if len(username) == 0 {
+ return fmt.Errorf("error deleting webauthn device with username '%s' and description '%s': username must not be empty", username, description)
+ }
+
+ if len(description) == 0 {
+ if _, err = p.db.ExecContext(ctx, p.sqlDeleteWebauthnDeviceByUsername, username); err != nil {
+ return fmt.Errorf("error deleting webauthn devices for username '%s': %w", username, err)
+ }
+ } else {
+ if _, err = p.db.ExecContext(ctx, p.sqlDeleteWebauthnDeviceByUsernameAndDescription, username, description); err != nil {
+ return fmt.Errorf("error deleting webauthn device with username '%s' and description '%s': %w", username, description, err)
+ }
+ }
+
+ return nil
+}
+
// LoadWebauthnDevices loads Webauthn device registrations.
func (p *SQLProvider) LoadWebauthnDevices(ctx context.Context, limit, page int) (devices []model.WebauthnDevice, err error) {
devices = make([]model.WebauthnDevice, 0, limit)
diff --git a/internal/storage/sql_provider_backend_postgres.go b/internal/storage/sql_provider_backend_postgres.go
index 3b98e64e4..06fc40c78 100644
--- a/internal/storage/sql_provider_backend_postgres.go
+++ b/internal/storage/sql_provider_backend_postgres.go
@@ -61,6 +61,9 @@ func NewPostgreSQLProvider(config *schema.Configuration) (provider *PostgreSQLPr
provider.sqlUpdateWebauthnDevicePublicKeyByUsername = provider.db.Rebind(provider.sqlUpdateWebauthnDevicePublicKeyByUsername)
provider.sqlUpdateWebauthnDeviceRecordSignIn = provider.db.Rebind(provider.sqlUpdateWebauthnDeviceRecordSignIn)
provider.sqlUpdateWebauthnDeviceRecordSignInByUsername = provider.db.Rebind(provider.sqlUpdateWebauthnDeviceRecordSignInByUsername)
+ provider.sqlDeleteWebauthnDevice = provider.db.Rebind(provider.sqlDeleteWebauthnDevice)
+ provider.sqlDeleteWebauthnDeviceByUsername = provider.db.Rebind(provider.sqlDeleteWebauthnDeviceByUsername)
+ provider.sqlDeleteWebauthnDeviceByUsernameAndDescription = provider.db.Rebind(provider.sqlDeleteWebauthnDeviceByUsernameAndDescription)
provider.sqlSelectDuoDevice = provider.db.Rebind(provider.sqlSelectDuoDevice)
provider.sqlDeleteDuoDevice = provider.db.Rebind(provider.sqlDeleteDuoDevice)
diff --git a/internal/storage/sql_provider_queries.go b/internal/storage/sql_provider_queries.go
index 7dc44e156..eedfca30a 100644
--- a/internal/storage/sql_provider_queries.go
+++ b/internal/storage/sql_provider_queries.go
@@ -122,13 +122,13 @@ const (
const (
queryFmtSelectWebauthnDevices = `
- SELECT id, created_at, last_used_at, rpid, username, description, kid, public_key, attestation_type, transport, aaguid, sign_count, clone_warning
+ SELECT id, created_at, last_used_at, rpid, username, description, kid, public_key, attestation_type, transport, aaguid, sign_count, clone_warning
FROM %s
LIMIT ?
OFFSET ?;`
queryFmtSelectWebauthnDevicesByUsername = `
- SELECT id, created_at, last_used_at, rpid, username, description, kid, public_key, attestation_type, transport, aaguid, sign_count, clone_warning
+ SELECT id, created_at, last_used_at, rpid, username, description, kid, public_key, attestation_type, transport, aaguid, sign_count, clone_warning
FROM %s
WHERE username = ?;`
@@ -144,14 +144,14 @@ const (
queryFmtUpdateWebauthnDeviceRecordSignIn = `
UPDATE %s
- SET
+ SET
rpid = ?, last_used_at = ?, sign_count = ?,
clone_warning = CASE clone_warning WHEN TRUE THEN TRUE ELSE ? END
WHERE id = ?;`
queryFmtUpdateWebauthnDeviceRecordSignInByUsername = `
UPDATE %s
- SET
+ SET
rpid = ?, last_used_at = ?, sign_count = ?,
clone_warning = CASE clone_warning WHEN TRUE THEN TRUE ELSE ? END
WHERE username = ? AND kid = ?;`
@@ -165,6 +165,18 @@ const (
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
ON CONFLICT (username, description)
DO UPDATE SET created_at = $1, last_used_at = $2, rpid = $3, kid = $6, public_key = $7, attestation_type = $8, transport = $9, aaguid = $10, sign_count = $11, clone_warning = $12;`
+
+ queryFmtDeleteWebauthnDevice = `
+ DELETE FROM %s
+ WHERE kid = ?;`
+
+ queryFmtDeleteWebauthnDeviceByUsername = `
+ DELETE FROM %s
+ WHERE username = ?;`
+
+ queryFmtDeleteWebauthnDeviceByUsernameAndDescription = `
+ DELETE FROM %s
+ WHERE username = ? AND description = ?;`
)
const (
@@ -232,7 +244,7 @@ const (
SELECT id, challenge_id, client_id, subject, authorized, granted, requested_at, responded_at, expires_at,
form_data, requested_scopes, granted_scopes, requested_audience, granted_audience
FROM %s
- WHERE client_id = ? AND subject = ? AND
+ WHERE client_id = ? AND subject = ? AND
authorized = TRUE AND granted = TRUE AND expires_at IS NOT NULL AND expires_at >= CURRENT_TIMESTAMP;`
queryFmtInsertOAuth2ConsentSession = `
@@ -263,8 +275,8 @@ const (
WHERE signature = ? AND revoked = FALSE;`
queryFmtInsertOAuth2Session = `
- INSERT INTO %s (challenge_id, request_id, client_id, signature, subject, requested_at,
- requested_scopes, granted_scopes, requested_audience, granted_audience,
+ INSERT INTO %s (challenge_id, request_id, client_id, signature, subject, requested_at,
+ requested_scopes, granted_scopes, requested_audience, granted_audience,
active, revoked, form_data, session_data)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);`