diff --git a/cmd/admin_user_create.go b/cmd/admin_user_create.go index 33e61c0753..81a2a828de 100644 --- a/cmd/admin_user_create.go +++ b/cmd/admin_user_create.go @@ -6,6 +6,7 @@ package cmd import ( "errors" "fmt" + "strings" auth_model "forgejo.org/models/auth" "forgejo.org/models/db" @@ -61,6 +62,16 @@ var microcmdUserCreate = &cli.Command{ Name: "access-token", Usage: "Generate access token for the user", }, + &cli.StringFlag{ + Name: "access-token-name", + Usage: `Name of the generated access token`, + Value: "gitea-admin", + }, + &cli.StringFlag{ + Name: "access-token-scopes", + Usage: `Scopes of the generated access token, comma separated. Examples: "all", "public-only,read:issue", "write:repository,write:user"`, + Value: "all", + }, &cli.BoolFlag{ Name: "restricted", Usage: "Make a restricted user account", @@ -157,23 +168,40 @@ func runCreateUser(c *cli.Context) error { IsRestricted: restricted, } + var accessTokenName string + var accessTokenScope auth_model.AccessTokenScope + if c.IsSet("access-token") { + accessTokenName = strings.TrimSpace(c.String("access-token-name")) + if accessTokenName == "" { + return errors.New("access-token-name cannot be empty") + } + var err error + accessTokenScope, err = auth_model.AccessTokenScope(c.String("access-token-scopes")).Normalize() + if err != nil { + return fmt.Errorf("invalid access token scope provided: %w", err) + } + if !accessTokenScope.HasPermissionScope() { + return errors.New("access token does not have any permission") + } + } else if c.IsSet("access-token-name") || c.IsSet("access-token-scopes") { + return errors.New("access-token-name and access-token-scopes flags are only valid when access-token flag is set") + } + + // arguments should be prepared before creating the user & access token, in case there is anything wrong + + // create the user if err := user_model.CreateUser(ctx, u, overwriteDefault); err != nil { return fmt.Errorf("CreateUser: %w", err) } + fmt.Printf("New user '%s' has been successfully created!\n", username) - if c.Bool("access-token") { - t := &auth_model.AccessToken{ - Name: "gitea-admin", - UID: u.ID, - } - + // create the access token + if accessTokenScope != "" { + t := &auth_model.AccessToken{Name: accessTokenName, UID: u.ID, Scope: accessTokenScope} if err := auth_model.NewAccessToken(ctx, t); err != nil { return err } - fmt.Printf("Access token was successfully created... %s\n", t.Token) } - - fmt.Printf("New user '%s' has been successfully created!\n", username) return nil } diff --git a/cmd/admin_user_generate_access_token.go b/cmd/admin_user_generate_access_token.go index abb874bd5f..1a6c003171 100644 --- a/cmd/admin_user_generate_access_token.go +++ b/cmd/admin_user_generate_access_token.go @@ -34,8 +34,8 @@ var microcmdUserGenerateAccessToken = &cli.Command{ }, &cli.StringFlag{ Name: "scopes", - Value: "", - Usage: "Comma separated list of scopes to apply to access token", + Value: "all", + Usage: `Comma separated list of scopes to apply to access token, examples: "all", "public-only,read:issue", "write:repository,write:user"`, }, }, Action: runGenerateAccessToken, @@ -43,7 +43,7 @@ var microcmdUserGenerateAccessToken = &cli.Command{ func runGenerateAccessToken(c *cli.Context) error { if !c.IsSet("username") { - return errors.New("You must provide a username to generate a token for") + return errors.New("you must provide a username to generate a token for") } ctx, cancel := installSignals() @@ -77,6 +77,9 @@ func runGenerateAccessToken(c *cli.Context) error { if err != nil { return fmt.Errorf("invalid access token scope provided: %w", err) } + if !accessTokenScope.HasPermissionScope() { + return errors.New("access token does not have any permission") + } t.Scope = accessTokenScope // create the token diff --git a/models/auth/access_token_scope.go b/models/auth/access_token_scope.go index 802ad5aa07..d14838cf02 100644 --- a/models/auth/access_token_scope.go +++ b/models/auth/access_token_scope.go @@ -283,6 +283,10 @@ func (s AccessTokenScope) Normalize() (AccessTokenScope, error) { return bitmap.toScope(), nil } +func (s AccessTokenScope) HasPermissionScope() bool { + return s != "" && s != AccessTokenScopePublicOnly +} + // PublicOnly checks if this token scope is limited to public resources func (s AccessTokenScope) PublicOnly() (bool, error) { bitmap, err := s.parse() diff --git a/routers/web/user/setting/applications.go b/routers/web/user/setting/applications.go index 631d5958ea..e73239b79b 100644 --- a/routers/web/user/setting/applications.go +++ b/routers/web/user/setting/applications.go @@ -49,6 +49,9 @@ func ApplicationsPost(ctx *context.Context) { ctx.ServerError("GetScope", err) return } + if !scope.HasPermissionScope() { + ctx.Flash.Error(ctx.Tr("settings.at_least_one_permission"), true) + } t := &auth_model.AccessToken{ UID: ctx.Doer.ID, Name: form.Name,