Changes
17 changed files (+344/-88)
-
-
@@ -299,6 +299,36 @@ var (Name: "skip-local-2fa", Usage: "Set to true to skip local 2fa for users authenticated by this source", }, cli.StringSliceFlag{ Name: "scopes", Value: nil, Usage: "Scopes to request when to authenticate against this OAuth2 source", }, cli.StringFlag{ Name: "required-claim-name", Value: "", Usage: "Claim name that has to be set to allow users to login with this source", }, cli.StringFlag{ Name: "required-claim-value", Value: "", Usage: "Claim value that has to be set to allow users to login with this source", }, cli.StringFlag{ Name: "group-claim-name", Value: "", Usage: "Claim name providing group names for this source", }, cli.StringFlag{ Name: "admin-group", Value: "", Usage: "Group Claim value for administrator users", }, cli.StringFlag{ Name: "restricted-group", Value: "", Usage: "Group Claim value for restricted users", }, } microcmdAuthUpdateOauth = cli.Command{
-
@@ -649,6 +679,12 @@ func parseOAuth2Config(c *cli.Context) *oauth2.Source {CustomURLMapping: customURLMapping, IconURL: c.String("icon-url"), SkipLocalTwoFA: c.Bool("skip-local-2fa"), Scopes: c.StringSlice("scopes"), RequiredClaimName: c.String("required-claim-name"), RequiredClaimValue: c.String("required-claim-value"), GroupClaimName: c.String("group-claim-name"), AdminGroup: c.String("admin-group"), RestrictedGroup: c.String("restricted-group"), } }
-
@@ -711,6 +747,28 @@ func runUpdateOauth(c *cli.Context) error {oAuth2Config.IconURL = c.String("icon-url") } if c.IsSet("scopes") { oAuth2Config.Scopes = c.StringSlice("scopes") } if c.IsSet("required-claim-name") { oAuth2Config.RequiredClaimName = c.String("required-claim-name") } if c.IsSet("required-claim-value") { oAuth2Config.RequiredClaimValue = c.String("required-claim-value") } if c.IsSet("group-claim-name") { oAuth2Config.GroupClaimName = c.String("group-claim-name") } if c.IsSet("admin-group") { oAuth2Config.AdminGroup = c.String("admin-group") } if c.IsSet("restricted-group") { oAuth2Config.RestrictedGroup = c.String("restricted-group") } // update custom URL mapping var customURLMapping = &oauth2.CustomURLMapping{}
-
-
-
@@ -129,6 +129,13 @@ Admin operations:- `--custom-profile-url`: Use a custom Profile URL (option for GitLab/GitHub). - `--custom-email-url`: Use a custom Email URL (option for GitHub). - `--icon-url`: Custom icon URL for OAuth2 login source. - `--override-local-2fa`: Allow source to override local 2fa. (Optional) - `--scopes`: Addtional scopes to request for this OAuth2 source. (Optional) - `--required-claim-name`: Claim name that has to be set to allow users to login with this source. (Optional) - `--required-claim-value`: Claim value that has to be set to allow users to login with this source. (Optional) - `--group-claim-name`: Claim name providing group names for this source. (Optional) - `--admin-group`: Group Claim value for administrator users. (Optional) - `--restricted-group`: Group Claim value for restricted users. (Optional) - Examples: - `gitea admin auth add-oauth --name external-github --provider github --key OBTAIN_FROM_SOURCE --secret OBTAIN_FROM_SOURCE` - `update-oauth`:
-
@@ -145,6 +152,13 @@ Admin operations:- `--custom-profile-url`: Use a custom Profile URL (option for GitLab/GitHub). - `--custom-email-url`: Use a custom Email URL (option for GitHub). - `--icon-url`: Custom icon URL for OAuth2 login source. - `--override-local-2fa`: Allow source to override local 2fa. (Optional) - `--scopes`: Addtional scopes to request for this OAuth2 source. - `--required-claim-name`: Claim name that has to be set to allow users to login with this source. (Optional) - `--required-claim-value`: Claim value that has to be set to allow users to login with this source. (Optional) - `--group-claim-name`: Claim name providing group names for this source. (Optional) - `--admin-group`: Group Claim value for administrator users. (Optional) - `--restricted-group`: Group Claim value for restricted users. (Optional) - Examples: - `gitea admin auth update-oauth --id 1 --name external-github-updated` - `add-ldap`: Add new LDAP (via Bind DN) authentication source
-
-
-
@@ -10,9 +10,7 @@ import ("time" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/login" "github.com/markbates/goth" "xorm.io/builder" )
-
@@ -139,42 +137,18 @@ func GetUserIDByExternalUserID(provider, userID string) (int64, error) {return id, nil } // UpdateExternalUser updates external user's information func UpdateExternalUser(user *User, gothUser goth.User) error { loginSource, err := login.GetActiveOAuth2LoginSourceByName(gothUser.Provider) if err != nil { return err } externalLoginUser := &ExternalLoginUser{ ExternalID: gothUser.UserID, UserID: user.ID, LoginSourceID: loginSource.ID, RawData: gothUser.RawData, Provider: gothUser.Provider, Email: gothUser.Email, Name: gothUser.Name, FirstName: gothUser.FirstName, LastName: gothUser.LastName, NickName: gothUser.NickName, Description: gothUser.Description, AvatarURL: gothUser.AvatarURL, Location: gothUser.Location, AccessToken: gothUser.AccessToken, AccessTokenSecret: gothUser.AccessTokenSecret, RefreshToken: gothUser.RefreshToken, ExpiresAt: gothUser.ExpiresAt, } has, err := db.GetEngine(db.DefaultContext).Where("external_id=? AND login_source_id=?", gothUser.UserID, loginSource.ID). // UpdateExternalUserByExternalID updates an external user's information func UpdateExternalUserByExternalID(external *ExternalLoginUser) error { has, err := db.GetEngine(db.DefaultContext).Where("external_id=? AND login_source_id=?", external.ExternalID, external.LoginSourceID). NoAutoCondition(). Exist(externalLoginUser) Exist(external) if err != nil { return err } else if !has { return ErrExternalLoginUserNotExist{user.ID, loginSource.ID} return ErrExternalLoginUserNotExist{external.UserID, external.LoginSourceID} } _, err = db.GetEngine(db.DefaultContext).Where("external_id=? AND login_source_id=?", gothUser.UserID, loginSource.ID).AllCols().Update(externalLoginUser) _, err = db.GetEngine(db.DefaultContext).Where("external_id=? AND login_source_id=?", external.ExternalID, external.LoginSourceID).AllCols().Update(external) return err }
-
-
-
@@ -377,6 +377,7 @@ func NewFuncMap() []template.FuncMap {"MermaidMaxSourceCharacters": func() int { return setting.MermaidMaxSourceCharacters }, "Join": strings.Join, "QueryEscape": url.QueryEscape, }} }
-
-
-
@@ -2521,6 +2521,11 @@ auths.oauth2_emailURL = Email URLauths.skip_local_two_fa = Skip local 2FA auths.skip_local_two_fa_helper = Leaving unset means local users with 2FA set will still have to pass 2FA to log on auths.oauth2_tenant = Tenant auths.oauth2_scopes = Additional Scopes auths.oauth2_required_claim_name = Required Claim Name auths.oauth2_required_claim_name_helper = Set this name to restrict login from this source to users with a claim with this name auths.oauth2_required_claim_value = Required Claim Value auths.oauth2_required_claim_value_helper = Set this value to restrict login from this source to users with a claim with this name and value auths.enable_auto_register = Enable Auto Registration auths.sspi_auto_create_users = Automatically create users auths.sspi_auto_create_users_helper = Allow SSPI auth method to automatically create new accounts for users that login for the first time
-
-
-
@@ -11,6 +11,7 @@ import ("net/url" "regexp" "strconv" "strings" "code.gitea.io/gitea/models/login" "code.gitea.io/gitea/modules/auth/pam"
-
@@ -187,6 +188,9 @@ func parseOAuth2Config(form forms.AuthenticationForm) *oauth2.Source {OpenIDConnectAutoDiscoveryURL: form.OpenIDConnectAutoDiscoveryURL, CustomURLMapping: customURLMapping, IconURL: form.Oauth2IconURL, Scopes: strings.Split(form.Oauth2Scopes, ","), RequiredClaimName: form.Oauth2RequiredClaimName, RequiredClaimValue: form.Oauth2RequiredClaimValue, SkipLocalTwoFA: form.SkipLocalTwoFA, } }
-
@@ -329,8 +333,8 @@ func EditAuthSource(ctx *context.Context) {break } } } ctx.HTML(http.StatusOK, tplAuthEdit) }
-
-
-
@@ -320,16 +320,8 @@ func TwoFactorPost(ctx *context.Context) {} if ctx.Session.Get("linkAccount") != nil { gothUser := ctx.Session.Get("linkAccountGothUser") if gothUser == nil { ctx.ServerError("UserSignIn", errors.New("not in LinkAccount session")) return } err = externalaccount.LinkAccountToUser(u, gothUser.(goth.User)) if err != nil { if err := externalaccount.LinkAccountFromStore(ctx.Session, u); err != nil { ctx.ServerError("UserSignIn", err) return } }
-
@@ -506,16 +498,8 @@ func U2FSign(ctx *context.Context) {} if ctx.Session.Get("linkAccount") != nil { gothUser := ctx.Session.Get("linkAccountGothUser") if gothUser == nil { ctx.ServerError("UserSignIn", errors.New("not in LinkAccount session")) return } err = externalaccount.LinkAccountToUser(user, gothUser.(goth.User)) if err != nil { if err := externalaccount.LinkAccountFromStore(ctx.Session, user); err != nil { ctx.ServerError("UserSignIn", err) return } } redirect := handleSignInFull(ctx, user, remember, false)
-
@@ -653,6 +637,13 @@ func SignInOAuthCallback(ctx *context.Context) {u, gothUser, err := oAuth2UserLoginCallback(loginSource, ctx.Req, ctx.Resp) if err != nil { if user_model.IsErrUserProhibitLogin(err) { uplerr := err.(*user_model.ErrUserProhibitLogin) log.Info("Failed authentication attempt for %s from %s: %v", uplerr.Name, ctx.RemoteAddr(), err) ctx.Data["Title"] = ctx.Tr("auth.prohibit_login") ctx.HTML(http.StatusOK, "user/auth/prohibit_login") return } ctx.ServerError("UserSignIn", err) return }
-
@@ -690,6 +681,8 @@ func SignInOAuthCallback(ctx *context.Context) {IsRestricted: setting.Service.DefaultUserIsRestricted, } setUserGroupClaims(loginSource, u, &gothUser) if !createAndHandleCreatedUser(ctx, base.TplName(""), nil, u, &gothUser, setting.OAuth2Client.AccountLinking != setting.OAuth2AccountLinkingDisabled) { // error already handled return
-
@@ -704,6 +697,53 @@ func SignInOAuthCallback(ctx *context.Context) {handleOAuth2SignIn(ctx, loginSource, u, gothUser) } func claimValueToStringSlice(claimValue interface{}) []string { var groups []string switch rawGroup := claimValue.(type) { case []string: groups = rawGroup default: str := fmt.Sprintf("%s", rawGroup) groups = strings.Split(str, ",") } return groups } func setUserGroupClaims(loginSource *login.Source, u *user_model.User, gothUser *goth.User) bool { source := loginSource.Cfg.(*oauth2.Source) if source.GroupClaimName == "" || (source.AdminGroup == "" && source.RestrictedGroup == "") { return false } groupClaims, has := gothUser.RawData[source.GroupClaimName] if !has { return false } groups := claimValueToStringSlice(groupClaims) wasAdmin, wasRestricted := u.IsAdmin, u.IsRestricted if source.AdminGroup != "" { u.IsAdmin = false } if source.RestrictedGroup != "" { u.IsRestricted = false } for _, g := range groups { if source.AdminGroup != "" && g == source.AdminGroup { u.IsAdmin = true } else if source.RestrictedGroup != "" && g == source.RestrictedGroup { u.IsRestricted = true } } return wasAdmin != u.IsAdmin || wasRestricted != u.IsRestricted } func getUserName(gothUser *goth.User) string { switch setting.OAuth2Client.Username { case setting.OAuth2UsernameEmail:
-
@@ -774,13 +814,21 @@ func handleOAuth2SignIn(ctx *context.Context, source *login.Source, u *user_mode// Register last login u.SetLastLogin() if err := user_model.UpdateUserCols(db.DefaultContext, u, "last_login_unix"); err != nil { // Update GroupClaims changed := setUserGroupClaims(source, u, &gothUser) cols := []string{"last_login_unix"} if changed { cols = append(cols, "is_admin", "is_restricted") } if err := user_model.UpdateUserCols(db.DefaultContext, u, cols...); err != nil { ctx.ServerError("UpdateUserCols", err) return } // update external user information if err := user_model.UpdateExternalUser(u, gothUser); err != nil { if err := externalaccount.UpdateExternalUser(u, gothUser); err != nil { log.Error("UpdateExternalUser failed: %v", err) }
-
@@ -794,6 +842,14 @@ func handleOAuth2SignIn(ctx *context.Context, source *login.Source, u *user_modereturn } changed := setUserGroupClaims(source, u, &gothUser) if changed { if err := user_model.UpdateUserCols(db.DefaultContext, u, "is_admin", "is_restricted"); err != nil { ctx.ServerError("UpdateUserCols", err) return } } // User needs to use 2FA, save data and redirect to 2FA page. if err := ctx.Session.Set("twofaUid", u.ID); err != nil { log.Error("Error setting twofaUid in session: %v", err)
-
@@ -818,7 +874,9 @@ func handleOAuth2SignIn(ctx *context.Context, source *login.Source, u *user_mode// OAuth2UserLoginCallback attempts to handle the callback from the OAuth2 provider and if successful // login the user func oAuth2UserLoginCallback(loginSource *login.Source, request *http.Request, response http.ResponseWriter) (*user_model.User, goth.User, error) { gothUser, err := loginSource.Cfg.(*oauth2.Source).Callback(request, response) oauth2Source := loginSource.Cfg.(*oauth2.Source) gothUser, err := oauth2Source.Callback(request, response) if err != nil { if err.Error() == "securecookie: the value is too long" || strings.Contains(err.Error(), "Data too long") { log.Error("OAuth2 Provider %s returned too long a token. Current max: %d. Either increase the [OAuth2] MAX_TOKEN_LENGTH or reduce the information returned from the OAuth2 provider", loginSource.Name, setting.OAuth2.MaxTokenLength)
-
@@ -827,6 +885,27 @@ func oAuth2UserLoginCallback(loginSource *login.Source, request *http.Request, rreturn nil, goth.User{}, err } if oauth2Source.RequiredClaimName != "" { claimInterface, has := gothUser.RawData[oauth2Source.RequiredClaimName] if !has { return nil, goth.User{}, user_model.ErrUserProhibitLogin{Name: gothUser.UserID} } if oauth2Source.RequiredClaimValue != "" { groups := claimValueToStringSlice(claimInterface) found := false for _, group := range groups { if group == oauth2Source.RequiredClaimValue { found = true break } } if !found { return nil, goth.User{}, user_model.ErrUserProhibitLogin{Name: gothUser.UserID} } } } user := &user_model.User{ LoginName: gothUser.UserID, LoginType: login.OAuth2,
-
@@ -1354,7 +1433,7 @@ func handleUserCreated(ctx *context.Context, u *user_model.User, gothUser *goth.// update external user information if gothUser != nil { if err := user_model.UpdateExternalUser(u, *gothUser); err != nil { if err := externalaccount.UpdateExternalUser(u, *gothUser); err != nil { log.Error("UpdateExternalUser failed: %v", err) } }
-
-
-
@@ -144,10 +144,10 @@ func SignInOpenIDPost(ctx *context.Context) {// signInOpenIDVerify handles response from OpenID provider func signInOpenIDVerify(ctx *context.Context) { log.Trace("Incoming call to: " + ctx.Req.URL.String()) log.Trace("Incoming call to: %s", ctx.Req.URL.String()) fullURL := setting.AppURL + ctx.Req.URL.String()[1:] log.Trace("Full URL: " + fullURL) log.Trace("Full URL: %s", fullURL) var id, err = openid.Verify(fullURL) if err != nil {
-
@@ -157,7 +157,7 @@ func signInOpenIDVerify(ctx *context.Context) {return } log.Trace("Verified ID: " + id) log.Trace("Verified ID: %s", id) /* Now we should seek for the user and log him in, or prompt * to register if not found */
-
@@ -180,7 +180,7 @@ func signInOpenIDVerify(ctx *context.Context) {return } log.Trace("User with openid " + id + " does not exist, should connect or register") log.Trace("User with openid: %s does not exist, should connect or register", id) parsedURL, err := url.Parse(fullURL) if err != nil {
-
@@ -199,7 +199,7 @@ func signInOpenIDVerify(ctx *context.Context) {email := values.Get("openid.sreg.email") nickname := values.Get("openid.sreg.nickname") log.Trace("User has email=" + email + " and nickname=" + nickname) log.Trace("User has email=%s and nickname=%s", email, nickname) if email != "" { u, err = user_model.GetUserByEmail(email)
-
@@ -213,7 +213,7 @@ func signInOpenIDVerify(ctx *context.Context) {log.Error("signInOpenIDVerify: %v", err) } if u != nil { log.Trace("Local user " + u.LowerName + " has OpenID provided email " + email) log.Trace("Local user %s has OpenID provided email %s", u.LowerName, email) } }
-
@@ -228,7 +228,7 @@ func signInOpenIDVerify(ctx *context.Context) {} } if u != nil { log.Trace("Local user " + u.LowerName + " has OpenID provided nickname " + nickname) log.Trace("Local user %s has OpenID provided nickname %s", u.LowerName, nickname) } }
-
-
-
@@ -17,7 +17,7 @@ import () // CustomProviderNewFn creates a goth.Provider using a custom url mapping type CustomProviderNewFn func(clientID, secret, callbackURL string, custom *CustomURLMapping) (goth.Provider, error) type CustomProviderNewFn func(clientID, secret, callbackURL string, custom *CustomURLMapping, scopes []string) (goth.Provider, error) // CustomProvider is a GothProvider that has CustomURL features type CustomProvider struct {
-
@@ -35,7 +35,7 @@ func (c *CustomProvider) CustomURLSettings() *CustomURLSettings {func (c *CustomProvider) CreateGothProvider(providerName, callbackURL string, source *Source) (goth.Provider, error) { custom := c.customURLSettings.OverrideWith(source.CustomURLMapping) return c.newFn(source.ClientID, source.ClientSecret, callbackURL, custom) return c.newFn(source.ClientID, source.ClientSecret, callbackURL, custom, source.Scopes) } // NewCustomProvider is a constructor function for custom providers
-
@@ -60,8 +60,7 @@ func init() {ProfileURL: availableAttribute(github.ProfileURL), EmailURL: availableAttribute(github.EmailURL), }, func(clientID, secret, callbackURL string, custom *CustomURLMapping) (goth.Provider, error) { scopes := []string{} func(clientID, secret, callbackURL string, custom *CustomURLMapping, scopes []string) (goth.Provider, error) { if setting.OAuth2Client.EnableAutoRegistration { scopes = append(scopes, "user:email") }
-
@@ -73,8 +72,9 @@ func init() {AuthURL: availableAttribute(gitlab.AuthURL), TokenURL: availableAttribute(gitlab.TokenURL), ProfileURL: availableAttribute(gitlab.ProfileURL), }, func(clientID, secret, callbackURL string, custom *CustomURLMapping) (goth.Provider, error) { return gitlab.NewCustomisedURL(clientID, secret, callbackURL, custom.AuthURL, custom.TokenURL, custom.ProfileURL, "read_user"), nil }, func(clientID, secret, callbackURL string, custom *CustomURLMapping, scopes []string) (goth.Provider, error) { scopes = append(scopes, "read_user") return gitlab.NewCustomisedURL(clientID, secret, callbackURL, custom.AuthURL, custom.TokenURL, custom.ProfileURL, scopes...), nil })) RegisterGothProvider(NewCustomProvider(
-
@@ -83,8 +83,8 @@ func init() {AuthURL: requiredAttribute(gitea.AuthURL), ProfileURL: requiredAttribute(gitea.ProfileURL), }, func(clientID, secret, callbackURL string, custom *CustomURLMapping) (goth.Provider, error) { return gitea.NewCustomisedURL(clientID, secret, callbackURL, custom.AuthURL, custom.TokenURL, custom.ProfileURL), nil func(clientID, secret, callbackURL string, custom *CustomURLMapping, scopes []string) (goth.Provider, error) { return gitea.NewCustomisedURL(clientID, secret, callbackURL, custom.AuthURL, custom.TokenURL, custom.ProfileURL, scopes...), nil })) RegisterGothProvider(NewCustomProvider(
-
@@ -93,25 +93,31 @@ func init() {AuthURL: requiredAttribute(nextcloud.AuthURL), ProfileURL: requiredAttribute(nextcloud.ProfileURL), }, func(clientID, secret, callbackURL string, custom *CustomURLMapping) (goth.Provider, error) { return nextcloud.NewCustomisedURL(clientID, secret, callbackURL, custom.AuthURL, custom.TokenURL, custom.ProfileURL), nil func(clientID, secret, callbackURL string, custom *CustomURLMapping, scopes []string) (goth.Provider, error) { return nextcloud.NewCustomisedURL(clientID, secret, callbackURL, custom.AuthURL, custom.TokenURL, custom.ProfileURL, scopes...), nil })) RegisterGothProvider(NewCustomProvider( "mastodon", "Mastodon", &CustomURLSettings{ AuthURL: requiredAttribute(mastodon.InstanceURL), }, func(clientID, secret, callbackURL string, custom *CustomURLMapping) (goth.Provider, error) { return mastodon.NewCustomisedURL(clientID, secret, callbackURL, custom.AuthURL), nil func(clientID, secret, callbackURL string, custom *CustomURLMapping, scopes []string) (goth.Provider, error) { return mastodon.NewCustomisedURL(clientID, secret, callbackURL, custom.AuthURL, scopes...), nil })) RegisterGothProvider(NewCustomProvider( "azureadv2", "Azure AD v2", &CustomURLSettings{ Tenant: requiredAttribute("organizations"), }, func(clientID, secret, callbackURL string, custom *CustomURLMapping) (goth.Provider, error) { func(clientID, secret, callbackURL string, custom *CustomURLMapping, scopes []string) (goth.Provider, error) { azureScopes := make([]azureadv2.ScopeType, len(scopes)) for i, scope := range scopes { azureScopes[i] = azureadv2.ScopeType(scope) } return azureadv2.New(clientID, secret, callbackURL, azureadv2.ProviderOptions{ Tenant: azureadv2.TenantType(custom.Tenant), Scopes: azureScopes, }), nil }, ))
-
-
-
@@ -33,7 +33,12 @@ func (o *OpenIDProvider) Image() string {// CreateGothProvider creates a GothProvider from this Provider func (o *OpenIDProvider) CreateGothProvider(providerName, callbackURL string, source *Source) (goth.Provider, error) { provider, err := openidConnect.New(source.ClientID, source.ClientSecret, callbackURL, source.OpenIDConnectAutoDiscoveryURL, setting.OAuth2Client.OpenIDConnectScopes...) scopes := setting.OAuth2Client.OpenIDConnectScopes if len(scopes) == 0 { scopes = append(scopes, source.Scopes...) } provider, err := openidConnect.New(source.ClientID, source.ClientSecret, callbackURL, source.OpenIDConnectAutoDiscoveryURL, scopes...) if err != nil { log.Warn("Failed to create OpenID Connect Provider with name '%s' with url '%s': %v", providerName, source.OpenIDConnectAutoDiscoveryURL, err) }
-
-
-
@@ -31,7 +31,10 @@ type SimpleProvider struct {// CreateGothProvider creates a GothProvider from this Provider func (c *SimpleProvider) CreateGothProvider(providerName, callbackURL string, source *Source) (goth.Provider, error) { return c.newFn(source.ClientID, source.ClientSecret, callbackURL, c.scopes...), nil scopes := make([]string, len(c.scopes)+len(source.Scopes)) copy(scopes, c.scopes) copy(scopes[len(c.scopes):], source.Scopes) return c.newFn(source.ClientID, source.ClientSecret, callbackURL, scopes...), nil } // NewSimpleProvider is a constructor function for simple providers
-
-
-
@@ -24,7 +24,14 @@ type Source struct {OpenIDConnectAutoDiscoveryURL string CustomURLMapping *CustomURLMapping IconURL string SkipLocalTwoFA bool `json:",omitempty"` Scopes []string RequiredClaimName string RequiredClaimValue string GroupClaimName string AdminGroup string RestrictedGroup string SkipLocalTwoFA bool `json:",omitempty"` // reference to the loginSource loginSource *login.Source
-
-
-
@@ -0,0 +1,29 @@// Copyright 2021 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package externalaccount import ( "fmt" user_model "code.gitea.io/gitea/models/user" "github.com/markbates/goth" ) // Store represents a thing that stores things type Store interface { Get(interface{}) interface{} Set(interface{}, interface{}) error Release() error } // LinkAccountFromStore links the provided user with a stored external user func LinkAccountFromStore(store Store, user *user_model.User) error { gothUser := store.Get("linkAccountGothUser") if gothUser == nil { return fmt.Errorf("not in LinkAccount session") } return LinkAccountToUser(user, gothUser.(goth.User)) }
-
-
-
@@ -15,14 +15,12 @@ import ("github.com/markbates/goth" ) // LinkAccountToUser link the gothUser to the user func LinkAccountToUser(user *user_model.User, gothUser goth.User) error { func toExternalLoginUser(user *user_model.User, gothUser goth.User) (*user_model.ExternalLoginUser, error) { loginSource, err := login.GetActiveOAuth2LoginSourceByName(gothUser.Provider) if err != nil { return err return nil, err } externalLoginUser := &user_model.ExternalLoginUser{ return &user_model.ExternalLoginUser{ ExternalID: gothUser.UserID, UserID: user.ID, LoginSourceID: loginSource.ID,
-
@@ -40,6 +38,14 @@ func LinkAccountToUser(user *user_model.User, gothUser goth.User) error {AccessTokenSecret: gothUser.AccessTokenSecret, RefreshToken: gothUser.RefreshToken, ExpiresAt: gothUser.ExpiresAt, }, nil } // LinkAccountToUser link the gothUser to the user func LinkAccountToUser(user *user_model.User, gothUser goth.User) error { externalLoginUser, err := toExternalLoginUser(user, gothUser) if err != nil { return err } if err := user_model.LinkExternalToUser(user, externalLoginUser); err != nil {
-
@@ -62,3 +68,13 @@ func LinkAccountToUser(user *user_model.User, gothUser goth.User) error {return nil } // UpdateExternalUser updates external user's information func UpdateExternalUser(user *user_model.User, gothUser goth.User) error { externalLoginUser, err := toExternalLoginUser(user, gothUser) if err != nil { return err } return user_model.UpdateExternalUserByExternalID(externalLoginUser) }
-
-
-
@@ -67,6 +67,12 @@ type AuthenticationForm struct {Oauth2EmailURL string Oauth2IconURL string Oauth2Tenant string Oauth2Scopes string Oauth2RequiredClaimName string Oauth2RequiredClaimValue string Oauth2GroupClaimName string Oauth2AdminGroup string Oauth2RestrictedGroup string SkipLocalTwoFA bool SSPIAutoCreateUsers bool SSPIAutoActivateUsers bool
-
-
-
@@ -286,11 +286,6 @@<input id="skip_local_two_fa" name="skip_local_two_fa" type="checkbox" {{if $cfg.SkipLocalTwoFA}}checked{{end}}> <p class="help">{{.i18n.Tr "admin.auths.skip_local_two_fa_helper"}}</p> </div> </div> <div class="oauth2_use_custom_url inline field"> <div class="ui checkbox"> <label><strong>{{.i18n.Tr "admin.auths.oauth2_use_custom_url"}}</strong></label> <input id="oauth2_use_custom_url" name="oauth2_use_custom_url" type="checkbox" {{if $cfg.CustomURLMapping}}checked{{end}}> </div> </div>
-
@@ -323,6 +318,33 @@<input id="{{.Name}}_email_url" value="{{.CustomURLSettings.EmailURL.Value}}" data-available="{{.CustomURLSettings.EmailURL.Available}}" data-required="{{.CustomURLSettings.EmailURL.Required}}" type="hidden" /> <input id="{{.Name}}_tenant" value="{{.CustomURLSettings.Tenant.Value}}" data-available="{{.CustomURLSettings.Tenant.Available}}" data-required="{{.CustomURLSettings.Tenant.Required}}" type="hidden" /> {{end}}{{end}} <div class="field"> <label for="oauth2_scopes">{{.i18n.Tr "admin.auths.oauth2_scopes"}}</label> <input id="oauth2_scopes" name="oauth2_scopes" value="{{if $cfg.Scopes}}{{Join $cfg.Scopes "," }}{{end}}"> </div> <div class="field"> <label for="oauth2_required_claim_name">{{.i18n.Tr "admin.auths.oauth2_required_claim_name"}}</label> <input id="oauth2_required_claim_name" name="oauth2_required_claim_name" values="{{$cfg.RequiredClaimName}}"> <p class="help">{{.i18n.Tr "admin.auths.oauth2_required_claim_name_helper"}}</p> </div> <div class="field"> <label for="oauth2_required_claim_value">{{.i18n.Tr "admin.auths.oauth2_required_claim_value"}}</label> <input id="oauth2_required_claim_value" name="oauth2_required_claim_value" values="{{$cfg.RequiredClaimValue}}"> <p class="help">{{.i18n.Tr "admin.auths.oauth2_required_claim_value_helper"}}</p> </div> <div class="field"> <label for="oauth2_group_claim_name">{{.i18n.Tr "admin.auths.oauth2_group_claim_name"}}</label> <input id="oauth2_group_claim_name" name="oauth2_group_claim_name" value="{{$cfg.GroupClaimName}}"> </div> <div class="field"> <label for="oauth2_admin_group">{{.i18n.Tr "admin.auths.oauth2_admin_group"}}</label> <input id="oauth2_admin_group" name="oauth2_admin_group" value="{{$cfg.AdminGroup}}"> </div> <div class="field"> <label for="oauth2_restricted_group">{{.i18n.Tr "admin.auths.oauth2_restricted_group"}}</label> <input id="oauth2_restricted_group" name="oauth2_restricted_group" value="{{$cfg.RestrictedGroup}}"> </div> {{end}} <!-- SSPI -->
-
-
-
@@ -71,4 +71,31 @@<input id="{{.Name}}_email_url" value="{{.CustomURLSettings.EmailURL.Value}}" data-available="{{.CustomURLSettings.EmailURL.Available}}" data-required="{{.CustomURLSettings.EmailURL.Required}}" type="hidden" /> <input id="{{.Name}}_tenant" value="{{.CustomURLSettings.Tenant.Value}}" data-available="{{.CustomURLSettings.Tenant.Available}}" data-required="{{.CustomURLSettings.Tenant.Required}}" type="hidden" /> {{end}}{{end}} <div class="field"> <label for="oauth2_scopes">{{.i18n.Tr "admin.auths.oauth2_scopes"}}</label> <input id="oauth2_scopes" name="oauth2_scopes" values="{{.oauth2_scopes}}"> </div> <div class="field"> <label for="oauth2_required_claim_name">{{.i18n.Tr "admin.auths.oauth2_required_claim_name"}}</label> <input id="oauth2_required_claim_name" name="oauth2_required_claim_name" values="{{.oauth2_required_claim_name}}"> <p class="help">{{.i18n.Tr "admin.auths.oauth2_required_claim_name_helper"}}</p> </div> <div class="field"> <label for="oauth2_required_claim_value">{{.i18n.Tr "admin.auths.oauth2_required_claim_value"}}</label> <input id="oauth2_required_claim_value" name="oauth2_required_claim_value" values="{{.oauth2_required_claim_value}}"> <p class="help">{{.i18n.Tr "admin.auths.oauth2_required_claim_value_helper"}}</p> </div> <div class="field"> <label for="oauth2_group_claim_name">{{.i18n.Tr "admin.auths.oauth2_group_claim_name"}}</label> <input id="oauth2_group_claim_name" name="oauth2_group_claim_name" value="{{.oauth2_group_claim_name}}"> </div> <div class="field"> <label for="oauth2_admin_group">{{.i18n.Tr "admin.auths.oauth2_admin_group"}}</label> <input id="oauth2_admin_group" name="oauth2_admin_group" value="{{.oauth2_group_claim_name}}"> </div> <div class="field"> <label for="oauth2_restricted_group">{{.i18n.Tr "admin.auths.oauth2_restricted_group"}}</label> <input id="oauth2_restricted_group" name="oauth2_restricted_group" value="{{.oauth2_group_claim_name}}"> </div> </div>
-