diff options
| author | 2025-08-13 12:24:40 +0200 | |
|---|---|---|
| committer | 2025-08-13 12:24:40 +0200 | |
| commit | 7f8cb204cd5a58eb143ab20a21bfa32bd8c3c26b (patch) | |
| tree | e8a10032b73bd1c60df5c8e8fdf7817410d67e1a /cmd | |
| parent | [chore] bump to code.superseriousbusiness.org/oauth2/v4@ssb-v4.5.3-2 (#4367) (diff) | |
| download | gotosocial-7f8cb204cd5a58eb143ab20a21bfa32bd8c3c26b.tar.xz | |
[feature] 2fa management via CLI (#4368)
Adds 2FA management to the admin CLI. Also does some CLI refactoring so the functions we pass around are exported functions instead of changeable global variables.
closes https://codeberg.org/superseriousbusiness/gotosocial/issues/4320
Reviewed-on: https://codeberg.org/superseriousbusiness/gotosocial/pulls/4368
Co-authored-by: kim <grufwub@gmail.com>
Co-committed-by: kim <grufwub@gmail.com>
Diffstat (limited to 'cmd')
| -rw-r--r-- | cmd/gotosocial/action/admin/account/account.go | 67 | ||||
| -rw-r--r-- | cmd/gotosocial/action/admin/media/list.go | 8 | ||||
| -rw-r--r-- | cmd/gotosocial/action/admin/media/prune/all.go | 5 | ||||
| -rw-r--r-- | cmd/gotosocial/action/admin/media/prune/orphaned.go | 5 | ||||
| -rw-r--r-- | cmd/gotosocial/action/admin/media/prune/remote.go | 5 | ||||
| -rw-r--r-- | cmd/gotosocial/action/admin/trans/export.go | 5 | ||||
| -rw-r--r-- | cmd/gotosocial/action/admin/trans/import.go | 5 | ||||
| -rw-r--r-- | cmd/gotosocial/action/debug/config/config.go | 5 | ||||
| -rw-r--r-- | cmd/gotosocial/action/migration/run.go | 5 | ||||
| -rw-r--r-- | cmd/gotosocial/action/server/server.go | 8 | ||||
| -rw-r--r-- | cmd/gotosocial/action/testrig/no_testrig.go | 11 | ||||
| -rw-r--r-- | cmd/gotosocial/action/testrig/testrig.go | 5 | ||||
| -rw-r--r-- | cmd/gotosocial/admin.go | 13 | ||||
| -rw-r--r-- | cmd/gotosocial/testrig.go | 3 |
14 files changed, 127 insertions, 23 deletions
diff --git a/cmd/gotosocial/action/admin/account/account.go b/cmd/gotosocial/action/admin/account/account.go index 2c12f90bb..16b8bb807 100644 --- a/cmd/gotosocial/action/admin/account/account.go +++ b/cmd/gotosocial/action/admin/account/account.go @@ -29,12 +29,25 @@ import ( "code.superseriousbusiness.org/gotosocial/internal/db/bundb" "code.superseriousbusiness.org/gotosocial/internal/gtsmodel" "code.superseriousbusiness.org/gotosocial/internal/log" + userprocessor "code.superseriousbusiness.org/gotosocial/internal/processing/user" "code.superseriousbusiness.org/gotosocial/internal/state" "code.superseriousbusiness.org/gotosocial/internal/util" "code.superseriousbusiness.org/gotosocial/internal/validate" "golang.org/x/crypto/bcrypt" ) +var ( + // check function conformance + _ action.GTSAction = Create + _ action.GTSAction = List + _ action.GTSAction = Confirm + _ action.GTSAction = Promote + _ action.GTSAction = Demote + _ action.GTSAction = Enable + _ action.GTSAction = Disable + _ action.GTSAction = Password +) + func initState(ctx context.Context) (*state.State, error) { var state state.State state.Caches.Init() @@ -61,7 +74,7 @@ func stopState(state *state.State) error { // Create creates a new account and user // in the database using the provided flags. -var Create action.GTSAction = func(ctx context.Context) error { +func Create(ctx context.Context) error { state, err := initState(ctx) if err != nil { return err @@ -118,7 +131,7 @@ var Create action.GTSAction = func(ctx context.Context) error { } // List returns all existing local accounts. -var List action.GTSAction = func(ctx context.Context) error { +func List(ctx context.Context) error { state, err := initState(ctx) if err != nil { return err @@ -156,7 +169,7 @@ var List action.GTSAction = func(ctx context.Context) error { // Confirm sets a user to Approved, sets Email to the current // UnconfirmedEmail value, and sets ConfirmedAt to now. -var Confirm action.GTSAction = func(ctx context.Context) error { +func Confirm(ctx context.Context) error { state, err := initState(ctx) if err != nil { return err @@ -198,7 +211,7 @@ var Confirm action.GTSAction = func(ctx context.Context) error { } // Promote sets admin + moderator flags on a user to true. -var Promote action.GTSAction = func(ctx context.Context) error { +func Promote(ctx context.Context) error { state, err := initState(ctx) if err != nil { return err @@ -235,7 +248,7 @@ var Promote action.GTSAction = func(ctx context.Context) error { } // Demote sets admin + moderator flags on a user to false. -var Demote action.GTSAction = func(ctx context.Context) error { +func Demote(ctx context.Context) error { state, err := initState(ctx) if err != nil { return err @@ -272,7 +285,7 @@ var Demote action.GTSAction = func(ctx context.Context) error { } // Disable sets Disabled to true on a user. -var Disable action.GTSAction = func(ctx context.Context) error { +func Disable(ctx context.Context) error { state, err := initState(ctx) if err != nil { return err @@ -308,7 +321,7 @@ var Disable action.GTSAction = func(ctx context.Context) error { } // Enable sets Disabled to false on a user. -var Enable action.GTSAction = func(ctx context.Context) error { +func Enable(ctx context.Context) error { state, err := initState(ctx) if err != nil { return err @@ -344,7 +357,7 @@ var Enable action.GTSAction = func(ctx context.Context) error { } // Password sets the password of target account. -var Password action.GTSAction = func(ctx context.Context) error { +func Password(ctx context.Context) error { state, err := initState(ctx) if err != nil { return err @@ -389,3 +402,41 @@ var Password action.GTSAction = func(ctx context.Context) error { "encrypted_password", ) } + +// Disable2FA disables 2FA for target account. +func Disable2FA(ctx context.Context) error { + state, err := initState(ctx) + if err != nil { + return err + } + + defer func() { + // Ensure state gets stopped on return. + if err := stopState(state); err != nil { + log.Error(ctx, err) + } + }() + + username := config.GetAdminAccountUsername() + if err := validate.Username(username); err != nil { + return err + } + + account, err := state.DB.GetAccountByUsernameDomain(ctx, username, "") + if err != nil { + return err + } + + user, err := state.DB.GetUserByAccountID(ctx, account.ID) + if err != nil { + return err + } + + err = userprocessor.TwoFactorDisable(ctx, state, user) + if err != nil { + return err + } + + fmt.Printf("2fa disabled\n") + return nil +} diff --git a/cmd/gotosocial/action/admin/media/list.go b/cmd/gotosocial/action/admin/media/list.go index a07bf4145..8b8df204b 100644 --- a/cmd/gotosocial/action/admin/media/list.go +++ b/cmd/gotosocial/action/admin/media/list.go @@ -35,6 +35,10 @@ import ( "code.superseriousbusiness.org/gotosocial/internal/state" ) +// check function conformance. +var _ action.GTSAction = ListAttachments +var _ action.GTSAction = ListEmojis + type list struct { dbService db.DB state *state.State @@ -155,7 +159,7 @@ func (l *list) shutdown() error { } // ListAttachments lists local, remote, or all attachment paths. -var ListAttachments action.GTSAction = func(ctx context.Context) error { +func ListAttachments(ctx context.Context) error { list, err := setupList(ctx) if err != nil { return err @@ -214,7 +218,7 @@ var ListAttachments action.GTSAction = func(ctx context.Context) error { } // ListEmojis lists local, remote, or all emoji filepaths. -var ListEmojis action.GTSAction = func(ctx context.Context) error { +func ListEmojis(ctx context.Context) error { list, err := setupList(ctx) if err != nil { return err diff --git a/cmd/gotosocial/action/admin/media/prune/all.go b/cmd/gotosocial/action/admin/media/prune/all.go index 9c2a6a99f..f030517e7 100644 --- a/cmd/gotosocial/action/admin/media/prune/all.go +++ b/cmd/gotosocial/action/admin/media/prune/all.go @@ -26,8 +26,11 @@ import ( "code.superseriousbusiness.org/gotosocial/internal/log" ) +// check function conformance. +var _ action.GTSAction = All + // All performs all media clean actions -var All action.GTSAction = func(ctx context.Context) error { +func All(ctx context.Context) error { // Setup pruning utilities. prune, err := setupPrune(ctx) if err != nil { diff --git a/cmd/gotosocial/action/admin/media/prune/orphaned.go b/cmd/gotosocial/action/admin/media/prune/orphaned.go index 4894e8900..a58485455 100644 --- a/cmd/gotosocial/action/admin/media/prune/orphaned.go +++ b/cmd/gotosocial/action/admin/media/prune/orphaned.go @@ -26,8 +26,11 @@ import ( "code.superseriousbusiness.org/gotosocial/internal/log" ) +// check function conformance. +var _ action.GTSAction = Orphaned + // Orphaned prunes orphaned media from storage. -var Orphaned action.GTSAction = func(ctx context.Context) error { +func Orphaned(ctx context.Context) error { // Setup pruning utilities. prune, err := setupPrune(ctx) if err != nil { diff --git a/cmd/gotosocial/action/admin/media/prune/remote.go b/cmd/gotosocial/action/admin/media/prune/remote.go index fdb3a3ce9..270c9baaf 100644 --- a/cmd/gotosocial/action/admin/media/prune/remote.go +++ b/cmd/gotosocial/action/admin/media/prune/remote.go @@ -27,8 +27,11 @@ import ( "code.superseriousbusiness.org/gotosocial/internal/log" ) +// check function conformance. +var _ action.GTSAction = Remote + // Remote prunes old and/or unused remote media. -var Remote action.GTSAction = func(ctx context.Context) error { +func Remote(ctx context.Context) error { // Setup pruning utilities. prune, err := setupPrune(ctx) if err != nil { diff --git a/cmd/gotosocial/action/admin/trans/export.go b/cmd/gotosocial/action/admin/trans/export.go index 8984a2bab..221a33c70 100644 --- a/cmd/gotosocial/action/admin/trans/export.go +++ b/cmd/gotosocial/action/admin/trans/export.go @@ -29,8 +29,11 @@ import ( "code.superseriousbusiness.org/gotosocial/internal/trans" ) +// check function conformance. +var _ action.GTSAction = Export + // Export exports info from the database into a file -var Export action.GTSAction = func(ctx context.Context) error { +func Export(ctx context.Context) error { var state state.State // Only set state DB connection. diff --git a/cmd/gotosocial/action/admin/trans/import.go b/cmd/gotosocial/action/admin/trans/import.go index 00762bee3..e3e20105c 100644 --- a/cmd/gotosocial/action/admin/trans/import.go +++ b/cmd/gotosocial/action/admin/trans/import.go @@ -29,8 +29,11 @@ import ( "code.superseriousbusiness.org/gotosocial/internal/trans" ) +// check function conformance. +var _ action.GTSAction = Import + // Import imports info from a file into the database -var Import action.GTSAction = func(ctx context.Context) error { +func Import(ctx context.Context) error { var state state.State // Only set state DB connection. diff --git a/cmd/gotosocial/action/debug/config/config.go b/cmd/gotosocial/action/debug/config/config.go index f79f5234d..ead019e69 100644 --- a/cmd/gotosocial/action/debug/config/config.go +++ b/cmd/gotosocial/action/debug/config/config.go @@ -26,8 +26,11 @@ import ( "code.superseriousbusiness.org/gotosocial/internal/config" ) +// check function conformance. +var _ action.GTSAction = Config + // Config just prints the collated config out to stdout as json. -var Config action.GTSAction = func(ctx context.Context) (err error) { +func Config(ctx context.Context) (err error) { var raw map[string]interface{} // Marshal configuration to a raw JSON map diff --git a/cmd/gotosocial/action/migration/run.go b/cmd/gotosocial/action/migration/run.go index 61cec035b..d63160073 100644 --- a/cmd/gotosocial/action/migration/run.go +++ b/cmd/gotosocial/action/migration/run.go @@ -27,8 +27,11 @@ import ( "code.superseriousbusiness.org/gotosocial/internal/state" ) +// check function conformance. +var _ action.GTSAction = Run + // Run will initialize the database, running any available migrations. -var Run action.GTSAction = func(ctx context.Context) error { +func Run(ctx context.Context) error { var state state.State defer func() { diff --git a/cmd/gotosocial/action/server/server.go b/cmd/gotosocial/action/server/server.go index afd908304..85f19b9db 100644 --- a/cmd/gotosocial/action/server/server.go +++ b/cmd/gotosocial/action/server/server.go @@ -70,9 +70,13 @@ import ( "go.uber.org/automaxprocs/maxprocs" ) +// check function conformance. +var _ action.GTSAction = Maintenance +var _ action.GTSAction = Start + // Maintenance starts and creates a GoToSocial server // in maintenance mode (returns 503 for most requests). -var Maintenance action.GTSAction = func(ctx context.Context) error { +func Maintenance(ctx context.Context) error { route, err := router.New(ctx) if err != nil { return fmt.Errorf("error creating maintenance router: %w", err) @@ -101,7 +105,7 @@ var Maintenance action.GTSAction = func(ctx context.Context) error { } // Start creates and starts a gotosocial server -var Start action.GTSAction = func(ctx context.Context) error { +func Start(ctx context.Context) error { // Set GOMAXPROCS / GOMEMLIMIT // to match container limits. setLimits(ctx) diff --git a/cmd/gotosocial/action/testrig/no_testrig.go b/cmd/gotosocial/action/testrig/no_testrig.go index 07e9960dc..d5459b200 100644 --- a/cmd/gotosocial/action/testrig/no_testrig.go +++ b/cmd/gotosocial/action/testrig/no_testrig.go @@ -19,8 +19,15 @@ package testrig -import "code.superseriousbusiness.org/gotosocial/cmd/gotosocial/action" +import ( + "context" + + "code.superseriousbusiness.org/gotosocial/cmd/gotosocial/action" +) + +// check function conformance. +var _ action.GTSAction = Start // Start creates and starts a gotosocial testrig server. // This is only enabled in debug builds, else is nil. -var Start action.GTSAction +func Start(context.Context) error { return nil } diff --git a/cmd/gotosocial/action/testrig/testrig.go b/cmd/gotosocial/action/testrig/testrig.go index 2b70a3447..6399be41c 100644 --- a/cmd/gotosocial/action/testrig/testrig.go +++ b/cmd/gotosocial/action/testrig/testrig.go @@ -50,9 +50,12 @@ import ( "github.com/gin-gonic/gin" ) +// check function conformance. +var _ action.GTSAction = Start + // Start creates and starts a gotosocial testrig server. // This is only enabled in debug builds, else is nil. -var Start action.GTSAction = func(ctx context.Context) error { +func Start(ctx context.Context) error { testrig.InitTestConfig() testrig.InitTestLog() diff --git a/cmd/gotosocial/admin.go b/cmd/gotosocial/admin.go index 63c37a7ce..93fc9c1f8 100644 --- a/cmd/gotosocial/admin.go +++ b/cmd/gotosocial/admin.go @@ -146,6 +146,19 @@ func adminCommands() *cobra.Command { config.AddAdminAccountPassword(adminAccountPasswordCmd) adminAccountCmd.AddCommand(adminAccountPasswordCmd) + adminAccountDisable2FACmd := &cobra.Command{ + Use: "disable-2fa", + Short: "disable 2fa for the given local account", + PreRunE: func(cmd *cobra.Command, args []string) error { + return preRun(preRunArgs{cmd: cmd}) + }, + RunE: func(cmd *cobra.Command, args []string) error { + return run(cmd.Context(), account.Disable2FA) + }, + } + config.AddAdminAccount(adminAccountDisable2FACmd) + adminAccountCmd.AddCommand(adminAccountDisable2FACmd) + adminCmd.AddCommand(adminAccountCmd) /* diff --git a/cmd/gotosocial/testrig.go b/cmd/gotosocial/testrig.go index 55498243b..4c3d75b3a 100644 --- a/cmd/gotosocial/testrig.go +++ b/cmd/gotosocial/testrig.go @@ -20,11 +20,12 @@ package main import ( "code.superseriousbusiness.org/gotosocial/cmd/gotosocial/action/testrig" "code.superseriousbusiness.org/gotosocial/internal/config" + "codeberg.org/gruf/go-debug" "github.com/spf13/cobra" ) func testrigCommands() *cobra.Command { - if testrig.Start != nil { + if debug.DEBUG { testrigCmd := &cobra.Command{ Use: "testrig", Short: "gotosocial testrig-related tasks", |
