mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-31 21:28:11 +09:00 
			
		
		
		
	Refactor auth package (#17962)
This commit is contained in:
		
							
								
								
									
										18
									
								
								cmd/admin.go
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								cmd/admin.go
									
									
									
									
									
								
							| @@ -15,8 +15,8 @@ import ( | |||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" | 	"code.gitea.io/gitea/models" | ||||||
| 	asymkey_model "code.gitea.io/gitea/models/asymkey" | 	asymkey_model "code.gitea.io/gitea/models/asymkey" | ||||||
|  | 	"code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	"code.gitea.io/gitea/models/login" |  | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	"code.gitea.io/gitea/modules/git" | 	"code.gitea.io/gitea/modules/git" | ||||||
| 	"code.gitea.io/gitea/modules/graceful" | 	"code.gitea.io/gitea/modules/graceful" | ||||||
| @@ -700,8 +700,8 @@ func runAddOauth(c *cli.Context) error { | |||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return login.CreateSource(&login.Source{ | 	return auth.CreateSource(&auth.Source{ | ||||||
| 		Type:     login.OAuth2, | 		Type:     auth.OAuth2, | ||||||
| 		Name:     c.String("name"), | 		Name:     c.String("name"), | ||||||
| 		IsActive: true, | 		IsActive: true, | ||||||
| 		Cfg:      parseOAuth2Config(c), | 		Cfg:      parseOAuth2Config(c), | ||||||
| @@ -720,7 +720,7 @@ func runUpdateOauth(c *cli.Context) error { | |||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	source, err := login.GetSourceByID(c.Int64("id")) | 	source, err := auth.GetSourceByID(c.Int64("id")) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| @@ -801,7 +801,7 @@ func runUpdateOauth(c *cli.Context) error { | |||||||
| 	oAuth2Config.CustomURLMapping = customURLMapping | 	oAuth2Config.CustomURLMapping = customURLMapping | ||||||
| 	source.Cfg = oAuth2Config | 	source.Cfg = oAuth2Config | ||||||
|  |  | ||||||
| 	return login.UpdateSource(source) | 	return auth.UpdateSource(source) | ||||||
| } | } | ||||||
|  |  | ||||||
| func runListAuth(c *cli.Context) error { | func runListAuth(c *cli.Context) error { | ||||||
| @@ -812,7 +812,7 @@ func runListAuth(c *cli.Context) error { | |||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	loginSources, err := login.Sources() | 	authSources, err := auth.Sources() | ||||||
|  |  | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| @@ -831,7 +831,7 @@ func runListAuth(c *cli.Context) error { | |||||||
| 	// loop through each source and print | 	// loop through each source and print | ||||||
| 	w := tabwriter.NewWriter(os.Stdout, c.Int("min-width"), c.Int("tab-width"), c.Int("padding"), padChar, flags) | 	w := tabwriter.NewWriter(os.Stdout, c.Int("min-width"), c.Int("tab-width"), c.Int("padding"), padChar, flags) | ||||||
| 	fmt.Fprintf(w, "ID\tName\tType\tEnabled\n") | 	fmt.Fprintf(w, "ID\tName\tType\tEnabled\n") | ||||||
| 	for _, source := range loginSources { | 	for _, source := range authSources { | ||||||
| 		fmt.Fprintf(w, "%d\t%s\t%s\t%t\n", source.ID, source.Name, source.Type.String(), source.IsActive) | 		fmt.Fprintf(w, "%d\t%s\t%s\t%t\n", source.ID, source.Name, source.Type.String(), source.IsActive) | ||||||
| 	} | 	} | ||||||
| 	w.Flush() | 	w.Flush() | ||||||
| @@ -851,10 +851,10 @@ func runDeleteAuth(c *cli.Context) error { | |||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	source, err := login.GetSourceByID(c.Int64("id")) | 	source, err := auth.GetSourceByID(c.Int64("id")) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return auth_service.DeleteLoginSource(source) | 	return auth_service.DeleteSource(source) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -9,7 +9,7 @@ import ( | |||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"strings" | 	"strings" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models/login" | 	"code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/services/auth/source/ldap" | 	"code.gitea.io/gitea/services/auth/source/ldap" | ||||||
|  |  | ||||||
| 	"github.com/urfave/cli" | 	"github.com/urfave/cli" | ||||||
| @@ -17,10 +17,10 @@ import ( | |||||||
|  |  | ||||||
| type ( | type ( | ||||||
| 	authService struct { | 	authService struct { | ||||||
| 		initDB             func(ctx context.Context) error | 		initDB            func(ctx context.Context) error | ||||||
| 		createLoginSource  func(loginSource *login.Source) error | 		createAuthSource  func(*auth.Source) error | ||||||
| 		updateLoginSource  func(loginSource *login.Source) error | 		updateAuthSource  func(*auth.Source) error | ||||||
| 		getLoginSourceByID func(id int64) (*login.Source, error) | 		getAuthSourceByID func(id int64) (*auth.Source, error) | ||||||
| 	} | 	} | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -168,23 +168,23 @@ var ( | |||||||
| // newAuthService creates a service with default functions. | // newAuthService creates a service with default functions. | ||||||
| func newAuthService() *authService { | func newAuthService() *authService { | ||||||
| 	return &authService{ | 	return &authService{ | ||||||
| 		initDB:             initDB, | 		initDB:            initDB, | ||||||
| 		createLoginSource:  login.CreateSource, | 		createAuthSource:  auth.CreateSource, | ||||||
| 		updateLoginSource:  login.UpdateSource, | 		updateAuthSource:  auth.UpdateSource, | ||||||
| 		getLoginSourceByID: login.GetSourceByID, | 		getAuthSourceByID: auth.GetSourceByID, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // parseLoginSource assigns values on loginSource according to command line flags. | // parseAuthSource assigns values on authSource according to command line flags. | ||||||
| func parseLoginSource(c *cli.Context, loginSource *login.Source) { | func parseAuthSource(c *cli.Context, authSource *auth.Source) { | ||||||
| 	if c.IsSet("name") { | 	if c.IsSet("name") { | ||||||
| 		loginSource.Name = c.String("name") | 		authSource.Name = c.String("name") | ||||||
| 	} | 	} | ||||||
| 	if c.IsSet("not-active") { | 	if c.IsSet("not-active") { | ||||||
| 		loginSource.IsActive = !c.Bool("not-active") | 		authSource.IsActive = !c.Bool("not-active") | ||||||
| 	} | 	} | ||||||
| 	if c.IsSet("synchronize-users") { | 	if c.IsSet("synchronize-users") { | ||||||
| 		loginSource.IsSyncEnabled = c.Bool("synchronize-users") | 		authSource.IsSyncEnabled = c.Bool("synchronize-users") | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -275,23 +275,23 @@ func findLdapSecurityProtocolByName(name string) (ldap.SecurityProtocol, bool) { | |||||||
| 	return 0, false | 	return 0, false | ||||||
| } | } | ||||||
|  |  | ||||||
| // getLoginSource gets the login source by its id defined in the command line flags. | // getAuthSource gets the login source by its id defined in the command line flags. | ||||||
| // It returns an error if the id is not set, does not match any source or if the source is not of expected type. | // It returns an error if the id is not set, does not match any source or if the source is not of expected type. | ||||||
| func (a *authService) getLoginSource(c *cli.Context, loginType login.Type) (*login.Source, error) { | func (a *authService) getAuthSource(c *cli.Context, authType auth.Type) (*auth.Source, error) { | ||||||
| 	if err := argsSet(c, "id"); err != nil { | 	if err := argsSet(c, "id"); err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	loginSource, err := a.getLoginSourceByID(c.Int64("id")) | 	authSource, err := a.getAuthSourceByID(c.Int64("id")) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if loginSource.Type != loginType { | 	if authSource.Type != authType { | ||||||
| 		return nil, fmt.Errorf("Invalid authentication type. expected: %s, actual: %s", loginType.String(), loginSource.Type.String()) | 		return nil, fmt.Errorf("Invalid authentication type. expected: %s, actual: %s", authType.String(), authSource.Type.String()) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return loginSource, nil | 	return authSource, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| // addLdapBindDn adds a new LDAP via Bind DN authentication source. | // addLdapBindDn adds a new LDAP via Bind DN authentication source. | ||||||
| @@ -307,20 +307,20 @@ func (a *authService) addLdapBindDn(c *cli.Context) error { | |||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	loginSource := &login.Source{ | 	authSource := &auth.Source{ | ||||||
| 		Type:     login.LDAP, | 		Type:     auth.LDAP, | ||||||
| 		IsActive: true, // active by default | 		IsActive: true, // active by default | ||||||
| 		Cfg: &ldap.Source{ | 		Cfg: &ldap.Source{ | ||||||
| 			Enabled: true, // always true | 			Enabled: true, // always true | ||||||
| 		}, | 		}, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	parseLoginSource(c, loginSource) | 	parseAuthSource(c, authSource) | ||||||
| 	if err := parseLdapConfig(c, loginSource.Cfg.(*ldap.Source)); err != nil { | 	if err := parseLdapConfig(c, authSource.Cfg.(*ldap.Source)); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return a.createLoginSource(loginSource) | 	return a.createAuthSource(authSource) | ||||||
| } | } | ||||||
|  |  | ||||||
| // updateLdapBindDn updates a new LDAP via Bind DN authentication source. | // updateLdapBindDn updates a new LDAP via Bind DN authentication source. | ||||||
| @@ -332,17 +332,17 @@ func (a *authService) updateLdapBindDn(c *cli.Context) error { | |||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	loginSource, err := a.getLoginSource(c, login.LDAP) | 	authSource, err := a.getAuthSource(c, auth.LDAP) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	parseLoginSource(c, loginSource) | 	parseAuthSource(c, authSource) | ||||||
| 	if err := parseLdapConfig(c, loginSource.Cfg.(*ldap.Source)); err != nil { | 	if err := parseLdapConfig(c, authSource.Cfg.(*ldap.Source)); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return a.updateLoginSource(loginSource) | 	return a.updateAuthSource(authSource) | ||||||
| } | } | ||||||
|  |  | ||||||
| // addLdapSimpleAuth adds a new LDAP (simple auth) authentication source. | // addLdapSimpleAuth adds a new LDAP (simple auth) authentication source. | ||||||
| @@ -358,20 +358,20 @@ func (a *authService) addLdapSimpleAuth(c *cli.Context) error { | |||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	loginSource := &login.Source{ | 	authSource := &auth.Source{ | ||||||
| 		Type:     login.DLDAP, | 		Type:     auth.DLDAP, | ||||||
| 		IsActive: true, // active by default | 		IsActive: true, // active by default | ||||||
| 		Cfg: &ldap.Source{ | 		Cfg: &ldap.Source{ | ||||||
| 			Enabled: true, // always true | 			Enabled: true, // always true | ||||||
| 		}, | 		}, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	parseLoginSource(c, loginSource) | 	parseAuthSource(c, authSource) | ||||||
| 	if err := parseLdapConfig(c, loginSource.Cfg.(*ldap.Source)); err != nil { | 	if err := parseLdapConfig(c, authSource.Cfg.(*ldap.Source)); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return a.createLoginSource(loginSource) | 	return a.createAuthSource(authSource) | ||||||
| } | } | ||||||
|  |  | ||||||
| // updateLdapBindDn updates a new LDAP (simple auth) authentication source. | // updateLdapBindDn updates a new LDAP (simple auth) authentication source. | ||||||
| @@ -383,15 +383,15 @@ func (a *authService) updateLdapSimpleAuth(c *cli.Context) error { | |||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	loginSource, err := a.getLoginSource(c, login.DLDAP) | 	authSource, err := a.getAuthSource(c, auth.DLDAP) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	parseLoginSource(c, loginSource) | 	parseAuthSource(c, authSource) | ||||||
| 	if err := parseLdapConfig(c, loginSource.Cfg.(*ldap.Source)); err != nil { | 	if err := parseLdapConfig(c, authSource.Cfg.(*ldap.Source)); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return a.updateLoginSource(loginSource) | 	return a.updateAuthSource(authSource) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -8,7 +8,7 @@ import ( | |||||||
| 	"context" | 	"context" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models/login" | 	"code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/services/auth/source/ldap" | 	"code.gitea.io/gitea/services/auth/source/ldap" | ||||||
|  |  | ||||||
| 	"github.com/stretchr/testify/assert" | 	"github.com/stretchr/testify/assert" | ||||||
| @@ -23,9 +23,9 @@ func TestAddLdapBindDn(t *testing.T) { | |||||||
|  |  | ||||||
| 	// Test cases | 	// Test cases | ||||||
| 	var cases = []struct { | 	var cases = []struct { | ||||||
| 		args        []string | 		args   []string | ||||||
| 		loginSource *login.Source | 		source *auth.Source | ||||||
| 		errMsg      string | 		errMsg string | ||||||
| 	}{ | 	}{ | ||||||
| 		// case 0 | 		// case 0 | ||||||
| 		{ | 		{ | ||||||
| @@ -53,8 +53,8 @@ func TestAddLdapBindDn(t *testing.T) { | |||||||
| 				"--synchronize-users", | 				"--synchronize-users", | ||||||
| 				"--page-size", "99", | 				"--page-size", "99", | ||||||
| 			}, | 			}, | ||||||
| 			loginSource: &login.Source{ | 			source: &auth.Source{ | ||||||
| 				Type:          login.LDAP, | 				Type:          auth.LDAP, | ||||||
| 				Name:          "ldap (via Bind DN) source full", | 				Name:          "ldap (via Bind DN) source full", | ||||||
| 				IsActive:      false, | 				IsActive:      false, | ||||||
| 				IsSyncEnabled: true, | 				IsSyncEnabled: true, | ||||||
| @@ -94,8 +94,8 @@ func TestAddLdapBindDn(t *testing.T) { | |||||||
| 				"--user-filter", "(memberOf=cn=user-group,ou=example,dc=min-domain-bind,dc=org)", | 				"--user-filter", "(memberOf=cn=user-group,ou=example,dc=min-domain-bind,dc=org)", | ||||||
| 				"--email-attribute", "mail-bind min", | 				"--email-attribute", "mail-bind min", | ||||||
| 			}, | 			}, | ||||||
| 			loginSource: &login.Source{ | 			source: &auth.Source{ | ||||||
| 				Type:     login.LDAP, | 				Type:     auth.LDAP, | ||||||
| 				Name:     "ldap (via Bind DN) source min", | 				Name:     "ldap (via Bind DN) source min", | ||||||
| 				IsActive: true, | 				IsActive: true, | ||||||
| 				Cfg: &ldap.Source{ | 				Cfg: &ldap.Source{ | ||||||
| @@ -206,21 +206,21 @@ func TestAddLdapBindDn(t *testing.T) { | |||||||
|  |  | ||||||
| 	for n, c := range cases { | 	for n, c := range cases { | ||||||
| 		// Mock functions. | 		// Mock functions. | ||||||
| 		var createdLoginSource *login.Source | 		var createdAuthSource *auth.Source | ||||||
| 		service := &authService{ | 		service := &authService{ | ||||||
| 			initDB: func(context.Context) error { | 			initDB: func(context.Context) error { | ||||||
| 				return nil | 				return nil | ||||||
| 			}, | 			}, | ||||||
| 			createLoginSource: func(loginSource *login.Source) error { | 			createAuthSource: func(authSource *auth.Source) error { | ||||||
| 				createdLoginSource = loginSource | 				createdAuthSource = authSource | ||||||
| 				return nil | 				return nil | ||||||
| 			}, | 			}, | ||||||
| 			updateLoginSource: func(loginSource *login.Source) error { | 			updateAuthSource: func(authSource *auth.Source) error { | ||||||
| 				assert.FailNow(t, "case %d: should not call updateLoginSource", n) | 				assert.FailNow(t, "case %d: should not call updateAuthSource", n) | ||||||
| 				return nil | 				return nil | ||||||
| 			}, | 			}, | ||||||
| 			getLoginSourceByID: func(id int64) (*login.Source, error) { | 			getAuthSourceByID: func(id int64) (*auth.Source, error) { | ||||||
| 				assert.FailNow(t, "case %d: should not call getLoginSourceByID", n) | 				assert.FailNow(t, "case %d: should not call getAuthSourceByID", n) | ||||||
| 				return nil, nil | 				return nil, nil | ||||||
| 			}, | 			}, | ||||||
| 		} | 		} | ||||||
| @@ -236,7 +236,7 @@ func TestAddLdapBindDn(t *testing.T) { | |||||||
| 			assert.EqualError(t, err, c.errMsg, "case %d: error should match", n) | 			assert.EqualError(t, err, c.errMsg, "case %d: error should match", n) | ||||||
| 		} else { | 		} else { | ||||||
| 			assert.NoError(t, err, "case %d: should have no errors", n) | 			assert.NoError(t, err, "case %d: should have no errors", n) | ||||||
| 			assert.Equal(t, c.loginSource, createdLoginSource, "case %d: wrong loginSource", n) | 			assert.Equal(t, c.source, createdAuthSource, "case %d: wrong authSource", n) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @@ -249,9 +249,9 @@ func TestAddLdapSimpleAuth(t *testing.T) { | |||||||
|  |  | ||||||
| 	// Test cases | 	// Test cases | ||||||
| 	var cases = []struct { | 	var cases = []struct { | ||||||
| 		args        []string | 		args       []string | ||||||
| 		loginSource *login.Source | 		authSource *auth.Source | ||||||
| 		errMsg      string | 		errMsg     string | ||||||
| 	}{ | 	}{ | ||||||
| 		// case 0 | 		// case 0 | ||||||
| 		{ | 		{ | ||||||
| @@ -275,8 +275,8 @@ func TestAddLdapSimpleAuth(t *testing.T) { | |||||||
| 				"--avatar-attribute", "avatar-simple full", | 				"--avatar-attribute", "avatar-simple full", | ||||||
| 				"--user-dn", "cn=%s,ou=Users,dc=full-domain-simple,dc=org", | 				"--user-dn", "cn=%s,ou=Users,dc=full-domain-simple,dc=org", | ||||||
| 			}, | 			}, | ||||||
| 			loginSource: &login.Source{ | 			authSource: &auth.Source{ | ||||||
| 				Type:     login.DLDAP, | 				Type:     auth.DLDAP, | ||||||
| 				Name:     "ldap (simple auth) source full", | 				Name:     "ldap (simple auth) source full", | ||||||
| 				IsActive: false, | 				IsActive: false, | ||||||
| 				Cfg: &ldap.Source{ | 				Cfg: &ldap.Source{ | ||||||
| @@ -312,8 +312,8 @@ func TestAddLdapSimpleAuth(t *testing.T) { | |||||||
| 				"--email-attribute", "mail-simple min", | 				"--email-attribute", "mail-simple min", | ||||||
| 				"--user-dn", "cn=%s,ou=Users,dc=min-domain-simple,dc=org", | 				"--user-dn", "cn=%s,ou=Users,dc=min-domain-simple,dc=org", | ||||||
| 			}, | 			}, | ||||||
| 			loginSource: &login.Source{ | 			authSource: &auth.Source{ | ||||||
| 				Type:     login.DLDAP, | 				Type:     auth.DLDAP, | ||||||
| 				Name:     "ldap (simple auth) source min", | 				Name:     "ldap (simple auth) source min", | ||||||
| 				IsActive: true, | 				IsActive: true, | ||||||
| 				Cfg: &ldap.Source{ | 				Cfg: &ldap.Source{ | ||||||
| @@ -437,21 +437,21 @@ func TestAddLdapSimpleAuth(t *testing.T) { | |||||||
|  |  | ||||||
| 	for n, c := range cases { | 	for n, c := range cases { | ||||||
| 		// Mock functions. | 		// Mock functions. | ||||||
| 		var createdLoginSource *login.Source | 		var createdAuthSource *auth.Source | ||||||
| 		service := &authService{ | 		service := &authService{ | ||||||
| 			initDB: func(context.Context) error { | 			initDB: func(context.Context) error { | ||||||
| 				return nil | 				return nil | ||||||
| 			}, | 			}, | ||||||
| 			createLoginSource: func(loginSource *login.Source) error { | 			createAuthSource: func(authSource *auth.Source) error { | ||||||
| 				createdLoginSource = loginSource | 				createdAuthSource = authSource | ||||||
| 				return nil | 				return nil | ||||||
| 			}, | 			}, | ||||||
| 			updateLoginSource: func(loginSource *login.Source) error { | 			updateAuthSource: func(authSource *auth.Source) error { | ||||||
| 				assert.FailNow(t, "case %d: should not call updateLoginSource", n) | 				assert.FailNow(t, "case %d: should not call updateAuthSource", n) | ||||||
| 				return nil | 				return nil | ||||||
| 			}, | 			}, | ||||||
| 			getLoginSourceByID: func(id int64) (*login.Source, error) { | 			getAuthSourceByID: func(id int64) (*auth.Source, error) { | ||||||
| 				assert.FailNow(t, "case %d: should not call getLoginSourceByID", n) | 				assert.FailNow(t, "case %d: should not call getAuthSourceByID", n) | ||||||
| 				return nil, nil | 				return nil, nil | ||||||
| 			}, | 			}, | ||||||
| 		} | 		} | ||||||
| @@ -467,7 +467,7 @@ func TestAddLdapSimpleAuth(t *testing.T) { | |||||||
| 			assert.EqualError(t, err, c.errMsg, "case %d: error should match", n) | 			assert.EqualError(t, err, c.errMsg, "case %d: error should match", n) | ||||||
| 		} else { | 		} else { | ||||||
| 			assert.NoError(t, err, "case %d: should have no errors", n) | 			assert.NoError(t, err, "case %d: should have no errors", n) | ||||||
| 			assert.Equal(t, c.loginSource, createdLoginSource, "case %d: wrong loginSource", n) | 			assert.Equal(t, c.authSource, createdAuthSource, "case %d: wrong authSource", n) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @@ -480,11 +480,11 @@ func TestUpdateLdapBindDn(t *testing.T) { | |||||||
|  |  | ||||||
| 	// Test cases | 	// Test cases | ||||||
| 	var cases = []struct { | 	var cases = []struct { | ||||||
| 		args                []string | 		args               []string | ||||||
| 		id                  int64 | 		id                 int64 | ||||||
| 		existingLoginSource *login.Source | 		existingAuthSource *auth.Source | ||||||
| 		loginSource         *login.Source | 		authSource         *auth.Source | ||||||
| 		errMsg              string | 		errMsg             string | ||||||
| 	}{ | 	}{ | ||||||
| 		// case 0 | 		// case 0 | ||||||
| 		{ | 		{ | ||||||
| @@ -513,15 +513,15 @@ func TestUpdateLdapBindDn(t *testing.T) { | |||||||
| 				"--page-size", "99", | 				"--page-size", "99", | ||||||
| 			}, | 			}, | ||||||
| 			id: 23, | 			id: 23, | ||||||
| 			existingLoginSource: &login.Source{ | 			existingAuthSource: &auth.Source{ | ||||||
| 				Type:     login.LDAP, | 				Type:     auth.LDAP, | ||||||
| 				IsActive: true, | 				IsActive: true, | ||||||
| 				Cfg: &ldap.Source{ | 				Cfg: &ldap.Source{ | ||||||
| 					Enabled: true, | 					Enabled: true, | ||||||
| 				}, | 				}, | ||||||
| 			}, | 			}, | ||||||
| 			loginSource: &login.Source{ | 			authSource: &auth.Source{ | ||||||
| 				Type:          login.LDAP, | 				Type:          auth.LDAP, | ||||||
| 				Name:          "ldap (via Bind DN) source full", | 				Name:          "ldap (via Bind DN) source full", | ||||||
| 				IsActive:      false, | 				IsActive:      false, | ||||||
| 				IsSyncEnabled: true, | 				IsSyncEnabled: true, | ||||||
| @@ -555,8 +555,8 @@ func TestUpdateLdapBindDn(t *testing.T) { | |||||||
| 				"ldap-test", | 				"ldap-test", | ||||||
| 				"--id", "1", | 				"--id", "1", | ||||||
| 			}, | 			}, | ||||||
| 			loginSource: &login.Source{ | 			authSource: &auth.Source{ | ||||||
| 				Type: login.LDAP, | 				Type: auth.LDAP, | ||||||
| 				Cfg:  &ldap.Source{}, | 				Cfg:  &ldap.Source{}, | ||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| @@ -567,8 +567,8 @@ func TestUpdateLdapBindDn(t *testing.T) { | |||||||
| 				"--id", "1", | 				"--id", "1", | ||||||
| 				"--name", "ldap (via Bind DN) source", | 				"--name", "ldap (via Bind DN) source", | ||||||
| 			}, | 			}, | ||||||
| 			loginSource: &login.Source{ | 			authSource: &auth.Source{ | ||||||
| 				Type: login.LDAP, | 				Type: auth.LDAP, | ||||||
| 				Name: "ldap (via Bind DN) source", | 				Name: "ldap (via Bind DN) source", | ||||||
| 				Cfg: &ldap.Source{ | 				Cfg: &ldap.Source{ | ||||||
| 					Name: "ldap (via Bind DN) source", | 					Name: "ldap (via Bind DN) source", | ||||||
| @@ -582,13 +582,13 @@ func TestUpdateLdapBindDn(t *testing.T) { | |||||||
| 				"--id", "1", | 				"--id", "1", | ||||||
| 				"--not-active", | 				"--not-active", | ||||||
| 			}, | 			}, | ||||||
| 			existingLoginSource: &login.Source{ | 			existingAuthSource: &auth.Source{ | ||||||
| 				Type:     login.LDAP, | 				Type:     auth.LDAP, | ||||||
| 				IsActive: true, | 				IsActive: true, | ||||||
| 				Cfg:      &ldap.Source{}, | 				Cfg:      &ldap.Source{}, | ||||||
| 			}, | 			}, | ||||||
| 			loginSource: &login.Source{ | 			authSource: &auth.Source{ | ||||||
| 				Type:     login.LDAP, | 				Type:     auth.LDAP, | ||||||
| 				IsActive: false, | 				IsActive: false, | ||||||
| 				Cfg:      &ldap.Source{}, | 				Cfg:      &ldap.Source{}, | ||||||
| 			}, | 			}, | ||||||
| @@ -600,8 +600,8 @@ func TestUpdateLdapBindDn(t *testing.T) { | |||||||
| 				"--id", "1", | 				"--id", "1", | ||||||
| 				"--security-protocol", "LDAPS", | 				"--security-protocol", "LDAPS", | ||||||
| 			}, | 			}, | ||||||
| 			loginSource: &login.Source{ | 			authSource: &auth.Source{ | ||||||
| 				Type: login.LDAP, | 				Type: auth.LDAP, | ||||||
| 				Cfg: &ldap.Source{ | 				Cfg: &ldap.Source{ | ||||||
| 					SecurityProtocol: ldap.SecurityProtocol(1), | 					SecurityProtocol: ldap.SecurityProtocol(1), | ||||||
| 				}, | 				}, | ||||||
| @@ -614,8 +614,8 @@ func TestUpdateLdapBindDn(t *testing.T) { | |||||||
| 				"--id", "1", | 				"--id", "1", | ||||||
| 				"--skip-tls-verify", | 				"--skip-tls-verify", | ||||||
| 			}, | 			}, | ||||||
| 			loginSource: &login.Source{ | 			authSource: &auth.Source{ | ||||||
| 				Type: login.LDAP, | 				Type: auth.LDAP, | ||||||
| 				Cfg: &ldap.Source{ | 				Cfg: &ldap.Source{ | ||||||
| 					SkipVerify: true, | 					SkipVerify: true, | ||||||
| 				}, | 				}, | ||||||
| @@ -628,8 +628,8 @@ func TestUpdateLdapBindDn(t *testing.T) { | |||||||
| 				"--id", "1", | 				"--id", "1", | ||||||
| 				"--host", "ldap-server", | 				"--host", "ldap-server", | ||||||
| 			}, | 			}, | ||||||
| 			loginSource: &login.Source{ | 			authSource: &auth.Source{ | ||||||
| 				Type: login.LDAP, | 				Type: auth.LDAP, | ||||||
| 				Cfg: &ldap.Source{ | 				Cfg: &ldap.Source{ | ||||||
| 					Host: "ldap-server", | 					Host: "ldap-server", | ||||||
| 				}, | 				}, | ||||||
| @@ -642,8 +642,8 @@ func TestUpdateLdapBindDn(t *testing.T) { | |||||||
| 				"--id", "1", | 				"--id", "1", | ||||||
| 				"--port", "389", | 				"--port", "389", | ||||||
| 			}, | 			}, | ||||||
| 			loginSource: &login.Source{ | 			authSource: &auth.Source{ | ||||||
| 				Type: login.LDAP, | 				Type: auth.LDAP, | ||||||
| 				Cfg: &ldap.Source{ | 				Cfg: &ldap.Source{ | ||||||
| 					Port: 389, | 					Port: 389, | ||||||
| 				}, | 				}, | ||||||
| @@ -656,8 +656,8 @@ func TestUpdateLdapBindDn(t *testing.T) { | |||||||
| 				"--id", "1", | 				"--id", "1", | ||||||
| 				"--user-search-base", "ou=Users,dc=domain,dc=org", | 				"--user-search-base", "ou=Users,dc=domain,dc=org", | ||||||
| 			}, | 			}, | ||||||
| 			loginSource: &login.Source{ | 			authSource: &auth.Source{ | ||||||
| 				Type: login.LDAP, | 				Type: auth.LDAP, | ||||||
| 				Cfg: &ldap.Source{ | 				Cfg: &ldap.Source{ | ||||||
| 					UserBase: "ou=Users,dc=domain,dc=org", | 					UserBase: "ou=Users,dc=domain,dc=org", | ||||||
| 				}, | 				}, | ||||||
| @@ -670,8 +670,8 @@ func TestUpdateLdapBindDn(t *testing.T) { | |||||||
| 				"--id", "1", | 				"--id", "1", | ||||||
| 				"--user-filter", "(memberOf=cn=user-group,ou=example,dc=domain,dc=org)", | 				"--user-filter", "(memberOf=cn=user-group,ou=example,dc=domain,dc=org)", | ||||||
| 			}, | 			}, | ||||||
| 			loginSource: &login.Source{ | 			authSource: &auth.Source{ | ||||||
| 				Type: login.LDAP, | 				Type: auth.LDAP, | ||||||
| 				Cfg: &ldap.Source{ | 				Cfg: &ldap.Source{ | ||||||
| 					Filter: "(memberOf=cn=user-group,ou=example,dc=domain,dc=org)", | 					Filter: "(memberOf=cn=user-group,ou=example,dc=domain,dc=org)", | ||||||
| 				}, | 				}, | ||||||
| @@ -684,8 +684,8 @@ func TestUpdateLdapBindDn(t *testing.T) { | |||||||
| 				"--id", "1", | 				"--id", "1", | ||||||
| 				"--admin-filter", "(memberOf=cn=admin-group,ou=example,dc=domain,dc=org)", | 				"--admin-filter", "(memberOf=cn=admin-group,ou=example,dc=domain,dc=org)", | ||||||
| 			}, | 			}, | ||||||
| 			loginSource: &login.Source{ | 			authSource: &auth.Source{ | ||||||
| 				Type: login.LDAP, | 				Type: auth.LDAP, | ||||||
| 				Cfg: &ldap.Source{ | 				Cfg: &ldap.Source{ | ||||||
| 					AdminFilter: "(memberOf=cn=admin-group,ou=example,dc=domain,dc=org)", | 					AdminFilter: "(memberOf=cn=admin-group,ou=example,dc=domain,dc=org)", | ||||||
| 				}, | 				}, | ||||||
| @@ -698,8 +698,8 @@ func TestUpdateLdapBindDn(t *testing.T) { | |||||||
| 				"--id", "1", | 				"--id", "1", | ||||||
| 				"--username-attribute", "uid", | 				"--username-attribute", "uid", | ||||||
| 			}, | 			}, | ||||||
| 			loginSource: &login.Source{ | 			authSource: &auth.Source{ | ||||||
| 				Type: login.LDAP, | 				Type: auth.LDAP, | ||||||
| 				Cfg: &ldap.Source{ | 				Cfg: &ldap.Source{ | ||||||
| 					AttributeUsername: "uid", | 					AttributeUsername: "uid", | ||||||
| 				}, | 				}, | ||||||
| @@ -712,8 +712,8 @@ func TestUpdateLdapBindDn(t *testing.T) { | |||||||
| 				"--id", "1", | 				"--id", "1", | ||||||
| 				"--firstname-attribute", "givenName", | 				"--firstname-attribute", "givenName", | ||||||
| 			}, | 			}, | ||||||
| 			loginSource: &login.Source{ | 			authSource: &auth.Source{ | ||||||
| 				Type: login.LDAP, | 				Type: auth.LDAP, | ||||||
| 				Cfg: &ldap.Source{ | 				Cfg: &ldap.Source{ | ||||||
| 					AttributeName: "givenName", | 					AttributeName: "givenName", | ||||||
| 				}, | 				}, | ||||||
| @@ -726,8 +726,8 @@ func TestUpdateLdapBindDn(t *testing.T) { | |||||||
| 				"--id", "1", | 				"--id", "1", | ||||||
| 				"--surname-attribute", "sn", | 				"--surname-attribute", "sn", | ||||||
| 			}, | 			}, | ||||||
| 			loginSource: &login.Source{ | 			authSource: &auth.Source{ | ||||||
| 				Type: login.LDAP, | 				Type: auth.LDAP, | ||||||
| 				Cfg: &ldap.Source{ | 				Cfg: &ldap.Source{ | ||||||
| 					AttributeSurname: "sn", | 					AttributeSurname: "sn", | ||||||
| 				}, | 				}, | ||||||
| @@ -740,8 +740,8 @@ func TestUpdateLdapBindDn(t *testing.T) { | |||||||
| 				"--id", "1", | 				"--id", "1", | ||||||
| 				"--email-attribute", "mail", | 				"--email-attribute", "mail", | ||||||
| 			}, | 			}, | ||||||
| 			loginSource: &login.Source{ | 			authSource: &auth.Source{ | ||||||
| 				Type: login.LDAP, | 				Type: auth.LDAP, | ||||||
| 				Cfg: &ldap.Source{ | 				Cfg: &ldap.Source{ | ||||||
| 					AttributeMail: "mail", | 					AttributeMail: "mail", | ||||||
| 				}, | 				}, | ||||||
| @@ -754,8 +754,8 @@ func TestUpdateLdapBindDn(t *testing.T) { | |||||||
| 				"--id", "1", | 				"--id", "1", | ||||||
| 				"--attributes-in-bind", | 				"--attributes-in-bind", | ||||||
| 			}, | 			}, | ||||||
| 			loginSource: &login.Source{ | 			authSource: &auth.Source{ | ||||||
| 				Type: login.LDAP, | 				Type: auth.LDAP, | ||||||
| 				Cfg: &ldap.Source{ | 				Cfg: &ldap.Source{ | ||||||
| 					AttributesInBind: true, | 					AttributesInBind: true, | ||||||
| 				}, | 				}, | ||||||
| @@ -768,8 +768,8 @@ func TestUpdateLdapBindDn(t *testing.T) { | |||||||
| 				"--id", "1", | 				"--id", "1", | ||||||
| 				"--public-ssh-key-attribute", "publickey", | 				"--public-ssh-key-attribute", "publickey", | ||||||
| 			}, | 			}, | ||||||
| 			loginSource: &login.Source{ | 			authSource: &auth.Source{ | ||||||
| 				Type: login.LDAP, | 				Type: auth.LDAP, | ||||||
| 				Cfg: &ldap.Source{ | 				Cfg: &ldap.Source{ | ||||||
| 					AttributeSSHPublicKey: "publickey", | 					AttributeSSHPublicKey: "publickey", | ||||||
| 				}, | 				}, | ||||||
| @@ -782,8 +782,8 @@ func TestUpdateLdapBindDn(t *testing.T) { | |||||||
| 				"--id", "1", | 				"--id", "1", | ||||||
| 				"--bind-dn", "cn=readonly,dc=domain,dc=org", | 				"--bind-dn", "cn=readonly,dc=domain,dc=org", | ||||||
| 			}, | 			}, | ||||||
| 			loginSource: &login.Source{ | 			authSource: &auth.Source{ | ||||||
| 				Type: login.LDAP, | 				Type: auth.LDAP, | ||||||
| 				Cfg: &ldap.Source{ | 				Cfg: &ldap.Source{ | ||||||
| 					BindDN: "cn=readonly,dc=domain,dc=org", | 					BindDN: "cn=readonly,dc=domain,dc=org", | ||||||
| 				}, | 				}, | ||||||
| @@ -796,8 +796,8 @@ func TestUpdateLdapBindDn(t *testing.T) { | |||||||
| 				"--id", "1", | 				"--id", "1", | ||||||
| 				"--bind-password", "secret", | 				"--bind-password", "secret", | ||||||
| 			}, | 			}, | ||||||
| 			loginSource: &login.Source{ | 			authSource: &auth.Source{ | ||||||
| 				Type: login.LDAP, | 				Type: auth.LDAP, | ||||||
| 				Cfg: &ldap.Source{ | 				Cfg: &ldap.Source{ | ||||||
| 					BindPassword: "secret", | 					BindPassword: "secret", | ||||||
| 				}, | 				}, | ||||||
| @@ -810,8 +810,8 @@ func TestUpdateLdapBindDn(t *testing.T) { | |||||||
| 				"--id", "1", | 				"--id", "1", | ||||||
| 				"--synchronize-users", | 				"--synchronize-users", | ||||||
| 			}, | 			}, | ||||||
| 			loginSource: &login.Source{ | 			authSource: &auth.Source{ | ||||||
| 				Type:          login.LDAP, | 				Type:          auth.LDAP, | ||||||
| 				IsSyncEnabled: true, | 				IsSyncEnabled: true, | ||||||
| 				Cfg:           &ldap.Source{}, | 				Cfg:           &ldap.Source{}, | ||||||
| 			}, | 			}, | ||||||
| @@ -823,8 +823,8 @@ func TestUpdateLdapBindDn(t *testing.T) { | |||||||
| 				"--id", "1", | 				"--id", "1", | ||||||
| 				"--page-size", "12", | 				"--page-size", "12", | ||||||
| 			}, | 			}, | ||||||
| 			loginSource: &login.Source{ | 			authSource: &auth.Source{ | ||||||
| 				Type: login.LDAP, | 				Type: auth.LDAP, | ||||||
| 				Cfg: &ldap.Source{ | 				Cfg: &ldap.Source{ | ||||||
| 					SearchPageSize: 12, | 					SearchPageSize: 12, | ||||||
| 				}, | 				}, | ||||||
| @@ -852,8 +852,8 @@ func TestUpdateLdapBindDn(t *testing.T) { | |||||||
| 				"ldap-test", | 				"ldap-test", | ||||||
| 				"--id", "1", | 				"--id", "1", | ||||||
| 			}, | 			}, | ||||||
| 			existingLoginSource: &login.Source{ | 			existingAuthSource: &auth.Source{ | ||||||
| 				Type: login.OAuth2, | 				Type: auth.OAuth2, | ||||||
| 				Cfg:  &ldap.Source{}, | 				Cfg:  &ldap.Source{}, | ||||||
| 			}, | 			}, | ||||||
| 			errMsg: "Invalid authentication type. expected: LDAP (via BindDN), actual: OAuth2", | 			errMsg: "Invalid authentication type. expected: LDAP (via BindDN), actual: OAuth2", | ||||||
| @@ -862,28 +862,28 @@ func TestUpdateLdapBindDn(t *testing.T) { | |||||||
|  |  | ||||||
| 	for n, c := range cases { | 	for n, c := range cases { | ||||||
| 		// Mock functions. | 		// Mock functions. | ||||||
| 		var updatedLoginSource *login.Source | 		var updatedAuthSource *auth.Source | ||||||
| 		service := &authService{ | 		service := &authService{ | ||||||
| 			initDB: func(context.Context) error { | 			initDB: func(context.Context) error { | ||||||
| 				return nil | 				return nil | ||||||
| 			}, | 			}, | ||||||
| 			createLoginSource: func(loginSource *login.Source) error { | 			createAuthSource: func(authSource *auth.Source) error { | ||||||
| 				assert.FailNow(t, "case %d: should not call createLoginSource", n) | 				assert.FailNow(t, "case %d: should not call createAuthSource", n) | ||||||
| 				return nil | 				return nil | ||||||
| 			}, | 			}, | ||||||
| 			updateLoginSource: func(loginSource *login.Source) error { | 			updateAuthSource: func(authSource *auth.Source) error { | ||||||
| 				updatedLoginSource = loginSource | 				updatedAuthSource = authSource | ||||||
| 				return nil | 				return nil | ||||||
| 			}, | 			}, | ||||||
| 			getLoginSourceByID: func(id int64) (*login.Source, error) { | 			getAuthSourceByID: func(id int64) (*auth.Source, error) { | ||||||
| 				if c.id != 0 { | 				if c.id != 0 { | ||||||
| 					assert.Equal(t, c.id, id, "case %d: wrong id", n) | 					assert.Equal(t, c.id, id, "case %d: wrong id", n) | ||||||
| 				} | 				} | ||||||
| 				if c.existingLoginSource != nil { | 				if c.existingAuthSource != nil { | ||||||
| 					return c.existingLoginSource, nil | 					return c.existingAuthSource, nil | ||||||
| 				} | 				} | ||||||
| 				return &login.Source{ | 				return &auth.Source{ | ||||||
| 					Type: login.LDAP, | 					Type: auth.LDAP, | ||||||
| 					Cfg:  &ldap.Source{}, | 					Cfg:  &ldap.Source{}, | ||||||
| 				}, nil | 				}, nil | ||||||
| 			}, | 			}, | ||||||
| @@ -900,7 +900,7 @@ func TestUpdateLdapBindDn(t *testing.T) { | |||||||
| 			assert.EqualError(t, err, c.errMsg, "case %d: error should match", n) | 			assert.EqualError(t, err, c.errMsg, "case %d: error should match", n) | ||||||
| 		} else { | 		} else { | ||||||
| 			assert.NoError(t, err, "case %d: should have no errors", n) | 			assert.NoError(t, err, "case %d: should have no errors", n) | ||||||
| 			assert.Equal(t, c.loginSource, updatedLoginSource, "case %d: wrong loginSource", n) | 			assert.Equal(t, c.authSource, updatedAuthSource, "case %d: wrong authSource", n) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @@ -913,11 +913,11 @@ func TestUpdateLdapSimpleAuth(t *testing.T) { | |||||||
|  |  | ||||||
| 	// Test cases | 	// Test cases | ||||||
| 	var cases = []struct { | 	var cases = []struct { | ||||||
| 		args                []string | 		args               []string | ||||||
| 		id                  int64 | 		id                 int64 | ||||||
| 		existingLoginSource *login.Source | 		existingAuthSource *auth.Source | ||||||
| 		loginSource         *login.Source | 		authSource         *auth.Source | ||||||
| 		errMsg              string | 		errMsg             string | ||||||
| 	}{ | 	}{ | ||||||
| 		// case 0 | 		// case 0 | ||||||
| 		{ | 		{ | ||||||
| @@ -943,8 +943,8 @@ func TestUpdateLdapSimpleAuth(t *testing.T) { | |||||||
| 				"--user-dn", "cn=%s,ou=Users,dc=full-domain-simple,dc=org", | 				"--user-dn", "cn=%s,ou=Users,dc=full-domain-simple,dc=org", | ||||||
| 			}, | 			}, | ||||||
| 			id: 7, | 			id: 7, | ||||||
| 			loginSource: &login.Source{ | 			authSource: &auth.Source{ | ||||||
| 				Type:     login.DLDAP, | 				Type:     auth.DLDAP, | ||||||
| 				Name:     "ldap (simple auth) source full", | 				Name:     "ldap (simple auth) source full", | ||||||
| 				IsActive: false, | 				IsActive: false, | ||||||
| 				Cfg: &ldap.Source{ | 				Cfg: &ldap.Source{ | ||||||
| @@ -973,8 +973,8 @@ func TestUpdateLdapSimpleAuth(t *testing.T) { | |||||||
| 				"ldap-test", | 				"ldap-test", | ||||||
| 				"--id", "1", | 				"--id", "1", | ||||||
| 			}, | 			}, | ||||||
| 			loginSource: &login.Source{ | 			authSource: &auth.Source{ | ||||||
| 				Type: login.DLDAP, | 				Type: auth.DLDAP, | ||||||
| 				Cfg:  &ldap.Source{}, | 				Cfg:  &ldap.Source{}, | ||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| @@ -985,8 +985,8 @@ func TestUpdateLdapSimpleAuth(t *testing.T) { | |||||||
| 				"--id", "1", | 				"--id", "1", | ||||||
| 				"--name", "ldap (simple auth) source", | 				"--name", "ldap (simple auth) source", | ||||||
| 			}, | 			}, | ||||||
| 			loginSource: &login.Source{ | 			authSource: &auth.Source{ | ||||||
| 				Type: login.DLDAP, | 				Type: auth.DLDAP, | ||||||
| 				Name: "ldap (simple auth) source", | 				Name: "ldap (simple auth) source", | ||||||
| 				Cfg: &ldap.Source{ | 				Cfg: &ldap.Source{ | ||||||
| 					Name: "ldap (simple auth) source", | 					Name: "ldap (simple auth) source", | ||||||
| @@ -1000,13 +1000,13 @@ func TestUpdateLdapSimpleAuth(t *testing.T) { | |||||||
| 				"--id", "1", | 				"--id", "1", | ||||||
| 				"--not-active", | 				"--not-active", | ||||||
| 			}, | 			}, | ||||||
| 			existingLoginSource: &login.Source{ | 			existingAuthSource: &auth.Source{ | ||||||
| 				Type:     login.DLDAP, | 				Type:     auth.DLDAP, | ||||||
| 				IsActive: true, | 				IsActive: true, | ||||||
| 				Cfg:      &ldap.Source{}, | 				Cfg:      &ldap.Source{}, | ||||||
| 			}, | 			}, | ||||||
| 			loginSource: &login.Source{ | 			authSource: &auth.Source{ | ||||||
| 				Type:     login.DLDAP, | 				Type:     auth.DLDAP, | ||||||
| 				IsActive: false, | 				IsActive: false, | ||||||
| 				Cfg:      &ldap.Source{}, | 				Cfg:      &ldap.Source{}, | ||||||
| 			}, | 			}, | ||||||
| @@ -1018,8 +1018,8 @@ func TestUpdateLdapSimpleAuth(t *testing.T) { | |||||||
| 				"--id", "1", | 				"--id", "1", | ||||||
| 				"--security-protocol", "starttls", | 				"--security-protocol", "starttls", | ||||||
| 			}, | 			}, | ||||||
| 			loginSource: &login.Source{ | 			authSource: &auth.Source{ | ||||||
| 				Type: login.DLDAP, | 				Type: auth.DLDAP, | ||||||
| 				Cfg: &ldap.Source{ | 				Cfg: &ldap.Source{ | ||||||
| 					SecurityProtocol: ldap.SecurityProtocol(2), | 					SecurityProtocol: ldap.SecurityProtocol(2), | ||||||
| 				}, | 				}, | ||||||
| @@ -1032,8 +1032,8 @@ func TestUpdateLdapSimpleAuth(t *testing.T) { | |||||||
| 				"--id", "1", | 				"--id", "1", | ||||||
| 				"--skip-tls-verify", | 				"--skip-tls-verify", | ||||||
| 			}, | 			}, | ||||||
| 			loginSource: &login.Source{ | 			authSource: &auth.Source{ | ||||||
| 				Type: login.DLDAP, | 				Type: auth.DLDAP, | ||||||
| 				Cfg: &ldap.Source{ | 				Cfg: &ldap.Source{ | ||||||
| 					SkipVerify: true, | 					SkipVerify: true, | ||||||
| 				}, | 				}, | ||||||
| @@ -1046,8 +1046,8 @@ func TestUpdateLdapSimpleAuth(t *testing.T) { | |||||||
| 				"--id", "1", | 				"--id", "1", | ||||||
| 				"--host", "ldap-server", | 				"--host", "ldap-server", | ||||||
| 			}, | 			}, | ||||||
| 			loginSource: &login.Source{ | 			authSource: &auth.Source{ | ||||||
| 				Type: login.DLDAP, | 				Type: auth.DLDAP, | ||||||
| 				Cfg: &ldap.Source{ | 				Cfg: &ldap.Source{ | ||||||
| 					Host: "ldap-server", | 					Host: "ldap-server", | ||||||
| 				}, | 				}, | ||||||
| @@ -1060,8 +1060,8 @@ func TestUpdateLdapSimpleAuth(t *testing.T) { | |||||||
| 				"--id", "1", | 				"--id", "1", | ||||||
| 				"--port", "987", | 				"--port", "987", | ||||||
| 			}, | 			}, | ||||||
| 			loginSource: &login.Source{ | 			authSource: &auth.Source{ | ||||||
| 				Type: login.DLDAP, | 				Type: auth.DLDAP, | ||||||
| 				Cfg: &ldap.Source{ | 				Cfg: &ldap.Source{ | ||||||
| 					Port: 987, | 					Port: 987, | ||||||
| 				}, | 				}, | ||||||
| @@ -1074,8 +1074,8 @@ func TestUpdateLdapSimpleAuth(t *testing.T) { | |||||||
| 				"--id", "1", | 				"--id", "1", | ||||||
| 				"--user-search-base", "ou=Users,dc=domain,dc=org", | 				"--user-search-base", "ou=Users,dc=domain,dc=org", | ||||||
| 			}, | 			}, | ||||||
| 			loginSource: &login.Source{ | 			authSource: &auth.Source{ | ||||||
| 				Type: login.DLDAP, | 				Type: auth.DLDAP, | ||||||
| 				Cfg: &ldap.Source{ | 				Cfg: &ldap.Source{ | ||||||
| 					UserBase: "ou=Users,dc=domain,dc=org", | 					UserBase: "ou=Users,dc=domain,dc=org", | ||||||
| 				}, | 				}, | ||||||
| @@ -1088,8 +1088,8 @@ func TestUpdateLdapSimpleAuth(t *testing.T) { | |||||||
| 				"--id", "1", | 				"--id", "1", | ||||||
| 				"--user-filter", "(&(objectClass=posixAccount)(cn=%s))", | 				"--user-filter", "(&(objectClass=posixAccount)(cn=%s))", | ||||||
| 			}, | 			}, | ||||||
| 			loginSource: &login.Source{ | 			authSource: &auth.Source{ | ||||||
| 				Type: login.DLDAP, | 				Type: auth.DLDAP, | ||||||
| 				Cfg: &ldap.Source{ | 				Cfg: &ldap.Source{ | ||||||
| 					Filter: "(&(objectClass=posixAccount)(cn=%s))", | 					Filter: "(&(objectClass=posixAccount)(cn=%s))", | ||||||
| 				}, | 				}, | ||||||
| @@ -1102,8 +1102,8 @@ func TestUpdateLdapSimpleAuth(t *testing.T) { | |||||||
| 				"--id", "1", | 				"--id", "1", | ||||||
| 				"--admin-filter", "(memberOf=cn=admin-group,ou=example,dc=domain,dc=org)", | 				"--admin-filter", "(memberOf=cn=admin-group,ou=example,dc=domain,dc=org)", | ||||||
| 			}, | 			}, | ||||||
| 			loginSource: &login.Source{ | 			authSource: &auth.Source{ | ||||||
| 				Type: login.DLDAP, | 				Type: auth.DLDAP, | ||||||
| 				Cfg: &ldap.Source{ | 				Cfg: &ldap.Source{ | ||||||
| 					AdminFilter: "(memberOf=cn=admin-group,ou=example,dc=domain,dc=org)", | 					AdminFilter: "(memberOf=cn=admin-group,ou=example,dc=domain,dc=org)", | ||||||
| 				}, | 				}, | ||||||
| @@ -1116,8 +1116,8 @@ func TestUpdateLdapSimpleAuth(t *testing.T) { | |||||||
| 				"--id", "1", | 				"--id", "1", | ||||||
| 				"--username-attribute", "uid", | 				"--username-attribute", "uid", | ||||||
| 			}, | 			}, | ||||||
| 			loginSource: &login.Source{ | 			authSource: &auth.Source{ | ||||||
| 				Type: login.DLDAP, | 				Type: auth.DLDAP, | ||||||
| 				Cfg: &ldap.Source{ | 				Cfg: &ldap.Source{ | ||||||
| 					AttributeUsername: "uid", | 					AttributeUsername: "uid", | ||||||
| 				}, | 				}, | ||||||
| @@ -1130,8 +1130,8 @@ func TestUpdateLdapSimpleAuth(t *testing.T) { | |||||||
| 				"--id", "1", | 				"--id", "1", | ||||||
| 				"--firstname-attribute", "givenName", | 				"--firstname-attribute", "givenName", | ||||||
| 			}, | 			}, | ||||||
| 			loginSource: &login.Source{ | 			authSource: &auth.Source{ | ||||||
| 				Type: login.DLDAP, | 				Type: auth.DLDAP, | ||||||
| 				Cfg: &ldap.Source{ | 				Cfg: &ldap.Source{ | ||||||
| 					AttributeName: "givenName", | 					AttributeName: "givenName", | ||||||
| 				}, | 				}, | ||||||
| @@ -1144,8 +1144,8 @@ func TestUpdateLdapSimpleAuth(t *testing.T) { | |||||||
| 				"--id", "1", | 				"--id", "1", | ||||||
| 				"--surname-attribute", "sn", | 				"--surname-attribute", "sn", | ||||||
| 			}, | 			}, | ||||||
| 			loginSource: &login.Source{ | 			authSource: &auth.Source{ | ||||||
| 				Type: login.DLDAP, | 				Type: auth.DLDAP, | ||||||
| 				Cfg: &ldap.Source{ | 				Cfg: &ldap.Source{ | ||||||
| 					AttributeSurname: "sn", | 					AttributeSurname: "sn", | ||||||
| 				}, | 				}, | ||||||
| @@ -1158,8 +1158,8 @@ func TestUpdateLdapSimpleAuth(t *testing.T) { | |||||||
| 				"--id", "1", | 				"--id", "1", | ||||||
| 				"--email-attribute", "mail", | 				"--email-attribute", "mail", | ||||||
| 			}, | 			}, | ||||||
| 			loginSource: &login.Source{ | 			authSource: &auth.Source{ | ||||||
| 				Type: login.DLDAP, | 				Type: auth.DLDAP, | ||||||
| 				Cfg: &ldap.Source{ | 				Cfg: &ldap.Source{ | ||||||
|  |  | ||||||
| 					AttributeMail: "mail", | 					AttributeMail: "mail", | ||||||
| @@ -1173,8 +1173,8 @@ func TestUpdateLdapSimpleAuth(t *testing.T) { | |||||||
| 				"--id", "1", | 				"--id", "1", | ||||||
| 				"--public-ssh-key-attribute", "publickey", | 				"--public-ssh-key-attribute", "publickey", | ||||||
| 			}, | 			}, | ||||||
| 			loginSource: &login.Source{ | 			authSource: &auth.Source{ | ||||||
| 				Type: login.DLDAP, | 				Type: auth.DLDAP, | ||||||
| 				Cfg: &ldap.Source{ | 				Cfg: &ldap.Source{ | ||||||
| 					AttributeSSHPublicKey: "publickey", | 					AttributeSSHPublicKey: "publickey", | ||||||
| 				}, | 				}, | ||||||
| @@ -1187,8 +1187,8 @@ func TestUpdateLdapSimpleAuth(t *testing.T) { | |||||||
| 				"--id", "1", | 				"--id", "1", | ||||||
| 				"--user-dn", "cn=%s,ou=Users,dc=domain,dc=org", | 				"--user-dn", "cn=%s,ou=Users,dc=domain,dc=org", | ||||||
| 			}, | 			}, | ||||||
| 			loginSource: &login.Source{ | 			authSource: &auth.Source{ | ||||||
| 				Type: login.DLDAP, | 				Type: auth.DLDAP, | ||||||
| 				Cfg: &ldap.Source{ | 				Cfg: &ldap.Source{ | ||||||
| 					UserDN: "cn=%s,ou=Users,dc=domain,dc=org", | 					UserDN: "cn=%s,ou=Users,dc=domain,dc=org", | ||||||
| 				}, | 				}, | ||||||
| @@ -1216,8 +1216,8 @@ func TestUpdateLdapSimpleAuth(t *testing.T) { | |||||||
| 				"ldap-test", | 				"ldap-test", | ||||||
| 				"--id", "1", | 				"--id", "1", | ||||||
| 			}, | 			}, | ||||||
| 			existingLoginSource: &login.Source{ | 			existingAuthSource: &auth.Source{ | ||||||
| 				Type: login.PAM, | 				Type: auth.PAM, | ||||||
| 				Cfg:  &ldap.Source{}, | 				Cfg:  &ldap.Source{}, | ||||||
| 			}, | 			}, | ||||||
| 			errMsg: "Invalid authentication type. expected: LDAP (simple auth), actual: PAM", | 			errMsg: "Invalid authentication type. expected: LDAP (simple auth), actual: PAM", | ||||||
| @@ -1226,28 +1226,28 @@ func TestUpdateLdapSimpleAuth(t *testing.T) { | |||||||
|  |  | ||||||
| 	for n, c := range cases { | 	for n, c := range cases { | ||||||
| 		// Mock functions. | 		// Mock functions. | ||||||
| 		var updatedLoginSource *login.Source | 		var updatedAuthSource *auth.Source | ||||||
| 		service := &authService{ | 		service := &authService{ | ||||||
| 			initDB: func(context.Context) error { | 			initDB: func(context.Context) error { | ||||||
| 				return nil | 				return nil | ||||||
| 			}, | 			}, | ||||||
| 			createLoginSource: func(loginSource *login.Source) error { | 			createAuthSource: func(authSource *auth.Source) error { | ||||||
| 				assert.FailNow(t, "case %d: should not call createLoginSource", n) | 				assert.FailNow(t, "case %d: should not call createAuthSource", n) | ||||||
| 				return nil | 				return nil | ||||||
| 			}, | 			}, | ||||||
| 			updateLoginSource: func(loginSource *login.Source) error { | 			updateAuthSource: func(authSource *auth.Source) error { | ||||||
| 				updatedLoginSource = loginSource | 				updatedAuthSource = authSource | ||||||
| 				return nil | 				return nil | ||||||
| 			}, | 			}, | ||||||
| 			getLoginSourceByID: func(id int64) (*login.Source, error) { | 			getAuthSourceByID: func(id int64) (*auth.Source, error) { | ||||||
| 				if c.id != 0 { | 				if c.id != 0 { | ||||||
| 					assert.Equal(t, c.id, id, "case %d: wrong id", n) | 					assert.Equal(t, c.id, id, "case %d: wrong id", n) | ||||||
| 				} | 				} | ||||||
| 				if c.existingLoginSource != nil { | 				if c.existingAuthSource != nil { | ||||||
| 					return c.existingLoginSource, nil | 					return c.existingAuthSource, nil | ||||||
| 				} | 				} | ||||||
| 				return &login.Source{ | 				return &auth.Source{ | ||||||
| 					Type: login.DLDAP, | 					Type: auth.DLDAP, | ||||||
| 					Cfg:  &ldap.Source{}, | 					Cfg:  &ldap.Source{}, | ||||||
| 				}, nil | 				}, nil | ||||||
| 			}, | 			}, | ||||||
| @@ -1264,7 +1264,7 @@ func TestUpdateLdapSimpleAuth(t *testing.T) { | |||||||
| 			assert.EqualError(t, err, c.errMsg, "case %d: error should match", n) | 			assert.EqualError(t, err, c.errMsg, "case %d: error should match", n) | ||||||
| 		} else { | 		} else { | ||||||
| 			assert.NoError(t, err, "case %d: should have no errors", n) | 			assert.NoError(t, err, "case %d: should have no errors", n) | ||||||
| 			assert.Equal(t, c.loginSource, updatedLoginSource, "case %d: wrong loginSource", n) | 			assert.Equal(t, c.authSource, updatedAuthSource, "case %d: wrong authSource", n) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
| @@ -9,7 +9,7 @@ import ( | |||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models/login" | 	"code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	api "code.gitea.io/gitea/modules/structs" | 	api "code.gitea.io/gitea/modules/structs" | ||||||
| @@ -47,7 +47,7 @@ func testAPICreateOAuth2Application(t *testing.T) { | |||||||
| 	assert.Len(t, createdApp.ClientID, 36) | 	assert.Len(t, createdApp.ClientID, 36) | ||||||
| 	assert.NotEmpty(t, createdApp.Created) | 	assert.NotEmpty(t, createdApp.Created) | ||||||
| 	assert.EqualValues(t, appBody.RedirectURIs[0], createdApp.RedirectURIs[0]) | 	assert.EqualValues(t, appBody.RedirectURIs[0], createdApp.RedirectURIs[0]) | ||||||
| 	unittest.AssertExistsAndLoadBean(t, &login.OAuth2Application{UID: user.ID, Name: createdApp.Name}) | 	unittest.AssertExistsAndLoadBean(t, &auth.OAuth2Application{UID: user.ID, Name: createdApp.Name}) | ||||||
| } | } | ||||||
|  |  | ||||||
| func testAPIListOAuth2Applications(t *testing.T) { | func testAPIListOAuth2Applications(t *testing.T) { | ||||||
| @@ -55,13 +55,13 @@ func testAPIListOAuth2Applications(t *testing.T) { | |||||||
| 	session := loginUser(t, user.Name) | 	session := loginUser(t, user.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session) | ||||||
|  |  | ||||||
| 	existApp := unittest.AssertExistsAndLoadBean(t, &login.OAuth2Application{ | 	existApp := unittest.AssertExistsAndLoadBean(t, &auth.OAuth2Application{ | ||||||
| 		UID:  user.ID, | 		UID:  user.ID, | ||||||
| 		Name: "test-app-1", | 		Name: "test-app-1", | ||||||
| 		RedirectURIs: []string{ | 		RedirectURIs: []string{ | ||||||
| 			"http://www.google.com", | 			"http://www.google.com", | ||||||
| 		}, | 		}, | ||||||
| 	}).(*login.OAuth2Application) | 	}).(*auth.OAuth2Application) | ||||||
|  |  | ||||||
| 	urlStr := fmt.Sprintf("/api/v1/user/applications/oauth2?token=%s", token) | 	urlStr := fmt.Sprintf("/api/v1/user/applications/oauth2?token=%s", token) | ||||||
| 	req := NewRequest(t, "GET", urlStr) | 	req := NewRequest(t, "GET", urlStr) | ||||||
| @@ -76,7 +76,7 @@ func testAPIListOAuth2Applications(t *testing.T) { | |||||||
| 	assert.Len(t, expectedApp.ClientID, 36) | 	assert.Len(t, expectedApp.ClientID, 36) | ||||||
| 	assert.Empty(t, expectedApp.ClientSecret) | 	assert.Empty(t, expectedApp.ClientSecret) | ||||||
| 	assert.EqualValues(t, existApp.RedirectURIs[0], expectedApp.RedirectURIs[0]) | 	assert.EqualValues(t, existApp.RedirectURIs[0], expectedApp.RedirectURIs[0]) | ||||||
| 	unittest.AssertExistsAndLoadBean(t, &login.OAuth2Application{ID: expectedApp.ID, Name: expectedApp.Name}) | 	unittest.AssertExistsAndLoadBean(t, &auth.OAuth2Application{ID: expectedApp.ID, Name: expectedApp.Name}) | ||||||
| } | } | ||||||
|  |  | ||||||
| func testAPIDeleteOAuth2Application(t *testing.T) { | func testAPIDeleteOAuth2Application(t *testing.T) { | ||||||
| @@ -84,16 +84,16 @@ func testAPIDeleteOAuth2Application(t *testing.T) { | |||||||
| 	session := loginUser(t, user.Name) | 	session := loginUser(t, user.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session) | ||||||
|  |  | ||||||
| 	oldApp := unittest.AssertExistsAndLoadBean(t, &login.OAuth2Application{ | 	oldApp := unittest.AssertExistsAndLoadBean(t, &auth.OAuth2Application{ | ||||||
| 		UID:  user.ID, | 		UID:  user.ID, | ||||||
| 		Name: "test-app-1", | 		Name: "test-app-1", | ||||||
| 	}).(*login.OAuth2Application) | 	}).(*auth.OAuth2Application) | ||||||
|  |  | ||||||
| 	urlStr := fmt.Sprintf("/api/v1/user/applications/oauth2/%d?token=%s", oldApp.ID, token) | 	urlStr := fmt.Sprintf("/api/v1/user/applications/oauth2/%d?token=%s", oldApp.ID, token) | ||||||
| 	req := NewRequest(t, "DELETE", urlStr) | 	req := NewRequest(t, "DELETE", urlStr) | ||||||
| 	session.MakeRequest(t, req, http.StatusNoContent) | 	session.MakeRequest(t, req, http.StatusNoContent) | ||||||
|  |  | ||||||
| 	unittest.AssertNotExistsBean(t, &login.OAuth2Application{UID: oldApp.UID, Name: oldApp.Name}) | 	unittest.AssertNotExistsBean(t, &auth.OAuth2Application{UID: oldApp.UID, Name: oldApp.Name}) | ||||||
|  |  | ||||||
| 	// Delete again will return not found | 	// Delete again will return not found | ||||||
| 	req = NewRequest(t, "DELETE", urlStr) | 	req = NewRequest(t, "DELETE", urlStr) | ||||||
| @@ -105,13 +105,13 @@ func testAPIGetOAuth2Application(t *testing.T) { | |||||||
| 	session := loginUser(t, user.Name) | 	session := loginUser(t, user.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session) | ||||||
|  |  | ||||||
| 	existApp := unittest.AssertExistsAndLoadBean(t, &login.OAuth2Application{ | 	existApp := unittest.AssertExistsAndLoadBean(t, &auth.OAuth2Application{ | ||||||
| 		UID:  user.ID, | 		UID:  user.ID, | ||||||
| 		Name: "test-app-1", | 		Name: "test-app-1", | ||||||
| 		RedirectURIs: []string{ | 		RedirectURIs: []string{ | ||||||
| 			"http://www.google.com", | 			"http://www.google.com", | ||||||
| 		}, | 		}, | ||||||
| 	}).(*login.OAuth2Application) | 	}).(*auth.OAuth2Application) | ||||||
|  |  | ||||||
| 	urlStr := fmt.Sprintf("/api/v1/user/applications/oauth2/%d?token=%s", existApp.ID, token) | 	urlStr := fmt.Sprintf("/api/v1/user/applications/oauth2/%d?token=%s", existApp.ID, token) | ||||||
| 	req := NewRequest(t, "GET", urlStr) | 	req := NewRequest(t, "GET", urlStr) | ||||||
| @@ -127,19 +127,19 @@ func testAPIGetOAuth2Application(t *testing.T) { | |||||||
| 	assert.Empty(t, expectedApp.ClientSecret) | 	assert.Empty(t, expectedApp.ClientSecret) | ||||||
| 	assert.Len(t, expectedApp.RedirectURIs, 1) | 	assert.Len(t, expectedApp.RedirectURIs, 1) | ||||||
| 	assert.EqualValues(t, existApp.RedirectURIs[0], expectedApp.RedirectURIs[0]) | 	assert.EqualValues(t, existApp.RedirectURIs[0], expectedApp.RedirectURIs[0]) | ||||||
| 	unittest.AssertExistsAndLoadBean(t, &login.OAuth2Application{ID: expectedApp.ID, Name: expectedApp.Name}) | 	unittest.AssertExistsAndLoadBean(t, &auth.OAuth2Application{ID: expectedApp.ID, Name: expectedApp.Name}) | ||||||
| } | } | ||||||
|  |  | ||||||
| func testAPIUpdateOAuth2Application(t *testing.T) { | func testAPIUpdateOAuth2Application(t *testing.T) { | ||||||
| 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) | 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) | ||||||
|  |  | ||||||
| 	existApp := unittest.AssertExistsAndLoadBean(t, &login.OAuth2Application{ | 	existApp := unittest.AssertExistsAndLoadBean(t, &auth.OAuth2Application{ | ||||||
| 		UID:  user.ID, | 		UID:  user.ID, | ||||||
| 		Name: "test-app-1", | 		Name: "test-app-1", | ||||||
| 		RedirectURIs: []string{ | 		RedirectURIs: []string{ | ||||||
| 			"http://www.google.com", | 			"http://www.google.com", | ||||||
| 		}, | 		}, | ||||||
| 	}).(*login.OAuth2Application) | 	}).(*auth.OAuth2Application) | ||||||
|  |  | ||||||
| 	appBody := api.CreateOAuth2ApplicationOptions{ | 	appBody := api.CreateOAuth2ApplicationOptions{ | ||||||
| 		Name: "test-app-1", | 		Name: "test-app-1", | ||||||
| @@ -161,5 +161,5 @@ func testAPIUpdateOAuth2Application(t *testing.T) { | |||||||
| 	assert.Len(t, expectedApp.RedirectURIs, 2) | 	assert.Len(t, expectedApp.RedirectURIs, 2) | ||||||
| 	assert.EqualValues(t, expectedApp.RedirectURIs[0], appBody.RedirectURIs[0]) | 	assert.EqualValues(t, expectedApp.RedirectURIs[0], appBody.RedirectURIs[0]) | ||||||
| 	assert.EqualValues(t, expectedApp.RedirectURIs[1], appBody.RedirectURIs[1]) | 	assert.EqualValues(t, expectedApp.RedirectURIs[1], appBody.RedirectURIs[1]) | ||||||
| 	unittest.AssertExistsAndLoadBean(t, &login.OAuth2Application{ID: expectedApp.ID, Name: expectedApp.Name}) | 	unittest.AssertExistsAndLoadBean(t, &auth.OAuth2Application{ID: expectedApp.ID, Name: expectedApp.Name}) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -11,8 +11,8 @@ import ( | |||||||
| 	"strings" | 	"strings" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
|  | 	"code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	"code.gitea.io/gitea/models/login" |  | ||||||
| 	"code.gitea.io/gitea/models/perm" | 	"code.gitea.io/gitea/models/perm" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| @@ -92,7 +92,7 @@ func addKey(e db.Engine, key *PublicKey) (err error) { | |||||||
| } | } | ||||||
|  |  | ||||||
| // AddPublicKey adds new public key to database and authorized_keys file. | // AddPublicKey adds new public key to database and authorized_keys file. | ||||||
| func AddPublicKey(ownerID int64, name, content string, loginSourceID int64) (*PublicKey, error) { | func AddPublicKey(ownerID int64, name, content string, authSourceID int64) (*PublicKey, error) { | ||||||
| 	log.Trace(content) | 	log.Trace(content) | ||||||
|  |  | ||||||
| 	fingerprint, err := calcFingerprint(content) | 	fingerprint, err := calcFingerprint(content) | ||||||
| @@ -128,7 +128,7 @@ func AddPublicKey(ownerID int64, name, content string, loginSourceID int64) (*Pu | |||||||
| 		Content:       content, | 		Content:       content, | ||||||
| 		Mode:          perm.AccessModeWrite, | 		Mode:          perm.AccessModeWrite, | ||||||
| 		Type:          KeyTypeUser, | 		Type:          KeyTypeUser, | ||||||
| 		LoginSourceID: loginSourceID, | 		LoginSourceID: authSourceID, | ||||||
| 	} | 	} | ||||||
| 	if err = addKey(sess, key); err != nil { | 	if err = addKey(sess, key); err != nil { | ||||||
| 		return nil, fmt.Errorf("addKey: %v", err) | 		return nil, fmt.Errorf("addKey: %v", err) | ||||||
| @@ -223,10 +223,10 @@ func CountPublicKeys(userID int64) (int64, error) { | |||||||
| } | } | ||||||
|  |  | ||||||
| // ListPublicKeysBySource returns a list of synchronized public keys for a given user and login source. | // ListPublicKeysBySource returns a list of synchronized public keys for a given user and login source. | ||||||
| func ListPublicKeysBySource(uid, loginSourceID int64) ([]*PublicKey, error) { | func ListPublicKeysBySource(uid, authSourceID int64) ([]*PublicKey, error) { | ||||||
| 	keys := make([]*PublicKey, 0, 5) | 	keys := make([]*PublicKey, 0, 5) | ||||||
| 	return keys, db.GetEngine(db.DefaultContext). | 	return keys, db.GetEngine(db.DefaultContext). | ||||||
| 		Where("owner_id = ? AND login_source_id = ?", uid, loginSourceID). | 		Where("owner_id = ? AND login_source_id = ?", uid, authSourceID). | ||||||
| 		Find(&keys) | 		Find(&keys) | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -261,7 +261,7 @@ func DeletePublicKeys(ctx context.Context, keyIDs ...int64) error { | |||||||
|  |  | ||||||
| // PublicKeysAreExternallyManaged returns whether the provided KeyID represents an externally managed Key | // PublicKeysAreExternallyManaged returns whether the provided KeyID represents an externally managed Key | ||||||
| func PublicKeysAreExternallyManaged(keys []*PublicKey) ([]bool, error) { | func PublicKeysAreExternallyManaged(keys []*PublicKey) ([]bool, error) { | ||||||
| 	sources := make([]*login.Source, 0, 5) | 	sources := make([]*auth.Source, 0, 5) | ||||||
| 	externals := make([]bool, len(keys)) | 	externals := make([]bool, len(keys)) | ||||||
| keyloop: | keyloop: | ||||||
| 	for i, key := range keys { | 	for i, key := range keys { | ||||||
| @@ -270,7 +270,7 @@ keyloop: | |||||||
| 			continue keyloop | 			continue keyloop | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		var source *login.Source | 		var source *auth.Source | ||||||
|  |  | ||||||
| 	sourceloop: | 	sourceloop: | ||||||
| 		for _, s := range sources { | 		for _, s := range sources { | ||||||
| @@ -282,11 +282,11 @@ keyloop: | |||||||
|  |  | ||||||
| 		if source == nil { | 		if source == nil { | ||||||
| 			var err error | 			var err error | ||||||
| 			source, err = login.GetSourceByID(key.LoginSourceID) | 			source, err = auth.GetSourceByID(key.LoginSourceID) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				if login.IsErrSourceNotExist(err) { | 				if auth.IsErrSourceNotExist(err) { | ||||||
| 					externals[i] = false | 					externals[i] = false | ||||||
| 					sources[i] = &login.Source{ | 					sources[i] = &auth.Source{ | ||||||
| 						ID: key.LoginSourceID, | 						ID: key.LoginSourceID, | ||||||
| 					} | 					} | ||||||
| 					continue keyloop | 					continue keyloop | ||||||
| @@ -295,7 +295,7 @@ keyloop: | |||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if sshKeyProvider, ok := source.Cfg.(login.SSHKeyProvider); ok && sshKeyProvider.ProvidesSSHKeys() { | 		if sshKeyProvider, ok := source.Cfg.(auth.SSHKeyProvider); ok && sshKeyProvider.ProvidesSSHKeys() { | ||||||
| 			// Disable setting SSH keys for this user | 			// Disable setting SSH keys for this user | ||||||
| 			externals[i] = true | 			externals[i] = true | ||||||
| 		} | 		} | ||||||
| @@ -313,14 +313,14 @@ func PublicKeyIsExternallyManaged(id int64) (bool, error) { | |||||||
| 	if key.LoginSourceID == 0 { | 	if key.LoginSourceID == 0 { | ||||||
| 		return false, nil | 		return false, nil | ||||||
| 	} | 	} | ||||||
| 	source, err := login.GetSourceByID(key.LoginSourceID) | 	source, err := auth.GetSourceByID(key.LoginSourceID) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		if login.IsErrSourceNotExist(err) { | 		if auth.IsErrSourceNotExist(err) { | ||||||
| 			return false, nil | 			return false, nil | ||||||
| 		} | 		} | ||||||
| 		return false, err | 		return false, err | ||||||
| 	} | 	} | ||||||
| 	if sshKeyProvider, ok := source.Cfg.(login.SSHKeyProvider); ok && sshKeyProvider.ProvidesSSHKeys() { | 	if sshKeyProvider, ok := source.Cfg.(auth.SSHKeyProvider); ok && sshKeyProvider.ProvidesSSHKeys() { | ||||||
| 		// Disable setting SSH keys for this user | 		// Disable setting SSH keys for this user | ||||||
| 		return true, nil | 		return true, nil | ||||||
| 	} | 	} | ||||||
| @@ -360,7 +360,7 @@ func deleteKeysMarkedForDeletion(keys []string) (bool, error) { | |||||||
| } | } | ||||||
|  |  | ||||||
| // AddPublicKeysBySource add a users public keys. Returns true if there are changes. | // AddPublicKeysBySource add a users public keys. Returns true if there are changes. | ||||||
| func AddPublicKeysBySource(usr *user_model.User, s *login.Source, sshPublicKeys []string) bool { | func AddPublicKeysBySource(usr *user_model.User, s *auth.Source, sshPublicKeys []string) bool { | ||||||
| 	var sshKeysNeedUpdate bool | 	var sshKeysNeedUpdate bool | ||||||
| 	for _, sshKey := range sshPublicKeys { | 	for _, sshKey := range sshPublicKeys { | ||||||
| 		var err error | 		var err error | ||||||
| @@ -398,7 +398,7 @@ func AddPublicKeysBySource(usr *user_model.User, s *login.Source, sshPublicKeys | |||||||
| } | } | ||||||
|  |  | ||||||
| // SynchronizePublicKeys updates a users public keys. Returns true if there are changes. | // SynchronizePublicKeys updates a users public keys. Returns true if there are changes. | ||||||
| func SynchronizePublicKeys(usr *user_model.User, s *login.Source, sshPublicKeys []string) bool { | func SynchronizePublicKeys(usr *user_model.User, s *auth.Source, sshPublicKeys []string) bool { | ||||||
| 	var sshKeysNeedUpdate bool | 	var sshKeysNeedUpdate bool | ||||||
|  |  | ||||||
| 	log.Trace("synchronizePublicKeys[%s]: Handling Public SSH Key synchronization for user %s", s.Name, usr.Name) | 	log.Trace("synchronizePublicKeys[%s]: Handling Public SSH Key synchronization for user %s", s.Name, usr.Name) | ||||||
|   | |||||||
| @@ -25,7 +25,7 @@ import ( | |||||||
| // This file contains functions related to principals | // This file contains functions related to principals | ||||||
|  |  | ||||||
| // AddPrincipalKey adds new principal to database and authorized_principals file. | // AddPrincipalKey adds new principal to database and authorized_principals file. | ||||||
| func AddPrincipalKey(ownerID int64, content string, loginSourceID int64) (*PublicKey, error) { | func AddPrincipalKey(ownerID int64, content string, authSourceID int64) (*PublicKey, error) { | ||||||
| 	ctx, committer, err := db.TxContext() | 	ctx, committer, err := db.TxContext() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| @@ -49,7 +49,7 @@ func AddPrincipalKey(ownerID int64, content string, loginSourceID int64) (*Publi | |||||||
| 		Content:       content, | 		Content:       content, | ||||||
| 		Mode:          perm.AccessModeWrite, | 		Mode:          perm.AccessModeWrite, | ||||||
| 		Type:          KeyTypePrincipal, | 		Type:          KeyTypePrincipal, | ||||||
| 		LoginSourceID: loginSourceID, | 		LoginSourceID: authSourceID, | ||||||
| 	} | 	} | ||||||
| 	if err = addPrincipalKey(sess, key); err != nil { | 	if err = addPrincipalKey(sess, key); err != nil { | ||||||
| 		return nil, fmt.Errorf("addKey: %v", err) | 		return nil, fmt.Errorf("addKey: %v", err) | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ | |||||||
| // Use of this source code is governed by a MIT-style | // Use of this source code is governed by a MIT-style | ||||||
| // license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||||
| 
 | 
 | ||||||
| package login | package auth | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"path/filepath" | 	"path/filepath" | ||||||
| @@ -2,7 +2,7 @@ | |||||||
| // Use of this source code is governed by a MIT-style | // Use of this source code is governed by a MIT-style | ||||||
| // license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||||
| 
 | 
 | ||||||
| package login | package auth | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"crypto/sha256" | 	"crypto/sha256" | ||||||
| @@ -510,3 +510,55 @@ func revokeOAuth2Grant(e db.Engine, grantID, userID int64) error { | |||||||
| 	_, err := e.Delete(&OAuth2Grant{ID: grantID, UserID: userID}) | 	_, err := e.Delete(&OAuth2Grant{ID: grantID, UserID: userID}) | ||||||
| 	return err | 	return err | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | // ErrOAuthClientIDInvalid will be thrown if client id cannot be found | ||||||
|  | type ErrOAuthClientIDInvalid struct { | ||||||
|  | 	ClientID string | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // IsErrOauthClientIDInvalid checks if an error is a ErrReviewNotExist. | ||||||
|  | func IsErrOauthClientIDInvalid(err error) bool { | ||||||
|  | 	_, ok := err.(ErrOAuthClientIDInvalid) | ||||||
|  | 	return ok | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Error returns the error message | ||||||
|  | func (err ErrOAuthClientIDInvalid) Error() string { | ||||||
|  | 	return fmt.Sprintf("Client ID invalid [Client ID: %s]", err.ClientID) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // ErrOAuthApplicationNotFound will be thrown if id cannot be found | ||||||
|  | type ErrOAuthApplicationNotFound struct { | ||||||
|  | 	ID int64 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // IsErrOAuthApplicationNotFound checks if an error is a ErrReviewNotExist. | ||||||
|  | func IsErrOAuthApplicationNotFound(err error) bool { | ||||||
|  | 	_, ok := err.(ErrOAuthApplicationNotFound) | ||||||
|  | 	return ok | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Error returns the error message | ||||||
|  | func (err ErrOAuthApplicationNotFound) Error() string { | ||||||
|  | 	return fmt.Sprintf("OAuth application not found [ID: %d]", err.ID) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // GetActiveOAuth2ProviderSources returns all actived LoginOAuth2 sources | ||||||
|  | func GetActiveOAuth2ProviderSources() ([]*Source, error) { | ||||||
|  | 	sources := make([]*Source, 0, 1) | ||||||
|  | 	if err := db.GetEngine(db.DefaultContext).Where("is_active = ? and type = ?", true, OAuth2).Find(&sources); err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	return sources, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // GetActiveOAuth2SourceByName returns a OAuth2 AuthSource based on the given name | ||||||
|  | func GetActiveOAuth2SourceByName(name string) (*Source, error) { | ||||||
|  | 	authSource := new(Source) | ||||||
|  | 	has, err := db.GetEngine(db.DefaultContext).Where("name = ? and type = ? and is_active = ?", name, OAuth2, true).Get(authSource) | ||||||
|  | 	if !has || err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return authSource, nil | ||||||
|  | } | ||||||
| @@ -2,7 +2,7 @@ | |||||||
| // Use of this source code is governed by a MIT-style | // Use of this source code is governed by a MIT-style | ||||||
| // license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||||
| 
 | 
 | ||||||
| package login | package auth | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"testing" | 	"testing" | ||||||
| @@ -2,7 +2,7 @@ | |||||||
| // Use of this source code is governed by a MIT-style | // Use of this source code is governed by a MIT-style | ||||||
| // license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||||
| 
 | 
 | ||||||
| package login | package auth | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
| @@ -3,12 +3,11 @@ | |||||||
| // Use of this source code is governed by a MIT-style | // Use of this source code is governed by a MIT-style | ||||||
| // license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||||
| 
 | 
 | ||||||
| package login | package auth | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"reflect" | 	"reflect" | ||||||
| 	"strconv" |  | ||||||
| 
 | 
 | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| @@ -84,10 +83,7 @@ type RegisterableSource interface { | |||||||
| 	UnregisterSource() error | 	UnregisterSource() error | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // SourceSettable configurations can have their loginSource set on them | var registeredConfigs = map[Type]func() Config{} | ||||||
| type SourceSettable interface { |  | ||||||
| 	SetLoginSource(*Source) |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| // RegisterTypeConfig register a config for a provided type | // RegisterTypeConfig register a config for a provided type | ||||||
| func RegisterTypeConfig(typ Type, exemplar Config) { | func RegisterTypeConfig(typ Type, exemplar Config) { | ||||||
| @@ -105,7 +101,10 @@ func RegisterTypeConfig(typ Type, exemplar Config) { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| var registeredConfigs = map[Type]func() Config{} | // SourceSettable configurations can have their authSource set on them | ||||||
|  | type SourceSettable interface { | ||||||
|  | 	SetAuthSource(*Source) | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| // Source represents an external way for authorizing users. | // Source represents an external way for authorizing users. | ||||||
| type Source struct { | type Source struct { | ||||||
| @@ -129,30 +128,17 @@ func init() { | |||||||
| 	db.RegisterModel(new(Source)) | 	db.RegisterModel(new(Source)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Cell2Int64 converts a xorm.Cell type to int64, |  | ||||||
| // and handles possible irregular cases. |  | ||||||
| func Cell2Int64(val xorm.Cell) int64 { |  | ||||||
| 	switch (*val).(type) { |  | ||||||
| 	case []uint8: |  | ||||||
| 		log.Trace("Cell2Int64 ([]uint8): %v", *val) |  | ||||||
| 
 |  | ||||||
| 		v, _ := strconv.ParseInt(string((*val).([]uint8)), 10, 64) |  | ||||||
| 		return v |  | ||||||
| 	} |  | ||||||
| 	return (*val).(int64) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // BeforeSet is invoked from XORM before setting the value of a field of this object. | // BeforeSet is invoked from XORM before setting the value of a field of this object. | ||||||
| func (source *Source) BeforeSet(colName string, val xorm.Cell) { | func (source *Source) BeforeSet(colName string, val xorm.Cell) { | ||||||
| 	if colName == "type" { | 	if colName == "type" { | ||||||
| 		typ := Type(Cell2Int64(val)) | 		typ := Type(db.Cell2Int64(val)) | ||||||
| 		constructor, ok := registeredConfigs[typ] | 		constructor, ok := registeredConfigs[typ] | ||||||
| 		if !ok { | 		if !ok { | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| 		source.Cfg = constructor() | 		source.Cfg = constructor() | ||||||
| 		if settable, ok := source.Cfg.(SourceSettable); ok { | 		if settable, ok := source.Cfg.(SourceSettable); ok { | ||||||
| 			settable.SetLoginSource(source) | 			settable.SetAuthSource(source) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @@ -211,7 +197,7 @@ func (source *Source) SkipVerify() bool { | |||||||
| 	return ok && skipVerifiable.IsSkipVerify() | 	return ok && skipVerifiable.IsSkipVerify() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // CreateSource inserts a LoginSource in the DB if not already | // CreateSource inserts a AuthSource in the DB if not already | ||||||
| // existing with the given name. | // existing with the given name. | ||||||
| func CreateSource(source *Source) error { | func CreateSource(source *Source) error { | ||||||
| 	has, err := db.GetEngine(db.DefaultContext).Where("name=?", source.Name).Exist(new(Source)) | 	has, err := db.GetEngine(db.DefaultContext).Where("name=?", source.Name).Exist(new(Source)) | ||||||
| @@ -235,7 +221,7 @@ func CreateSource(source *Source) error { | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if settable, ok := source.Cfg.(SourceSettable); ok { | 	if settable, ok := source.Cfg.(SourceSettable); ok { | ||||||
| 		settable.SetLoginSource(source) | 		settable.SetAuthSource(source) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	registerableSource, ok := source.Cfg.(RegisterableSource) | 	registerableSource, ok := source.Cfg.(RegisterableSource) | ||||||
| @@ -245,7 +231,7 @@ func CreateSource(source *Source) error { | |||||||
| 
 | 
 | ||||||
| 	err = registerableSource.RegisterSource() | 	err = registerableSource.RegisterSource() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		// remove the LoginSource in case of errors while registering configuration | 		// remove the AuthSource in case of errors while registering configuration | ||||||
| 		if _, err := db.GetEngine(db.DefaultContext).Delete(source); err != nil { | 		if _, err := db.GetEngine(db.DefaultContext).Delete(source); err != nil { | ||||||
| 			log.Error("CreateSource: Error while wrapOpenIDConnectInitializeError: %v", err) | 			log.Error("CreateSource: Error while wrapOpenIDConnectInitializeError: %v", err) | ||||||
| 		} | 		} | ||||||
| @@ -322,11 +308,11 @@ func GetSourceByID(id int64) (*Source, error) { | |||||||
| 
 | 
 | ||||||
| // UpdateSource updates a Source record in DB. | // UpdateSource updates a Source record in DB. | ||||||
| func UpdateSource(source *Source) error { | func UpdateSource(source *Source) error { | ||||||
| 	var originalLoginSource *Source | 	var originalSource *Source | ||||||
| 	if source.IsOAuth2() { | 	if source.IsOAuth2() { | ||||||
| 		// keep track of the original values so we can restore in case of errors while registering OAuth2 providers | 		// keep track of the original values so we can restore in case of errors while registering OAuth2 providers | ||||||
| 		var err error | 		var err error | ||||||
| 		if originalLoginSource, err = GetSourceByID(source.ID); err != nil { | 		if originalSource, err = GetSourceByID(source.ID); err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| @@ -341,7 +327,7 @@ func UpdateSource(source *Source) error { | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if settable, ok := source.Cfg.(SourceSettable); ok { | 	if settable, ok := source.Cfg.(SourceSettable); ok { | ||||||
| 		settable.SetLoginSource(source) | 		settable.SetAuthSource(source) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	registerableSource, ok := source.Cfg.(RegisterableSource) | 	registerableSource, ok := source.Cfg.(RegisterableSource) | ||||||
| @@ -352,7 +338,7 @@ func UpdateSource(source *Source) error { | |||||||
| 	err = registerableSource.RegisterSource() | 	err = registerableSource.RegisterSource() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		// restore original values since we cannot update the provider it self | 		// restore original values since we cannot update the provider it self | ||||||
| 		if _, err := db.GetEngine(db.DefaultContext).ID(source.ID).AllCols().Update(originalLoginSource); err != nil { | 		if _, err := db.GetEngine(db.DefaultContext).ID(source.ID).AllCols().Update(originalSource); err != nil { | ||||||
| 			log.Error("UpdateSource: Error while wrapOpenIDConnectInitializeError: %v", err) | 			log.Error("UpdateSource: Error while wrapOpenIDConnectInitializeError: %v", err) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| @@ -2,7 +2,7 @@ | |||||||
| // Use of this source code is governed by a MIT-style | // Use of this source code is governed by a MIT-style | ||||||
| // license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||||
| 
 | 
 | ||||||
| package login | package auth | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"strings" | 	"strings" | ||||||
| @@ -34,10 +34,10 @@ func (source *TestSource) ToDB() ([]byte, error) { | |||||||
| 	return json.Marshal(source) | 	return json.Marshal(source) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestDumpLoginSource(t *testing.T) { | func TestDumpAuthSource(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 
 | 
 | ||||||
| 	loginSourceSchema, err := db.TableInfo(new(Source)) | 	authSourceSchema, err := db.TableInfo(new(Source)) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 
 | 
 | ||||||
| 	RegisterTypeConfig(OAuth2, new(TestSource)) | 	RegisterTypeConfig(OAuth2, new(TestSource)) | ||||||
| @@ -54,7 +54,7 @@ func TestDumpLoginSource(t *testing.T) { | |||||||
| 
 | 
 | ||||||
| 	sb := new(strings.Builder) | 	sb := new(strings.Builder) | ||||||
| 
 | 
 | ||||||
| 	db.DumpTables([]*schemas.Table{loginSourceSchema}, sb) | 	db.DumpTables([]*schemas.Table{authSourceSchema}, sb) | ||||||
| 
 | 
 | ||||||
| 	assert.Contains(t, sb.String(), `"Provider":"ConvertibleSourceName"`) | 	assert.Contains(t, sb.String(), `"Provider":"ConvertibleSourceName"`) | ||||||
| } | } | ||||||
| @@ -2,7 +2,7 @@ | |||||||
| // Use of this source code is governed by a MIT-style | // Use of this source code is governed by a MIT-style | ||||||
| // license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||||
| 
 | 
 | ||||||
| package login | package auth | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"crypto/md5" | 	"crypto/md5" | ||||||
| @@ -2,7 +2,7 @@ | |||||||
| // Use of this source code is governed by a MIT-style | // Use of this source code is governed by a MIT-style | ||||||
| // license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||||
| 
 | 
 | ||||||
| package login | package auth | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
| @@ -2,7 +2,7 @@ | |||||||
| // Use of this source code is governed by a MIT-style | // Use of this source code is governed by a MIT-style | ||||||
| // license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||||
| 
 | 
 | ||||||
| package login | package auth | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"encoding/hex" | 	"encoding/hex" | ||||||
| @@ -6,9 +6,12 @@ package db | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  | 	"strconv" | ||||||
|  |  | ||||||
|  | 	"code.gitea.io/gitea/modules/log" | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
|  |  | ||||||
|  | 	"xorm.io/xorm" | ||||||
| 	"xorm.io/xorm/schemas" | 	"xorm.io/xorm/schemas" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -39,3 +42,16 @@ func ConvertUtf8ToUtf8mb4() error { | |||||||
|  |  | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Cell2Int64 converts a xorm.Cell type to int64, | ||||||
|  | // and handles possible irregular cases. | ||||||
|  | func Cell2Int64(val xorm.Cell) int64 { | ||||||
|  | 	switch (*val).(type) { | ||||||
|  | 	case []uint8: | ||||||
|  | 		log.Trace("Cell2Int64 ([]uint8): %v", *val) | ||||||
|  |  | ||||||
|  | 		v, _ := strconv.ParseInt(string((*val).([]uint8)), 10, 64) | ||||||
|  | 		return v | ||||||
|  | 	} | ||||||
|  | 	return (*val).(int64) | ||||||
|  | } | ||||||
|   | |||||||
| @@ -1,70 +0,0 @@ | |||||||
| // Copyright 2017 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 login |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"fmt" |  | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models/db" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| //  ________      _____          __  .__ |  | ||||||
| //  \_____  \    /  _  \  __ ___/  |_|  |__ |  | ||||||
| //   /   |   \  /  /_\  \|  |  \   __\  |  \ |  | ||||||
| //  /    |    \/    |    \  |  /|  | |   Y  \ |  | ||||||
| //  \_______  /\____|__  /____/ |__| |___|  / |  | ||||||
| //          \/         \/                 \/ |  | ||||||
|  |  | ||||||
| // ErrOAuthClientIDInvalid will be thrown if client id cannot be found |  | ||||||
| type ErrOAuthClientIDInvalid struct { |  | ||||||
| 	ClientID string |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // IsErrOauthClientIDInvalid checks if an error is a ErrReviewNotExist. |  | ||||||
| func IsErrOauthClientIDInvalid(err error) bool { |  | ||||||
| 	_, ok := err.(ErrOAuthClientIDInvalid) |  | ||||||
| 	return ok |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Error returns the error message |  | ||||||
| func (err ErrOAuthClientIDInvalid) Error() string { |  | ||||||
| 	return fmt.Sprintf("Client ID invalid [Client ID: %s]", err.ClientID) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // ErrOAuthApplicationNotFound will be thrown if id cannot be found |  | ||||||
| type ErrOAuthApplicationNotFound struct { |  | ||||||
| 	ID int64 |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // IsErrOAuthApplicationNotFound checks if an error is a ErrReviewNotExist. |  | ||||||
| func IsErrOAuthApplicationNotFound(err error) bool { |  | ||||||
| 	_, ok := err.(ErrOAuthApplicationNotFound) |  | ||||||
| 	return ok |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Error returns the error message |  | ||||||
| func (err ErrOAuthApplicationNotFound) Error() string { |  | ||||||
| 	return fmt.Sprintf("OAuth application not found [ID: %d]", err.ID) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // GetActiveOAuth2ProviderLoginSources returns all actived LoginOAuth2 sources |  | ||||||
| func GetActiveOAuth2ProviderLoginSources() ([]*Source, error) { |  | ||||||
| 	sources := make([]*Source, 0, 1) |  | ||||||
| 	if err := db.GetEngine(db.DefaultContext).Where("is_active = ? and type = ?", true, OAuth2).Find(&sources); err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 	return sources, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // GetActiveOAuth2LoginSourceByName returns a OAuth2 LoginSource based on the given name |  | ||||||
| func GetActiveOAuth2LoginSourceByName(name string) (*Source, error) { |  | ||||||
| 	loginSource := new(Source) |  | ||||||
| 	has, err := db.GetEngine(db.DefaultContext).Where("name = ? and type = ? and is_active = ?", name, OAuth2, true).Get(loginSource) |  | ||||||
| 	if !has || err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return loginSource, nil |  | ||||||
| } |  | ||||||
| @@ -8,7 +8,6 @@ import ( | |||||||
| 	"fmt" | 	"fmt" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	"code.gitea.io/gitea/models/login" |  | ||||||
| 	"code.gitea.io/gitea/models/unit" | 	"code.gitea.io/gitea/models/unit" | ||||||
| 	"code.gitea.io/gitea/modules/json" | 	"code.gitea.io/gitea/modules/json" | ||||||
| 	"code.gitea.io/gitea/modules/timeutil" | 	"code.gitea.io/gitea/modules/timeutil" | ||||||
| @@ -170,7 +169,7 @@ func (cfg *PullRequestsConfig) AllowedMergeStyleCount() int { | |||||||
| func (r *RepoUnit) BeforeSet(colName string, val xorm.Cell) { | func (r *RepoUnit) BeforeSet(colName string, val xorm.Cell) { | ||||||
| 	switch colName { | 	switch colName { | ||||||
| 	case "type": | 	case "type": | ||||||
| 		switch unit.Type(login.Cell2Int64(val)) { | 		switch unit.Type(db.Cell2Int64(val)) { | ||||||
| 		case unit.TypeCode, unit.TypeReleases, unit.TypeWiki, unit.TypeProjects: | 		case unit.TypeCode, unit.TypeReleases, unit.TypeWiki, unit.TypeProjects: | ||||||
| 			r.Config = new(UnitConfig) | 			r.Config = new(UnitConfig) | ||||||
| 		case unit.TypeExternalWiki: | 		case unit.TypeExternalWiki: | ||||||
|   | |||||||
| @@ -6,8 +6,8 @@ package models | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	asymkey_model "code.gitea.io/gitea/models/asymkey" | 	asymkey_model "code.gitea.io/gitea/models/asymkey" | ||||||
|  | 	"code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	"code.gitea.io/gitea/models/login" |  | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	"code.gitea.io/gitea/models/webhook" | 	"code.gitea.io/gitea/models/webhook" | ||||||
| @@ -21,7 +21,7 @@ type Statistic struct { | |||||||
| 		Repo, Watch, Star, Action, Access, | 		Repo, Watch, Star, Action, Access, | ||||||
| 		Issue, IssueClosed, IssueOpen, | 		Issue, IssueClosed, IssueOpen, | ||||||
| 		Comment, Oauth, Follow, | 		Comment, Oauth, Follow, | ||||||
| 		Mirror, Release, LoginSource, Webhook, | 		Mirror, Release, AuthSource, Webhook, | ||||||
| 		Milestone, Label, HookTask, | 		Milestone, Label, HookTask, | ||||||
| 		Team, UpdateTask, Project, | 		Team, UpdateTask, Project, | ||||||
| 		ProjectBoard, Attachment int64 | 		ProjectBoard, Attachment int64 | ||||||
| @@ -98,7 +98,7 @@ func GetStatistic() (stats Statistic) { | |||||||
| 	stats.Counter.Follow, _ = e.Count(new(user_model.Follow)) | 	stats.Counter.Follow, _ = e.Count(new(user_model.Follow)) | ||||||
| 	stats.Counter.Mirror, _ = e.Count(new(repo_model.Mirror)) | 	stats.Counter.Mirror, _ = e.Count(new(repo_model.Mirror)) | ||||||
| 	stats.Counter.Release, _ = e.Count(new(Release)) | 	stats.Counter.Release, _ = e.Count(new(Release)) | ||||||
| 	stats.Counter.LoginSource = login.CountSources() | 	stats.Counter.AuthSource = auth.CountSources() | ||||||
| 	stats.Counter.Webhook, _ = e.Count(new(webhook.Webhook)) | 	stats.Counter.Webhook, _ = e.Count(new(webhook.Webhook)) | ||||||
| 	stats.Counter.Milestone, _ = e.Count(new(Milestone)) | 	stats.Counter.Milestone, _ = e.Count(new(Milestone)) | ||||||
| 	stats.Counter.Label, _ = e.Count(new(Label)) | 	stats.Counter.Label, _ = e.Count(new(Label)) | ||||||
|   | |||||||
| @@ -10,8 +10,8 @@ import ( | |||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
|  | 	"code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	"code.gitea.io/gitea/models/login" |  | ||||||
| 	"code.gitea.io/gitea/modules/base" | 	"code.gitea.io/gitea/modules/base" | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
| 	"code.gitea.io/gitea/modules/timeutil" | 	"code.gitea.io/gitea/modules/timeutil" | ||||||
| @@ -68,7 +68,7 @@ func NewAccessToken(t *AccessToken) error { | |||||||
| 	} | 	} | ||||||
| 	t.TokenSalt = salt | 	t.TokenSalt = salt | ||||||
| 	t.Token = base.EncodeSha1(gouuid.New().String()) | 	t.Token = base.EncodeSha1(gouuid.New().String()) | ||||||
| 	t.TokenHash = login.HashToken(t.Token, t.TokenSalt) | 	t.TokenHash = auth.HashToken(t.Token, t.TokenSalt) | ||||||
| 	t.TokenLastEight = t.Token[len(t.Token)-8:] | 	t.TokenLastEight = t.Token[len(t.Token)-8:] | ||||||
| 	_, err = db.GetEngine(db.DefaultContext).Insert(t) | 	_, err = db.GetEngine(db.DefaultContext).Insert(t) | ||||||
| 	return err | 	return err | ||||||
| @@ -130,7 +130,7 @@ func GetAccessTokenBySHA(token string) (*AccessToken, error) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	for _, t := range tokens { | 	for _, t := range tokens { | ||||||
| 		tempHash := login.HashToken(token, t.TokenSalt) | 		tempHash := auth.HashToken(token, t.TokenSalt) | ||||||
| 		if subtle.ConstantTimeCompare([]byte(t.TokenHash), []byte(tempHash)) == 1 { | 		if subtle.ConstantTimeCompare([]byte(t.TokenHash), []byte(tempHash)) == 1 { | ||||||
| 			if successfulAccessTokenCache != nil { | 			if successfulAccessTokenCache != nil { | ||||||
| 				successfulAccessTokenCache.Add(token, t.ID) | 				successfulAccessTokenCache.Add(token, t.ID) | ||||||
|   | |||||||
| @@ -7,8 +7,8 @@ package user | |||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  |  | ||||||
|  | 	"code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	"code.gitea.io/gitea/models/login" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // UserList is a list of user. | // UserList is a list of user. | ||||||
| @@ -40,13 +40,13 @@ func (users UserList) GetTwoFaStatus() map[int64]bool { | |||||||
| 	return results | 	return results | ||||||
| } | } | ||||||
|  |  | ||||||
| func (users UserList) loadTwoFactorStatus(e db.Engine) (map[int64]*login.TwoFactor, error) { | func (users UserList) loadTwoFactorStatus(e db.Engine) (map[int64]*auth.TwoFactor, error) { | ||||||
| 	if len(users) == 0 { | 	if len(users) == 0 { | ||||||
| 		return nil, nil | 		return nil, nil | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	userIDs := users.GetUserIDs() | 	userIDs := users.GetUserIDs() | ||||||
| 	tokenMaps := make(map[int64]*login.TwoFactor, len(userIDs)) | 	tokenMaps := make(map[int64]*auth.TwoFactor, len(userIDs)) | ||||||
| 	err := e. | 	err := e. | ||||||
| 		In("uid", userIDs). | 		In("uid", userIDs). | ||||||
| 		Find(&tokenMaps) | 		Find(&tokenMaps) | ||||||
|   | |||||||
| @@ -19,8 +19,8 @@ import ( | |||||||
|  |  | ||||||
| 	_ "image/jpeg" // Needed for jpeg support | 	_ "image/jpeg" // Needed for jpeg support | ||||||
|  |  | ||||||
|  | 	"code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	"code.gitea.io/gitea/models/login" |  | ||||||
| 	"code.gitea.io/gitea/modules/auth/openid" | 	"code.gitea.io/gitea/modules/auth/openid" | ||||||
| 	"code.gitea.io/gitea/modules/base" | 	"code.gitea.io/gitea/modules/base" | ||||||
| 	"code.gitea.io/gitea/modules/git" | 	"code.gitea.io/gitea/modules/git" | ||||||
| @@ -89,7 +89,7 @@ type User struct { | |||||||
| 	// is to change his/her password after registration. | 	// is to change his/her password after registration. | ||||||
| 	MustChangePassword bool `xorm:"NOT NULL DEFAULT false"` | 	MustChangePassword bool `xorm:"NOT NULL DEFAULT false"` | ||||||
|  |  | ||||||
| 	LoginType   login.Type | 	LoginType   auth.Type | ||||||
| 	LoginSource int64 `xorm:"NOT NULL DEFAULT 0"` | 	LoginSource int64 `xorm:"NOT NULL DEFAULT 0"` | ||||||
| 	LoginName   string | 	LoginName   string | ||||||
| 	Type        UserType | 	Type        UserType | ||||||
| @@ -232,12 +232,12 @@ func GetAllUsers() ([]*User, error) { | |||||||
|  |  | ||||||
| // IsLocal returns true if user login type is LoginPlain. | // IsLocal returns true if user login type is LoginPlain. | ||||||
| func (u *User) IsLocal() bool { | func (u *User) IsLocal() bool { | ||||||
| 	return u.LoginType <= login.Plain | 	return u.LoginType <= auth.Plain | ||||||
| } | } | ||||||
|  |  | ||||||
| // IsOAuth2 returns true if user login type is LoginOAuth2. | // IsOAuth2 returns true if user login type is LoginOAuth2. | ||||||
| func (u *User) IsOAuth2() bool { | func (u *User) IsOAuth2() bool { | ||||||
| 	return u.LoginType == login.OAuth2 | 	return u.LoginType == auth.OAuth2 | ||||||
| } | } | ||||||
|  |  | ||||||
| // MaxCreationLimit returns the number of repositories a user is allowed to create | // MaxCreationLimit returns the number of repositories a user is allowed to create | ||||||
| @@ -1012,7 +1012,7 @@ func GetUserIDsByNames(names []string, ignoreNonExistent bool) ([]int64, error) | |||||||
| } | } | ||||||
|  |  | ||||||
| // GetUsersBySource returns a list of Users for a login source | // GetUsersBySource returns a list of Users for a login source | ||||||
| func GetUsersBySource(s *login.Source) ([]*User, error) { | func GetUsersBySource(s *auth.Source) ([]*User, error) { | ||||||
| 	var users []*User | 	var users []*User | ||||||
| 	err := db.GetEngine(db.DefaultContext).Where("login_type = ? AND login_source = ?", s.Type, s.ID).Find(&users) | 	err := db.GetEngine(db.DefaultContext).Where("login_type = ? AND login_source = ?", s.Type, s.ID).Find(&users) | ||||||
| 	return users, err | 	return users, err | ||||||
|   | |||||||
| @@ -9,8 +9,8 @@ import ( | |||||||
| 	"strings" | 	"strings" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	"code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	"code.gitea.io/gitea/models/login" |  | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
| 	"code.gitea.io/gitea/modules/structs" | 	"code.gitea.io/gitea/modules/structs" | ||||||
| @@ -21,7 +21,7 @@ import ( | |||||||
|  |  | ||||||
| func TestOAuth2Application_LoadUser(t *testing.T) { | func TestOAuth2Application_LoadUser(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 	app := unittest.AssertExistsAndLoadBean(t, &login.OAuth2Application{ID: 1}).(*login.OAuth2Application) | 	app := unittest.AssertExistsAndLoadBean(t, &auth.OAuth2Application{ID: 1}).(*auth.OAuth2Application) | ||||||
| 	user, err := GetUserByID(app.UID) | 	user, err := GetUserByID(app.UID) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.NotNil(t, user) | 	assert.NotNil(t, user) | ||||||
|   | |||||||
| @@ -13,13 +13,13 @@ import ( | |||||||
| 	"net/url" | 	"net/url" | ||||||
| 	"strings" | 	"strings" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models/login" | 	"code.gitea.io/gitea/models/auth" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	"code.gitea.io/gitea/modules/git" | 	"code.gitea.io/gitea/modules/git" | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
| 	"code.gitea.io/gitea/modules/web/middleware" | 	"code.gitea.io/gitea/modules/web/middleware" | ||||||
| 	"code.gitea.io/gitea/services/auth" | 	auth_service "code.gitea.io/gitea/services/auth" | ||||||
|  |  | ||||||
| 	"gitea.com/go-chi/session" | 	"gitea.com/go-chi/session" | ||||||
| ) | ) | ||||||
| @@ -225,9 +225,9 @@ func (ctx *APIContext) CheckForOTP() { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	otpHeader := ctx.Req.Header.Get("X-Gitea-OTP") | 	otpHeader := ctx.Req.Header.Get("X-Gitea-OTP") | ||||||
| 	twofa, err := login.GetTwoFactorByUID(ctx.Context.User.ID) | 	twofa, err := auth.GetTwoFactorByUID(ctx.Context.User.ID) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		if login.IsErrTwoFactorNotEnrolled(err) { | 		if auth.IsErrTwoFactorNotEnrolled(err) { | ||||||
| 			return // No 2FA enrollment for this user | 			return // No 2FA enrollment for this user | ||||||
| 		} | 		} | ||||||
| 		ctx.Context.Error(http.StatusInternalServerError) | 		ctx.Context.Error(http.StatusInternalServerError) | ||||||
| @@ -244,8 +244,8 @@ func (ctx *APIContext) CheckForOTP() { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // APIAuth converts auth.Auth as a middleware | // APIAuth converts auth_service.Auth as a middleware | ||||||
| func APIAuth(authMethod auth.Method) func(*APIContext) { | func APIAuth(authMethod auth_service.Method) func(*APIContext) { | ||||||
| 	return func(ctx *APIContext) { | 	return func(ctx *APIContext) { | ||||||
| 		// Get user from session if logged in. | 		// Get user from session if logged in. | ||||||
| 		ctx.User = authMethod.Verify(ctx.Req, ctx.Resp, ctx, ctx.Session) | 		ctx.User = authMethod.Verify(ctx.Req, ctx.Resp, ctx, ctx.Session) | ||||||
| @@ -253,7 +253,7 @@ func APIAuth(authMethod auth.Method) func(*APIContext) { | |||||||
| 			if ctx.Locale.Language() != ctx.User.Language { | 			if ctx.Locale.Language() != ctx.User.Language { | ||||||
| 				ctx.Locale = middleware.Locale(ctx.Resp, ctx.Req) | 				ctx.Locale = middleware.Locale(ctx.Resp, ctx.Req) | ||||||
| 			} | 			} | ||||||
| 			ctx.IsBasicAuth = ctx.Data["AuthedMethod"].(string) == auth.BasicMethodName | 			ctx.IsBasicAuth = ctx.Data["AuthedMethod"].(string) == auth_service.BasicMethodName | ||||||
| 			ctx.IsSigned = true | 			ctx.IsSigned = true | ||||||
| 			ctx.Data["IsSigned"] = ctx.IsSigned | 			ctx.Data["IsSigned"] = ctx.IsSigned | ||||||
| 			ctx.Data["SignedUser"] = ctx.User | 			ctx.Data["SignedUser"] = ctx.User | ||||||
|   | |||||||
| @@ -8,7 +8,7 @@ package context | |||||||
| import ( | import ( | ||||||
| 	"net/http" | 	"net/http" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models/login" | 	"code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
| 	"code.gitea.io/gitea/modules/web/middleware" | 	"code.gitea.io/gitea/modules/web/middleware" | ||||||
| @@ -154,9 +154,9 @@ func ToggleAPI(options *ToggleOptions) func(ctx *APIContext) { | |||||||
| 				if skip, ok := ctx.Data["SkipLocalTwoFA"]; ok && skip.(bool) { | 				if skip, ok := ctx.Data["SkipLocalTwoFA"]; ok && skip.(bool) { | ||||||
| 					return // Skip 2FA | 					return // Skip 2FA | ||||||
| 				} | 				} | ||||||
| 				twofa, err := login.GetTwoFactorByUID(ctx.User.ID) | 				twofa, err := auth.GetTwoFactorByUID(ctx.User.ID) | ||||||
| 				if err != nil { | 				if err != nil { | ||||||
| 					if login.IsErrTwoFactorNotEnrolled(err) { | 					if auth.IsErrTwoFactorNotEnrolled(err) { | ||||||
| 						return // No 2FA enrollment for this user | 						return // No 2FA enrollment for this user | ||||||
| 					} | 					} | ||||||
| 					ctx.InternalServerError(err) | 					ctx.InternalServerError(err) | ||||||
|   | |||||||
| @@ -13,7 +13,7 @@ import ( | |||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" | 	"code.gitea.io/gitea/models" | ||||||
| 	asymkey_model "code.gitea.io/gitea/models/asymkey" | 	asymkey_model "code.gitea.io/gitea/models/asymkey" | ||||||
| 	"code.gitea.io/gitea/models/login" | 	"code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/perm" | 	"code.gitea.io/gitea/models/perm" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	"code.gitea.io/gitea/models/unit" | 	"code.gitea.io/gitea/models/unit" | ||||||
| @@ -344,8 +344,8 @@ func ToTopicResponse(topic *repo_model.Topic) *api.TopicResponse { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // ToOAuth2Application convert from login.OAuth2Application to api.OAuth2Application | // ToOAuth2Application convert from auth.OAuth2Application to api.OAuth2Application | ||||||
| func ToOAuth2Application(app *login.OAuth2Application) *api.OAuth2Application { | func ToOAuth2Application(app *auth.OAuth2Application) *api.OAuth2Application { | ||||||
| 	return &api.OAuth2Application{ | 	return &api.OAuth2Application{ | ||||||
| 		ID:           app.ID, | 		ID:           app.ID, | ||||||
| 		Name:         app.Name, | 		Name:         app.Name, | ||||||
|   | |||||||
| @@ -297,7 +297,7 @@ func (c Collector) Collect(ch chan<- prometheus.Metric) { | |||||||
| 	ch <- prometheus.MustNewConstMetric( | 	ch <- prometheus.MustNewConstMetric( | ||||||
| 		c.LoginSources, | 		c.LoginSources, | ||||||
| 		prometheus.GaugeValue, | 		prometheus.GaugeValue, | ||||||
| 		float64(stats.Counter.LoginSource), | 		float64(stats.Counter.AuthSource), | ||||||
| 	) | 	) | ||||||
| 	ch <- prometheus.MustNewConstMetric( | 	ch <- prometheus.MustNewConstMetric( | ||||||
| 		c.Milestones, | 		c.Milestones, | ||||||
|   | |||||||
| @@ -8,7 +8,7 @@ import ( | |||||||
| 	"log" | 	"log" | ||||||
| 	"sync" | 	"sync" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models/login" | 	"code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/modules/timeutil" | 	"code.gitea.io/gitea/modules/timeutil" | ||||||
|  |  | ||||||
| 	"gitea.com/go-chi/session" | 	"gitea.com/go-chi/session" | ||||||
| @@ -72,7 +72,7 @@ func (s *DBStore) Release() error { | |||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return login.UpdateSession(s.sid, data) | 	return auth.UpdateSession(s.sid, data) | ||||||
| } | } | ||||||
|  |  | ||||||
| // Flush deletes all session data. | // Flush deletes all session data. | ||||||
| @@ -98,7 +98,7 @@ func (p *DBProvider) Init(maxLifetime int64, connStr string) error { | |||||||
|  |  | ||||||
| // Read returns raw session store by session ID. | // Read returns raw session store by session ID. | ||||||
| func (p *DBProvider) Read(sid string) (session.RawStore, error) { | func (p *DBProvider) Read(sid string) (session.RawStore, error) { | ||||||
| 	s, err := login.ReadSession(sid) | 	s, err := auth.ReadSession(sid) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| @@ -118,7 +118,7 @@ func (p *DBProvider) Read(sid string) (session.RawStore, error) { | |||||||
|  |  | ||||||
| // Exist returns true if session with given ID exists. | // Exist returns true if session with given ID exists. | ||||||
| func (p *DBProvider) Exist(sid string) bool { | func (p *DBProvider) Exist(sid string) bool { | ||||||
| 	has, err := login.ExistSession(sid) | 	has, err := auth.ExistSession(sid) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		panic("session/DB: error checking existence: " + err.Error()) | 		panic("session/DB: error checking existence: " + err.Error()) | ||||||
| 	} | 	} | ||||||
| @@ -127,12 +127,12 @@ func (p *DBProvider) Exist(sid string) bool { | |||||||
|  |  | ||||||
| // Destroy deletes a session by session ID. | // Destroy deletes a session by session ID. | ||||||
| func (p *DBProvider) Destroy(sid string) error { | func (p *DBProvider) Destroy(sid string) error { | ||||||
| 	return login.DestroySession(sid) | 	return auth.DestroySession(sid) | ||||||
| } | } | ||||||
|  |  | ||||||
| // Regenerate regenerates a session store from old session ID to new one. | // Regenerate regenerates a session store from old session ID to new one. | ||||||
| func (p *DBProvider) Regenerate(oldsid, sid string) (_ session.RawStore, err error) { | func (p *DBProvider) Regenerate(oldsid, sid string) (_ session.RawStore, err error) { | ||||||
| 	s, err := login.RegenerateSession(oldsid, sid) | 	s, err := auth.RegenerateSession(oldsid, sid) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
|  |  | ||||||
| @@ -153,7 +153,7 @@ func (p *DBProvider) Regenerate(oldsid, sid string) (_ session.RawStore, err err | |||||||
|  |  | ||||||
| // Count counts and returns number of sessions. | // Count counts and returns number of sessions. | ||||||
| func (p *DBProvider) Count() int { | func (p *DBProvider) Count() int { | ||||||
| 	total, err := login.CountSessions() | 	total, err := auth.CountSessions() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		panic("session/DB: error counting records: " + err.Error()) | 		panic("session/DB: error counting records: " + err.Error()) | ||||||
| 	} | 	} | ||||||
| @@ -162,7 +162,7 @@ func (p *DBProvider) Count() int { | |||||||
|  |  | ||||||
| // GC calls GC to clean expired sessions. | // GC calls GC to clean expired sessions. | ||||||
| func (p *DBProvider) GC() { | func (p *DBProvider) GC() { | ||||||
| 	if err := login.CleanupSessions(p.maxLifetime); err != nil { | 	if err := auth.CleanupSessions(p.maxLifetime); err != nil { | ||||||
| 		log.Printf("session/DB: error garbage collecting: %v", err) | 		log.Printf("session/DB: error garbage collecting: %v", err) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
| @@ -13,8 +13,8 @@ import ( | |||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" | 	"code.gitea.io/gitea/models" | ||||||
| 	asymkey_model "code.gitea.io/gitea/models/asymkey" | 	asymkey_model "code.gitea.io/gitea/models/asymkey" | ||||||
|  | 	"code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	"code.gitea.io/gitea/models/login" |  | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	"code.gitea.io/gitea/modules/context" | 	"code.gitea.io/gitea/modules/context" | ||||||
| 	"code.gitea.io/gitea/modules/convert" | 	"code.gitea.io/gitea/modules/convert" | ||||||
| @@ -30,17 +30,17 @@ import ( | |||||||
| 	user_service "code.gitea.io/gitea/services/user" | 	user_service "code.gitea.io/gitea/services/user" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func parseLoginSource(ctx *context.APIContext, u *user_model.User, sourceID int64, loginName string) { | func parseAuthSource(ctx *context.APIContext, u *user_model.User, sourceID int64, loginName string) { | ||||||
| 	if sourceID == 0 { | 	if sourceID == 0 { | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	source, err := login.GetSourceByID(sourceID) | 	source, err := auth.GetSourceByID(sourceID) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		if login.IsErrSourceNotExist(err) { | 		if auth.IsErrSourceNotExist(err) { | ||||||
| 			ctx.Error(http.StatusUnprocessableEntity, "", err) | 			ctx.Error(http.StatusUnprocessableEntity, "", err) | ||||||
| 		} else { | 		} else { | ||||||
| 			ctx.Error(http.StatusInternalServerError, "login.GetSourceByID", err) | 			ctx.Error(http.StatusInternalServerError, "auth.GetSourceByID", err) | ||||||
| 		} | 		} | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| @@ -82,13 +82,13 @@ func CreateUser(ctx *context.APIContext) { | |||||||
| 		Passwd:             form.Password, | 		Passwd:             form.Password, | ||||||
| 		MustChangePassword: true, | 		MustChangePassword: true, | ||||||
| 		IsActive:           true, | 		IsActive:           true, | ||||||
| 		LoginType:          login.Plain, | 		LoginType:          auth.Plain, | ||||||
| 	} | 	} | ||||||
| 	if form.MustChangePassword != nil { | 	if form.MustChangePassword != nil { | ||||||
| 		u.MustChangePassword = *form.MustChangePassword | 		u.MustChangePassword = *form.MustChangePassword | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	parseLoginSource(ctx, u, form.SourceID, form.LoginName) | 	parseAuthSource(ctx, u, form.SourceID, form.LoginName) | ||||||
| 	if ctx.Written() { | 	if ctx.Written() { | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| @@ -168,7 +168,7 @@ func EditUser(ctx *context.APIContext) { | |||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	parseLoginSource(ctx, u, form.SourceID, form.LoginName) | 	parseAuthSource(ctx, u, form.SourceID, form.LoginName) | ||||||
| 	if ctx.Written() { | 	if ctx.Written() { | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -12,7 +12,7 @@ import ( | |||||||
| 	"strconv" | 	"strconv" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" | 	"code.gitea.io/gitea/models" | ||||||
| 	"code.gitea.io/gitea/models/login" | 	"code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/modules/context" | 	"code.gitea.io/gitea/modules/context" | ||||||
| 	"code.gitea.io/gitea/modules/convert" | 	"code.gitea.io/gitea/modules/convert" | ||||||
| 	api "code.gitea.io/gitea/modules/structs" | 	api "code.gitea.io/gitea/modules/structs" | ||||||
| @@ -213,7 +213,7 @@ func CreateOauth2Application(ctx *context.APIContext) { | |||||||
|  |  | ||||||
| 	data := web.GetForm(ctx).(*api.CreateOAuth2ApplicationOptions) | 	data := web.GetForm(ctx).(*api.CreateOAuth2ApplicationOptions) | ||||||
|  |  | ||||||
| 	app, err := login.CreateOAuth2Application(login.CreateOAuth2ApplicationOptions{ | 	app, err := auth.CreateOAuth2Application(auth.CreateOAuth2ApplicationOptions{ | ||||||
| 		Name:         data.Name, | 		Name:         data.Name, | ||||||
| 		UserID:       ctx.User.ID, | 		UserID:       ctx.User.ID, | ||||||
| 		RedirectURIs: data.RedirectURIs, | 		RedirectURIs: data.RedirectURIs, | ||||||
| @@ -252,7 +252,7 @@ func ListOauth2Applications(ctx *context.APIContext) { | |||||||
| 	//   "200": | 	//   "200": | ||||||
| 	//     "$ref": "#/responses/OAuth2ApplicationList" | 	//     "$ref": "#/responses/OAuth2ApplicationList" | ||||||
|  |  | ||||||
| 	apps, total, err := login.ListOAuth2Applications(ctx.User.ID, utils.GetListOptions(ctx)) | 	apps, total, err := auth.ListOAuth2Applications(ctx.User.ID, utils.GetListOptions(ctx)) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		ctx.Error(http.StatusInternalServerError, "ListOAuth2Applications", err) | 		ctx.Error(http.StatusInternalServerError, "ListOAuth2Applications", err) | ||||||
| 		return | 		return | ||||||
| @@ -288,8 +288,8 @@ func DeleteOauth2Application(ctx *context.APIContext) { | |||||||
| 	//   "404": | 	//   "404": | ||||||
| 	//     "$ref": "#/responses/notFound" | 	//     "$ref": "#/responses/notFound" | ||||||
| 	appID := ctx.ParamsInt64(":id") | 	appID := ctx.ParamsInt64(":id") | ||||||
| 	if err := login.DeleteOAuth2Application(appID, ctx.User.ID); err != nil { | 	if err := auth.DeleteOAuth2Application(appID, ctx.User.ID); err != nil { | ||||||
| 		if login.IsErrOAuthApplicationNotFound(err) { | 		if auth.IsErrOAuthApplicationNotFound(err) { | ||||||
| 			ctx.NotFound() | 			ctx.NotFound() | ||||||
| 		} else { | 		} else { | ||||||
| 			ctx.Error(http.StatusInternalServerError, "DeleteOauth2ApplicationByID", err) | 			ctx.Error(http.StatusInternalServerError, "DeleteOauth2ApplicationByID", err) | ||||||
| @@ -320,9 +320,9 @@ func GetOauth2Application(ctx *context.APIContext) { | |||||||
| 	//   "404": | 	//   "404": | ||||||
| 	//     "$ref": "#/responses/notFound" | 	//     "$ref": "#/responses/notFound" | ||||||
| 	appID := ctx.ParamsInt64(":id") | 	appID := ctx.ParamsInt64(":id") | ||||||
| 	app, err := login.GetOAuth2ApplicationByID(appID) | 	app, err := auth.GetOAuth2ApplicationByID(appID) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		if login.IsErrOauthClientIDInvalid(err) || login.IsErrOAuthApplicationNotFound(err) { | 		if auth.IsErrOauthClientIDInvalid(err) || auth.IsErrOAuthApplicationNotFound(err) { | ||||||
| 			ctx.NotFound() | 			ctx.NotFound() | ||||||
| 		} else { | 		} else { | ||||||
| 			ctx.Error(http.StatusInternalServerError, "GetOauth2ApplicationByID", err) | 			ctx.Error(http.StatusInternalServerError, "GetOauth2ApplicationByID", err) | ||||||
| @@ -363,14 +363,14 @@ func UpdateOauth2Application(ctx *context.APIContext) { | |||||||
|  |  | ||||||
| 	data := web.GetForm(ctx).(*api.CreateOAuth2ApplicationOptions) | 	data := web.GetForm(ctx).(*api.CreateOAuth2ApplicationOptions) | ||||||
|  |  | ||||||
| 	app, err := login.UpdateOAuth2Application(login.UpdateOAuth2ApplicationOptions{ | 	app, err := auth.UpdateOAuth2Application(auth.UpdateOAuth2ApplicationOptions{ | ||||||
| 		Name:         data.Name, | 		Name:         data.Name, | ||||||
| 		UserID:       ctx.User.ID, | 		UserID:       ctx.User.ID, | ||||||
| 		ID:           appID, | 		ID:           appID, | ||||||
| 		RedirectURIs: data.RedirectURIs, | 		RedirectURIs: data.RedirectURIs, | ||||||
| 	}) | 	}) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		if login.IsErrOauthClientIDInvalid(err) || login.IsErrOAuthApplicationNotFound(err) { | 		if auth.IsErrOauthClientIDInvalid(err) || auth.IsErrOAuthApplicationNotFound(err) { | ||||||
| 			ctx.NotFound() | 			ctx.NotFound() | ||||||
| 		} else { | 		} else { | ||||||
| 			ctx.Error(http.StatusInternalServerError, "UpdateOauth2ApplicationByID", err) | 			ctx.Error(http.StatusInternalServerError, "UpdateOauth2ApplicationByID", err) | ||||||
|   | |||||||
| @@ -13,7 +13,7 @@ import ( | |||||||
| 	"strconv" | 	"strconv" | ||||||
| 	"strings" | 	"strings" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models/login" | 	"code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/modules/auth/pam" | 	"code.gitea.io/gitea/modules/auth/pam" | ||||||
| 	"code.gitea.io/gitea/modules/base" | 	"code.gitea.io/gitea/modules/base" | ||||||
| 	"code.gitea.io/gitea/modules/context" | 	"code.gitea.io/gitea/modules/context" | ||||||
| @@ -24,7 +24,7 @@ import ( | |||||||
| 	auth_service "code.gitea.io/gitea/services/auth" | 	auth_service "code.gitea.io/gitea/services/auth" | ||||||
| 	"code.gitea.io/gitea/services/auth/source/ldap" | 	"code.gitea.io/gitea/services/auth/source/ldap" | ||||||
| 	"code.gitea.io/gitea/services/auth/source/oauth2" | 	"code.gitea.io/gitea/services/auth/source/oauth2" | ||||||
| 	pamService "code.gitea.io/gitea/services/auth/source/pam" | 	pam_service "code.gitea.io/gitea/services/auth/source/pam" | ||||||
| 	"code.gitea.io/gitea/services/auth/source/smtp" | 	"code.gitea.io/gitea/services/auth/source/smtp" | ||||||
| 	"code.gitea.io/gitea/services/auth/source/sspi" | 	"code.gitea.io/gitea/services/auth/source/sspi" | ||||||
| 	"code.gitea.io/gitea/services/forms" | 	"code.gitea.io/gitea/services/forms" | ||||||
| @@ -50,13 +50,13 @@ func Authentications(ctx *context.Context) { | |||||||
| 	ctx.Data["PageIsAdminAuthentications"] = true | 	ctx.Data["PageIsAdminAuthentications"] = true | ||||||
|  |  | ||||||
| 	var err error | 	var err error | ||||||
| 	ctx.Data["Sources"], err = login.Sources() | 	ctx.Data["Sources"], err = auth.Sources() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		ctx.ServerError("login.Sources", err) | 		ctx.ServerError("auth.Sources", err) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	ctx.Data["Total"] = login.CountSources() | 	ctx.Data["Total"] = auth.CountSources() | ||||||
| 	ctx.HTML(http.StatusOK, tplAuths) | 	ctx.HTML(http.StatusOK, tplAuths) | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -68,14 +68,14 @@ type dropdownItem struct { | |||||||
| var ( | var ( | ||||||
| 	authSources = func() []dropdownItem { | 	authSources = func() []dropdownItem { | ||||||
| 		items := []dropdownItem{ | 		items := []dropdownItem{ | ||||||
| 			{login.LDAP.String(), login.LDAP}, | 			{auth.LDAP.String(), auth.LDAP}, | ||||||
| 			{login.DLDAP.String(), login.DLDAP}, | 			{auth.DLDAP.String(), auth.DLDAP}, | ||||||
| 			{login.SMTP.String(), login.SMTP}, | 			{auth.SMTP.String(), auth.SMTP}, | ||||||
| 			{login.OAuth2.String(), login.OAuth2}, | 			{auth.OAuth2.String(), auth.OAuth2}, | ||||||
| 			{login.SSPI.String(), login.SSPI}, | 			{auth.SSPI.String(), auth.SSPI}, | ||||||
| 		} | 		} | ||||||
| 		if pam.Supported { | 		if pam.Supported { | ||||||
| 			items = append(items, dropdownItem{login.Names[login.PAM], login.PAM}) | 			items = append(items, dropdownItem{auth.Names[auth.PAM], auth.PAM}) | ||||||
| 		} | 		} | ||||||
| 		return items | 		return items | ||||||
| 	}() | 	}() | ||||||
| @@ -93,8 +93,8 @@ func NewAuthSource(ctx *context.Context) { | |||||||
| 	ctx.Data["PageIsAdmin"] = true | 	ctx.Data["PageIsAdmin"] = true | ||||||
| 	ctx.Data["PageIsAdminAuthentications"] = true | 	ctx.Data["PageIsAdminAuthentications"] = true | ||||||
|  |  | ||||||
| 	ctx.Data["type"] = login.LDAP | 	ctx.Data["type"] = auth.LDAP | ||||||
| 	ctx.Data["CurrentTypeName"] = login.Names[login.LDAP] | 	ctx.Data["CurrentTypeName"] = auth.Names[auth.LDAP] | ||||||
| 	ctx.Data["CurrentSecurityProtocol"] = ldap.SecurityProtocolNames[ldap.SecurityProtocolUnencrypted] | 	ctx.Data["CurrentSecurityProtocol"] = ldap.SecurityProtocolNames[ldap.SecurityProtocolUnencrypted] | ||||||
| 	ctx.Data["smtp_auth"] = "PLAIN" | 	ctx.Data["smtp_auth"] = "PLAIN" | ||||||
| 	ctx.Data["is_active"] = true | 	ctx.Data["is_active"] = true | ||||||
| @@ -226,7 +226,7 @@ func NewAuthSourcePost(ctx *context.Context) { | |||||||
| 	ctx.Data["PageIsAdmin"] = true | 	ctx.Data["PageIsAdmin"] = true | ||||||
| 	ctx.Data["PageIsAdminAuthentications"] = true | 	ctx.Data["PageIsAdminAuthentications"] = true | ||||||
|  |  | ||||||
| 	ctx.Data["CurrentTypeName"] = login.Type(form.Type).String() | 	ctx.Data["CurrentTypeName"] = auth.Type(form.Type).String() | ||||||
| 	ctx.Data["CurrentSecurityProtocol"] = ldap.SecurityProtocolNames[ldap.SecurityProtocol(form.SecurityProtocol)] | 	ctx.Data["CurrentSecurityProtocol"] = ldap.SecurityProtocolNames[ldap.SecurityProtocol(form.SecurityProtocol)] | ||||||
| 	ctx.Data["AuthSources"] = authSources | 	ctx.Data["AuthSources"] = authSources | ||||||
| 	ctx.Data["SecurityProtocols"] = securityProtocols | 	ctx.Data["SecurityProtocols"] = securityProtocols | ||||||
| @@ -242,29 +242,29 @@ func NewAuthSourcePost(ctx *context.Context) { | |||||||
|  |  | ||||||
| 	hasTLS := false | 	hasTLS := false | ||||||
| 	var config convert.Conversion | 	var config convert.Conversion | ||||||
| 	switch login.Type(form.Type) { | 	switch auth.Type(form.Type) { | ||||||
| 	case login.LDAP, login.DLDAP: | 	case auth.LDAP, auth.DLDAP: | ||||||
| 		config = parseLDAPConfig(form) | 		config = parseLDAPConfig(form) | ||||||
| 		hasTLS = ldap.SecurityProtocol(form.SecurityProtocol) > ldap.SecurityProtocolUnencrypted | 		hasTLS = ldap.SecurityProtocol(form.SecurityProtocol) > ldap.SecurityProtocolUnencrypted | ||||||
| 	case login.SMTP: | 	case auth.SMTP: | ||||||
| 		config = parseSMTPConfig(form) | 		config = parseSMTPConfig(form) | ||||||
| 		hasTLS = true | 		hasTLS = true | ||||||
| 	case login.PAM: | 	case auth.PAM: | ||||||
| 		config = &pamService.Source{ | 		config = &pam_service.Source{ | ||||||
| 			ServiceName:    form.PAMServiceName, | 			ServiceName:    form.PAMServiceName, | ||||||
| 			EmailDomain:    form.PAMEmailDomain, | 			EmailDomain:    form.PAMEmailDomain, | ||||||
| 			SkipLocalTwoFA: form.SkipLocalTwoFA, | 			SkipLocalTwoFA: form.SkipLocalTwoFA, | ||||||
| 		} | 		} | ||||||
| 	case login.OAuth2: | 	case auth.OAuth2: | ||||||
| 		config = parseOAuth2Config(form) | 		config = parseOAuth2Config(form) | ||||||
| 	case login.SSPI: | 	case auth.SSPI: | ||||||
| 		var err error | 		var err error | ||||||
| 		config, err = parseSSPIConfig(ctx, form) | 		config, err = parseSSPIConfig(ctx, form) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			ctx.RenderWithErr(err.Error(), tplAuthNew, form) | 			ctx.RenderWithErr(err.Error(), tplAuthNew, form) | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| 		existing, err := login.SourcesByType(login.SSPI) | 		existing, err := auth.SourcesByType(auth.SSPI) | ||||||
| 		if err != nil || len(existing) > 0 { | 		if err != nil || len(existing) > 0 { | ||||||
| 			ctx.Data["Err_Type"] = true | 			ctx.Data["Err_Type"] = true | ||||||
| 			ctx.RenderWithErr(ctx.Tr("admin.auths.login_source_of_type_exist"), tplAuthNew, form) | 			ctx.RenderWithErr(ctx.Tr("admin.auths.login_source_of_type_exist"), tplAuthNew, form) | ||||||
| @@ -281,18 +281,18 @@ func NewAuthSourcePost(ctx *context.Context) { | |||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if err := login.CreateSource(&login.Source{ | 	if err := auth.CreateSource(&auth.Source{ | ||||||
| 		Type:          login.Type(form.Type), | 		Type:          auth.Type(form.Type), | ||||||
| 		Name:          form.Name, | 		Name:          form.Name, | ||||||
| 		IsActive:      form.IsActive, | 		IsActive:      form.IsActive, | ||||||
| 		IsSyncEnabled: form.IsSyncEnabled, | 		IsSyncEnabled: form.IsSyncEnabled, | ||||||
| 		Cfg:           config, | 		Cfg:           config, | ||||||
| 	}); err != nil { | 	}); err != nil { | ||||||
| 		if login.IsErrSourceAlreadyExist(err) { | 		if auth.IsErrSourceAlreadyExist(err) { | ||||||
| 			ctx.Data["Err_Name"] = true | 			ctx.Data["Err_Name"] = true | ||||||
| 			ctx.RenderWithErr(ctx.Tr("admin.auths.login_source_exist", err.(login.ErrSourceAlreadyExist).Name), tplAuthNew, form) | 			ctx.RenderWithErr(ctx.Tr("admin.auths.login_source_exist", err.(auth.ErrSourceAlreadyExist).Name), tplAuthNew, form) | ||||||
| 		} else { | 		} else { | ||||||
| 			ctx.ServerError("login.CreateSource", err) | 			ctx.ServerError("auth.CreateSource", err) | ||||||
| 		} | 		} | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| @@ -314,9 +314,9 @@ func EditAuthSource(ctx *context.Context) { | |||||||
| 	oauth2providers := oauth2.GetOAuth2Providers() | 	oauth2providers := oauth2.GetOAuth2Providers() | ||||||
| 	ctx.Data["OAuth2Providers"] = oauth2providers | 	ctx.Data["OAuth2Providers"] = oauth2providers | ||||||
|  |  | ||||||
| 	source, err := login.GetSourceByID(ctx.ParamsInt64(":authid")) | 	source, err := auth.GetSourceByID(ctx.ParamsInt64(":authid")) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		ctx.ServerError("login.GetSourceByID", err) | 		ctx.ServerError("auth.GetSourceByID", err) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	ctx.Data["Source"] = source | 	ctx.Data["Source"] = source | ||||||
| @@ -349,9 +349,9 @@ func EditAuthSourcePost(ctx *context.Context) { | |||||||
| 	oauth2providers := oauth2.GetOAuth2Providers() | 	oauth2providers := oauth2.GetOAuth2Providers() | ||||||
| 	ctx.Data["OAuth2Providers"] = oauth2providers | 	ctx.Data["OAuth2Providers"] = oauth2providers | ||||||
|  |  | ||||||
| 	source, err := login.GetSourceByID(ctx.ParamsInt64(":authid")) | 	source, err := auth.GetSourceByID(ctx.ParamsInt64(":authid")) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		ctx.ServerError("login.GetSourceByID", err) | 		ctx.ServerError("auth.GetSourceByID", err) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	ctx.Data["Source"] = source | 	ctx.Data["Source"] = source | ||||||
| @@ -363,19 +363,19 @@ func EditAuthSourcePost(ctx *context.Context) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	var config convert.Conversion | 	var config convert.Conversion | ||||||
| 	switch login.Type(form.Type) { | 	switch auth.Type(form.Type) { | ||||||
| 	case login.LDAP, login.DLDAP: | 	case auth.LDAP, auth.DLDAP: | ||||||
| 		config = parseLDAPConfig(form) | 		config = parseLDAPConfig(form) | ||||||
| 	case login.SMTP: | 	case auth.SMTP: | ||||||
| 		config = parseSMTPConfig(form) | 		config = parseSMTPConfig(form) | ||||||
| 	case login.PAM: | 	case auth.PAM: | ||||||
| 		config = &pamService.Source{ | 		config = &pam_service.Source{ | ||||||
| 			ServiceName: form.PAMServiceName, | 			ServiceName: form.PAMServiceName, | ||||||
| 			EmailDomain: form.PAMEmailDomain, | 			EmailDomain: form.PAMEmailDomain, | ||||||
| 		} | 		} | ||||||
| 	case login.OAuth2: | 	case auth.OAuth2: | ||||||
| 		config = parseOAuth2Config(form) | 		config = parseOAuth2Config(form) | ||||||
| 	case login.SSPI: | 	case auth.SSPI: | ||||||
| 		config, err = parseSSPIConfig(ctx, form) | 		config, err = parseSSPIConfig(ctx, form) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			ctx.RenderWithErr(err.Error(), tplAuthEdit, form) | 			ctx.RenderWithErr(err.Error(), tplAuthEdit, form) | ||||||
| @@ -390,7 +390,7 @@ func EditAuthSourcePost(ctx *context.Context) { | |||||||
| 	source.IsActive = form.IsActive | 	source.IsActive = form.IsActive | ||||||
| 	source.IsSyncEnabled = form.IsSyncEnabled | 	source.IsSyncEnabled = form.IsSyncEnabled | ||||||
| 	source.Cfg = config | 	source.Cfg = config | ||||||
| 	if err := login.UpdateSource(source); err != nil { | 	if err := auth.UpdateSource(source); err != nil { | ||||||
| 		if oauth2.IsErrOpenIDConnectInitialize(err) { | 		if oauth2.IsErrOpenIDConnectInitialize(err) { | ||||||
| 			ctx.Flash.Error(err.Error(), true) | 			ctx.Flash.Error(err.Error(), true) | ||||||
| 			ctx.HTML(http.StatusOK, tplAuthEdit) | 			ctx.HTML(http.StatusOK, tplAuthEdit) | ||||||
| @@ -407,17 +407,17 @@ func EditAuthSourcePost(ctx *context.Context) { | |||||||
|  |  | ||||||
| // DeleteAuthSource response for deleting an auth source | // DeleteAuthSource response for deleting an auth source | ||||||
| func DeleteAuthSource(ctx *context.Context) { | func DeleteAuthSource(ctx *context.Context) { | ||||||
| 	source, err := login.GetSourceByID(ctx.ParamsInt64(":authid")) | 	source, err := auth.GetSourceByID(ctx.ParamsInt64(":authid")) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		ctx.ServerError("login.GetSourceByID", err) | 		ctx.ServerError("auth.GetSourceByID", err) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if err = auth_service.DeleteLoginSource(source); err != nil { | 	if err = auth_service.DeleteSource(source); err != nil { | ||||||
| 		if login.IsErrSourceInUse(err) { | 		if auth.IsErrSourceInUse(err) { | ||||||
| 			ctx.Flash.Error(ctx.Tr("admin.auths.still_in_used")) | 			ctx.Flash.Error(ctx.Tr("admin.auths.still_in_used")) | ||||||
| 		} else { | 		} else { | ||||||
| 			ctx.Flash.Error(fmt.Sprintf("DeleteLoginSource: %v", err)) | 			ctx.Flash.Error(fmt.Sprintf("auth_service.DeleteSource: %v", err)) | ||||||
| 		} | 		} | ||||||
| 		ctx.JSON(http.StatusOK, map[string]interface{}{ | 		ctx.JSON(http.StatusOK, map[string]interface{}{ | ||||||
| 			"redirect": setting.AppSubURL + "/admin/auths/" + url.PathEscape(ctx.Params(":authid")), | 			"redirect": setting.AppSubURL + "/admin/auths/" + url.PathEscape(ctx.Params(":authid")), | ||||||
|   | |||||||
| @@ -12,8 +12,8 @@ import ( | |||||||
| 	"strings" | 	"strings" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" | 	"code.gitea.io/gitea/models" | ||||||
|  | 	"code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	"code.gitea.io/gitea/models/login" |  | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	"code.gitea.io/gitea/modules/base" | 	"code.gitea.io/gitea/modules/base" | ||||||
| 	"code.gitea.io/gitea/modules/context" | 	"code.gitea.io/gitea/modules/context" | ||||||
| @@ -81,9 +81,9 @@ func NewUser(ctx *context.Context) { | |||||||
|  |  | ||||||
| 	ctx.Data["login_type"] = "0-0" | 	ctx.Data["login_type"] = "0-0" | ||||||
|  |  | ||||||
| 	sources, err := login.Sources() | 	sources, err := auth.Sources() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		ctx.ServerError("login.Sources", err) | 		ctx.ServerError("auth.Sources", err) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	ctx.Data["Sources"] = sources | 	ctx.Data["Sources"] = sources | ||||||
| @@ -100,9 +100,9 @@ func NewUserPost(ctx *context.Context) { | |||||||
| 	ctx.Data["PageIsAdminUsers"] = true | 	ctx.Data["PageIsAdminUsers"] = true | ||||||
| 	ctx.Data["DefaultUserVisibilityMode"] = setting.Service.DefaultUserVisibilityMode | 	ctx.Data["DefaultUserVisibilityMode"] = setting.Service.DefaultUserVisibilityMode | ||||||
|  |  | ||||||
| 	sources, err := login.Sources() | 	sources, err := auth.Sources() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		ctx.ServerError("login.Sources", err) | 		ctx.ServerError("auth.Sources", err) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	ctx.Data["Sources"] = sources | 	ctx.Data["Sources"] = sources | ||||||
| @@ -119,19 +119,19 @@ func NewUserPost(ctx *context.Context) { | |||||||
| 		Email:     form.Email, | 		Email:     form.Email, | ||||||
| 		Passwd:    form.Password, | 		Passwd:    form.Password, | ||||||
| 		IsActive:  true, | 		IsActive:  true, | ||||||
| 		LoginType: login.Plain, | 		LoginType: auth.Plain, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if len(form.LoginType) > 0 { | 	if len(form.LoginType) > 0 { | ||||||
| 		fields := strings.Split(form.LoginType, "-") | 		fields := strings.Split(form.LoginType, "-") | ||||||
| 		if len(fields) == 2 { | 		if len(fields) == 2 { | ||||||
| 			lType, _ := strconv.ParseInt(fields[0], 10, 0) | 			lType, _ := strconv.ParseInt(fields[0], 10, 0) | ||||||
| 			u.LoginType = login.Type(lType) | 			u.LoginType = auth.Type(lType) | ||||||
| 			u.LoginSource, _ = strconv.ParseInt(fields[1], 10, 64) | 			u.LoginSource, _ = strconv.ParseInt(fields[1], 10, 64) | ||||||
| 			u.LoginName = form.LoginName | 			u.LoginName = form.LoginName | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	if u.LoginType == login.NoType || u.LoginType == login.Plain { | 	if u.LoginType == auth.NoType || u.LoginType == auth.Plain { | ||||||
| 		if len(form.Password) < setting.MinPasswordLength { | 		if len(form.Password) < setting.MinPasswordLength { | ||||||
| 			ctx.Data["Err_Password"] = true | 			ctx.Data["Err_Password"] = true | ||||||
| 			ctx.RenderWithErr(ctx.Tr("auth.password_too_short", setting.MinPasswordLength), tplUserNew, &form) | 			ctx.RenderWithErr(ctx.Tr("auth.password_too_short", setting.MinPasswordLength), tplUserNew, &form) | ||||||
| @@ -201,26 +201,26 @@ func prepareUserInfo(ctx *context.Context) *user_model.User { | |||||||
| 	ctx.Data["User"] = u | 	ctx.Data["User"] = u | ||||||
|  |  | ||||||
| 	if u.LoginSource > 0 { | 	if u.LoginSource > 0 { | ||||||
| 		ctx.Data["LoginSource"], err = login.GetSourceByID(u.LoginSource) | 		ctx.Data["LoginSource"], err = auth.GetSourceByID(u.LoginSource) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			ctx.ServerError("login.GetSourceByID", err) | 			ctx.ServerError("auth.GetSourceByID", err) | ||||||
| 			return nil | 			return nil | ||||||
| 		} | 		} | ||||||
| 	} else { | 	} else { | ||||||
| 		ctx.Data["LoginSource"] = &login.Source{} | 		ctx.Data["LoginSource"] = &auth.Source{} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	sources, err := login.Sources() | 	sources, err := auth.Sources() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		ctx.ServerError("login.Sources", err) | 		ctx.ServerError("auth.Sources", err) | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
| 	ctx.Data["Sources"] = sources | 	ctx.Data["Sources"] = sources | ||||||
|  |  | ||||||
| 	ctx.Data["TwoFactorEnabled"] = true | 	ctx.Data["TwoFactorEnabled"] = true | ||||||
| 	_, err = login.GetTwoFactorByUID(u.ID) | 	_, err = auth.GetTwoFactorByUID(u.ID) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		if !login.IsErrTwoFactorNotEnrolled(err) { | 		if !auth.IsErrTwoFactorNotEnrolled(err) { | ||||||
| 			ctx.ServerError("IsErrTwoFactorNotEnrolled", err) | 			ctx.ServerError("IsErrTwoFactorNotEnrolled", err) | ||||||
| 			return nil | 			return nil | ||||||
| 		} | 		} | ||||||
| @@ -268,11 +268,11 @@ func EditUserPost(ctx *context.Context) { | |||||||
| 	fields := strings.Split(form.LoginType, "-") | 	fields := strings.Split(form.LoginType, "-") | ||||||
| 	if len(fields) == 2 { | 	if len(fields) == 2 { | ||||||
| 		loginType, _ := strconv.ParseInt(fields[0], 10, 0) | 		loginType, _ := strconv.ParseInt(fields[0], 10, 0) | ||||||
| 		loginSource, _ := strconv.ParseInt(fields[1], 10, 64) | 		authSource, _ := strconv.ParseInt(fields[1], 10, 64) | ||||||
|  |  | ||||||
| 		if u.LoginSource != loginSource { | 		if u.LoginSource != authSource { | ||||||
| 			u.LoginSource = loginSource | 			u.LoginSource = authSource | ||||||
| 			u.LoginType = login.Type(loginType) | 			u.LoginType = auth.Type(loginType) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -325,13 +325,13 @@ func EditUserPost(ctx *context.Context) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if form.Reset2FA { | 	if form.Reset2FA { | ||||||
| 		tf, err := login.GetTwoFactorByUID(u.ID) | 		tf, err := auth.GetTwoFactorByUID(u.ID) | ||||||
| 		if err != nil && !login.IsErrTwoFactorNotEnrolled(err) { | 		if err != nil && !auth.IsErrTwoFactorNotEnrolled(err) { | ||||||
| 			ctx.ServerError("GetTwoFactorByUID", err) | 			ctx.ServerError("GetTwoFactorByUID", err) | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if err = login.DeleteTwoFactorByID(tf.ID, u.ID); err != nil { | 		if err = auth.DeleteTwoFactorByID(tf.ID, u.ID); err != nil { | ||||||
| 			ctx.ServerError("DeleteTwoFactorByID", err) | 			ctx.ServerError("DeleteTwoFactorByID", err) | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
|   | |||||||
							
								
								
									
										166
									
								
								routers/web/auth/2fa.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										166
									
								
								routers/web/auth/2fa.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,166 @@ | |||||||
|  | // Copyright 2017 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 auth | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"errors" | ||||||
|  | 	"net/http" | ||||||
|  |  | ||||||
|  | 	"code.gitea.io/gitea/models/auth" | ||||||
|  | 	user_model "code.gitea.io/gitea/models/user" | ||||||
|  | 	"code.gitea.io/gitea/modules/base" | ||||||
|  | 	"code.gitea.io/gitea/modules/context" | ||||||
|  | 	"code.gitea.io/gitea/modules/setting" | ||||||
|  | 	"code.gitea.io/gitea/modules/web" | ||||||
|  | 	"code.gitea.io/gitea/services/externalaccount" | ||||||
|  | 	"code.gitea.io/gitea/services/forms" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | var ( | ||||||
|  | 	tplTwofa        base.TplName = "user/auth/twofa" | ||||||
|  | 	tplTwofaScratch base.TplName = "user/auth/twofa_scratch" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // TwoFactor shows the user a two-factor authentication page. | ||||||
|  | func TwoFactor(ctx *context.Context) { | ||||||
|  | 	ctx.Data["Title"] = ctx.Tr("twofa") | ||||||
|  |  | ||||||
|  | 	// Check auto-login. | ||||||
|  | 	if checkAutoLogin(ctx) { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Ensure user is in a 2FA session. | ||||||
|  | 	if ctx.Session.Get("twofaUid") == nil { | ||||||
|  | 		ctx.ServerError("UserSignIn", errors.New("not in 2FA session")) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ctx.HTML(http.StatusOK, tplTwofa) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // TwoFactorPost validates a user's two-factor authentication token. | ||||||
|  | func TwoFactorPost(ctx *context.Context) { | ||||||
|  | 	form := web.GetForm(ctx).(*forms.TwoFactorAuthForm) | ||||||
|  | 	ctx.Data["Title"] = ctx.Tr("twofa") | ||||||
|  |  | ||||||
|  | 	// Ensure user is in a 2FA session. | ||||||
|  | 	idSess := ctx.Session.Get("twofaUid") | ||||||
|  | 	if idSess == nil { | ||||||
|  | 		ctx.ServerError("UserSignIn", errors.New("not in 2FA session")) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	id := idSess.(int64) | ||||||
|  | 	twofa, err := auth.GetTwoFactorByUID(id) | ||||||
|  | 	if err != nil { | ||||||
|  | 		ctx.ServerError("UserSignIn", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Validate the passcode with the stored TOTP secret. | ||||||
|  | 	ok, err := twofa.ValidateTOTP(form.Passcode) | ||||||
|  | 	if err != nil { | ||||||
|  | 		ctx.ServerError("UserSignIn", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if ok && twofa.LastUsedPasscode != form.Passcode { | ||||||
|  | 		remember := ctx.Session.Get("twofaRemember").(bool) | ||||||
|  | 		u, err := user_model.GetUserByID(id) | ||||||
|  | 		if err != nil { | ||||||
|  | 			ctx.ServerError("UserSignIn", err) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if ctx.Session.Get("linkAccount") != nil { | ||||||
|  | 			err = externalaccount.LinkAccountFromStore(ctx.Session, u) | ||||||
|  | 			if err != nil { | ||||||
|  | 				ctx.ServerError("UserSignIn", err) | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		twofa.LastUsedPasscode = form.Passcode | ||||||
|  | 		if err = auth.UpdateTwoFactor(twofa); err != nil { | ||||||
|  | 			ctx.ServerError("UserSignIn", err) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		handleSignIn(ctx, u, remember) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ctx.RenderWithErr(ctx.Tr("auth.twofa_passcode_incorrect"), tplTwofa, forms.TwoFactorAuthForm{}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // TwoFactorScratch shows the scratch code form for two-factor authentication. | ||||||
|  | func TwoFactorScratch(ctx *context.Context) { | ||||||
|  | 	ctx.Data["Title"] = ctx.Tr("twofa_scratch") | ||||||
|  |  | ||||||
|  | 	// Check auto-login. | ||||||
|  | 	if checkAutoLogin(ctx) { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Ensure user is in a 2FA session. | ||||||
|  | 	if ctx.Session.Get("twofaUid") == nil { | ||||||
|  | 		ctx.ServerError("UserSignIn", errors.New("not in 2FA session")) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ctx.HTML(http.StatusOK, tplTwofaScratch) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // TwoFactorScratchPost validates and invalidates a user's two-factor scratch token. | ||||||
|  | func TwoFactorScratchPost(ctx *context.Context) { | ||||||
|  | 	form := web.GetForm(ctx).(*forms.TwoFactorScratchAuthForm) | ||||||
|  | 	ctx.Data["Title"] = ctx.Tr("twofa_scratch") | ||||||
|  |  | ||||||
|  | 	// Ensure user is in a 2FA session. | ||||||
|  | 	idSess := ctx.Session.Get("twofaUid") | ||||||
|  | 	if idSess == nil { | ||||||
|  | 		ctx.ServerError("UserSignIn", errors.New("not in 2FA session")) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	id := idSess.(int64) | ||||||
|  | 	twofa, err := auth.GetTwoFactorByUID(id) | ||||||
|  | 	if err != nil { | ||||||
|  | 		ctx.ServerError("UserSignIn", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Validate the passcode with the stored TOTP secret. | ||||||
|  | 	if twofa.VerifyScratchToken(form.Token) { | ||||||
|  | 		// Invalidate the scratch token. | ||||||
|  | 		_, err = twofa.GenerateScratchToken() | ||||||
|  | 		if err != nil { | ||||||
|  | 			ctx.ServerError("UserSignIn", err) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		if err = auth.UpdateTwoFactor(twofa); err != nil { | ||||||
|  | 			ctx.ServerError("UserSignIn", err) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		remember := ctx.Session.Get("twofaRemember").(bool) | ||||||
|  | 		u, err := user_model.GetUserByID(id) | ||||||
|  | 		if err != nil { | ||||||
|  | 			ctx.ServerError("UserSignIn", err) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		handleSignInFull(ctx, u, remember, false) | ||||||
|  | 		if ctx.Written() { | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		ctx.Flash.Info(ctx.Tr("auth.twofa_scratch_used")) | ||||||
|  | 		ctx.Redirect(setting.AppSubURL + "/user/settings/security") | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ctx.RenderWithErr(ctx.Tr("auth.twofa_scratch_token_incorrect"), tplTwofaScratch, forms.TwoFactorScratchAuthForm{}) | ||||||
|  | } | ||||||
							
								
								
									
										795
									
								
								routers/web/auth/auth.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										795
									
								
								routers/web/auth/auth.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,795 @@ | |||||||
|  | // Copyright 2014 The Gogs Authors. All rights reserved. | ||||||
|  | // Copyright 2018 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 auth | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"net/http" | ||||||
|  | 	"strings" | ||||||
|  |  | ||||||
|  | 	"code.gitea.io/gitea/models/auth" | ||||||
|  | 	"code.gitea.io/gitea/models/db" | ||||||
|  | 	user_model "code.gitea.io/gitea/models/user" | ||||||
|  | 	"code.gitea.io/gitea/modules/base" | ||||||
|  | 	"code.gitea.io/gitea/modules/context" | ||||||
|  | 	"code.gitea.io/gitea/modules/eventsource" | ||||||
|  | 	"code.gitea.io/gitea/modules/hcaptcha" | ||||||
|  | 	"code.gitea.io/gitea/modules/log" | ||||||
|  | 	"code.gitea.io/gitea/modules/password" | ||||||
|  | 	"code.gitea.io/gitea/modules/recaptcha" | ||||||
|  | 	"code.gitea.io/gitea/modules/session" | ||||||
|  | 	"code.gitea.io/gitea/modules/setting" | ||||||
|  | 	"code.gitea.io/gitea/modules/timeutil" | ||||||
|  | 	"code.gitea.io/gitea/modules/web" | ||||||
|  | 	"code.gitea.io/gitea/modules/web/middleware" | ||||||
|  | 	"code.gitea.io/gitea/routers/utils" | ||||||
|  | 	auth_service "code.gitea.io/gitea/services/auth" | ||||||
|  | 	"code.gitea.io/gitea/services/auth/source/oauth2" | ||||||
|  | 	"code.gitea.io/gitea/services/externalaccount" | ||||||
|  | 	"code.gitea.io/gitea/services/forms" | ||||||
|  | 	"code.gitea.io/gitea/services/mailer" | ||||||
|  |  | ||||||
|  | 	"github.com/markbates/goth" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	// tplSignIn template for sign in page | ||||||
|  | 	tplSignIn base.TplName = "user/auth/signin" | ||||||
|  | 	// tplSignUp template path for sign up page | ||||||
|  | 	tplSignUp base.TplName = "user/auth/signup" | ||||||
|  | 	// TplActivate template path for activate user | ||||||
|  | 	TplActivate base.TplName = "user/auth/activate" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // AutoSignIn reads cookie and try to auto-login. | ||||||
|  | func AutoSignIn(ctx *context.Context) (bool, error) { | ||||||
|  | 	if !db.HasEngine { | ||||||
|  | 		return false, nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	uname := ctx.GetCookie(setting.CookieUserName) | ||||||
|  | 	if len(uname) == 0 { | ||||||
|  | 		return false, nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	isSucceed := false | ||||||
|  | 	defer func() { | ||||||
|  | 		if !isSucceed { | ||||||
|  | 			log.Trace("auto-login cookie cleared: %s", uname) | ||||||
|  | 			ctx.DeleteCookie(setting.CookieUserName) | ||||||
|  | 			ctx.DeleteCookie(setting.CookieRememberName) | ||||||
|  | 		} | ||||||
|  | 	}() | ||||||
|  |  | ||||||
|  | 	u, err := user_model.GetUserByName(uname) | ||||||
|  | 	if err != nil { | ||||||
|  | 		if !user_model.IsErrUserNotExist(err) { | ||||||
|  | 			return false, fmt.Errorf("GetUserByName: %v", err) | ||||||
|  | 		} | ||||||
|  | 		return false, nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if val, ok := ctx.GetSuperSecureCookie( | ||||||
|  | 		base.EncodeMD5(u.Rands+u.Passwd), setting.CookieRememberName); !ok || val != u.Name { | ||||||
|  | 		return false, nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	isSucceed = true | ||||||
|  |  | ||||||
|  | 	if _, err := session.RegenerateSession(ctx.Resp, ctx.Req); err != nil { | ||||||
|  | 		return false, fmt.Errorf("unable to RegenerateSession: Error: %w", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Set session IDs | ||||||
|  | 	if err := ctx.Session.Set("uid", u.ID); err != nil { | ||||||
|  | 		return false, err | ||||||
|  | 	} | ||||||
|  | 	if err := ctx.Session.Set("uname", u.Name); err != nil { | ||||||
|  | 		return false, err | ||||||
|  | 	} | ||||||
|  | 	if err := ctx.Session.Release(); err != nil { | ||||||
|  | 		return false, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err := resetLocale(ctx, u); err != nil { | ||||||
|  | 		return false, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	middleware.DeleteCSRFCookie(ctx.Resp) | ||||||
|  | 	return true, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func resetLocale(ctx *context.Context, u *user_model.User) error { | ||||||
|  | 	// Language setting of the user overwrites the one previously set | ||||||
|  | 	// If the user does not have a locale set, we save the current one. | ||||||
|  | 	if len(u.Language) == 0 { | ||||||
|  | 		u.Language = ctx.Locale.Language() | ||||||
|  | 		if err := user_model.UpdateUserCols(db.DefaultContext, u, "language"); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	middleware.SetLocaleCookie(ctx.Resp, u.Language, 0) | ||||||
|  |  | ||||||
|  | 	if ctx.Locale.Language() != u.Language { | ||||||
|  | 		ctx.Locale = middleware.Locale(ctx.Resp, ctx.Req) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func checkAutoLogin(ctx *context.Context) bool { | ||||||
|  | 	// Check auto-login | ||||||
|  | 	isSucceed, err := AutoSignIn(ctx) | ||||||
|  | 	if err != nil { | ||||||
|  | 		ctx.ServerError("AutoSignIn", err) | ||||||
|  | 		return true | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	redirectTo := ctx.FormString("redirect_to") | ||||||
|  | 	if len(redirectTo) > 0 { | ||||||
|  | 		middleware.SetRedirectToCookie(ctx.Resp, redirectTo) | ||||||
|  | 	} else { | ||||||
|  | 		redirectTo = ctx.GetCookie("redirect_to") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if isSucceed { | ||||||
|  | 		middleware.DeleteRedirectToCookie(ctx.Resp) | ||||||
|  | 		ctx.RedirectToFirst(redirectTo, setting.AppSubURL+string(setting.LandingPageURL)) | ||||||
|  | 		return true | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return false | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SignIn render sign in page | ||||||
|  | func SignIn(ctx *context.Context) { | ||||||
|  | 	ctx.Data["Title"] = ctx.Tr("sign_in") | ||||||
|  |  | ||||||
|  | 	// Check auto-login | ||||||
|  | 	if checkAutoLogin(ctx) { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	orderedOAuth2Names, oauth2Providers, err := oauth2.GetActiveOAuth2Providers() | ||||||
|  | 	if err != nil { | ||||||
|  | 		ctx.ServerError("UserSignIn", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	ctx.Data["OrderedOAuth2Names"] = orderedOAuth2Names | ||||||
|  | 	ctx.Data["OAuth2Providers"] = oauth2Providers | ||||||
|  | 	ctx.Data["Title"] = ctx.Tr("sign_in") | ||||||
|  | 	ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login" | ||||||
|  | 	ctx.Data["PageIsSignIn"] = true | ||||||
|  | 	ctx.Data["PageIsLogin"] = true | ||||||
|  | 	ctx.Data["EnableSSPI"] = auth.IsSSPIEnabled() | ||||||
|  |  | ||||||
|  | 	ctx.HTML(http.StatusOK, tplSignIn) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SignInPost response for sign in request | ||||||
|  | func SignInPost(ctx *context.Context) { | ||||||
|  | 	ctx.Data["Title"] = ctx.Tr("sign_in") | ||||||
|  |  | ||||||
|  | 	orderedOAuth2Names, oauth2Providers, err := oauth2.GetActiveOAuth2Providers() | ||||||
|  | 	if err != nil { | ||||||
|  | 		ctx.ServerError("UserSignIn", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	ctx.Data["OrderedOAuth2Names"] = orderedOAuth2Names | ||||||
|  | 	ctx.Data["OAuth2Providers"] = oauth2Providers | ||||||
|  | 	ctx.Data["Title"] = ctx.Tr("sign_in") | ||||||
|  | 	ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login" | ||||||
|  | 	ctx.Data["PageIsSignIn"] = true | ||||||
|  | 	ctx.Data["PageIsLogin"] = true | ||||||
|  | 	ctx.Data["EnableSSPI"] = auth.IsSSPIEnabled() | ||||||
|  |  | ||||||
|  | 	if ctx.HasError() { | ||||||
|  | 		ctx.HTML(http.StatusOK, tplSignIn) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	form := web.GetForm(ctx).(*forms.SignInForm) | ||||||
|  | 	u, source, err := auth_service.UserSignIn(form.UserName, form.Password) | ||||||
|  | 	if err != nil { | ||||||
|  | 		if user_model.IsErrUserNotExist(err) { | ||||||
|  | 			ctx.RenderWithErr(ctx.Tr("form.username_password_incorrect"), tplSignIn, &form) | ||||||
|  | 			log.Info("Failed authentication attempt for %s from %s: %v", form.UserName, ctx.RemoteAddr(), err) | ||||||
|  | 		} else if user_model.IsErrEmailAlreadyUsed(err) { | ||||||
|  | 			ctx.RenderWithErr(ctx.Tr("form.email_been_used"), tplSignIn, &form) | ||||||
|  | 			log.Info("Failed authentication attempt for %s from %s: %v", form.UserName, ctx.RemoteAddr(), err) | ||||||
|  | 		} else if user_model.IsErrUserProhibitLogin(err) { | ||||||
|  | 			log.Info("Failed authentication attempt for %s from %s: %v", form.UserName, ctx.RemoteAddr(), err) | ||||||
|  | 			ctx.Data["Title"] = ctx.Tr("auth.prohibit_login") | ||||||
|  | 			ctx.HTML(http.StatusOK, "user/auth/prohibit_login") | ||||||
|  | 		} else if user_model.IsErrUserInactive(err) { | ||||||
|  | 			if setting.Service.RegisterEmailConfirm { | ||||||
|  | 				ctx.Data["Title"] = ctx.Tr("auth.active_your_account") | ||||||
|  | 				ctx.HTML(http.StatusOK, TplActivate) | ||||||
|  | 			} else { | ||||||
|  | 				log.Info("Failed authentication attempt for %s from %s: %v", form.UserName, ctx.RemoteAddr(), err) | ||||||
|  | 				ctx.Data["Title"] = ctx.Tr("auth.prohibit_login") | ||||||
|  | 				ctx.HTML(http.StatusOK, "user/auth/prohibit_login") | ||||||
|  | 			} | ||||||
|  | 		} else { | ||||||
|  | 			ctx.ServerError("UserSignIn", err) | ||||||
|  | 		} | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Now handle 2FA: | ||||||
|  |  | ||||||
|  | 	// First of all if the source can skip local two fa we're done | ||||||
|  | 	if skipper, ok := source.Cfg.(auth_service.LocalTwoFASkipper); ok && skipper.IsSkipLocalTwoFA() { | ||||||
|  | 		handleSignIn(ctx, u, form.Remember) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// If this user is enrolled in 2FA TOTP, we can't sign the user in just yet. | ||||||
|  | 	// Instead, redirect them to the 2FA authentication page. | ||||||
|  | 	hasTOTPtwofa, err := auth.HasTwoFactorByUID(u.ID) | ||||||
|  | 	if err != nil { | ||||||
|  | 		ctx.ServerError("UserSignIn", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Check if the user has u2f registration | ||||||
|  | 	hasU2Ftwofa, err := auth.HasU2FRegistrationsByUID(u.ID) | ||||||
|  | 	if err != nil { | ||||||
|  | 		ctx.ServerError("UserSignIn", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if !hasTOTPtwofa && !hasU2Ftwofa { | ||||||
|  | 		// No two factor auth configured we can sign in the user | ||||||
|  | 		handleSignIn(ctx, u, form.Remember) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if _, err := session.RegenerateSession(ctx.Resp, ctx.Req); err != nil { | ||||||
|  | 		ctx.ServerError("UserSignIn: Unable to set regenerate session", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// User will need to use 2FA TOTP or U2F, save data | ||||||
|  | 	if err := ctx.Session.Set("twofaUid", u.ID); err != nil { | ||||||
|  | 		ctx.ServerError("UserSignIn: Unable to set twofaUid in session", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err := ctx.Session.Set("twofaRemember", form.Remember); err != nil { | ||||||
|  | 		ctx.ServerError("UserSignIn: Unable to set twofaRemember in session", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if hasTOTPtwofa { | ||||||
|  | 		// User will need to use U2F, save data | ||||||
|  | 		if err := ctx.Session.Set("totpEnrolled", u.ID); err != nil { | ||||||
|  | 			ctx.ServerError("UserSignIn: Unable to set u2fEnrolled in session", err) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err := ctx.Session.Release(); err != nil { | ||||||
|  | 		ctx.ServerError("UserSignIn: Unable to save session", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// If we have U2F redirect there first | ||||||
|  | 	if hasU2Ftwofa { | ||||||
|  | 		ctx.Redirect(setting.AppSubURL + "/user/u2f") | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Fallback to 2FA | ||||||
|  | 	ctx.Redirect(setting.AppSubURL + "/user/two_factor") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // This handles the final part of the sign-in process of the user. | ||||||
|  | func handleSignIn(ctx *context.Context, u *user_model.User, remember bool) { | ||||||
|  | 	redirect := handleSignInFull(ctx, u, remember, true) | ||||||
|  | 	if ctx.Written() { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	ctx.Redirect(redirect) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func handleSignInFull(ctx *context.Context, u *user_model.User, remember bool, obeyRedirect bool) string { | ||||||
|  | 	if remember { | ||||||
|  | 		days := 86400 * setting.LogInRememberDays | ||||||
|  | 		ctx.SetCookie(setting.CookieUserName, u.Name, days) | ||||||
|  | 		ctx.SetSuperSecureCookie(base.EncodeMD5(u.Rands+u.Passwd), | ||||||
|  | 			setting.CookieRememberName, u.Name, days) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if _, err := session.RegenerateSession(ctx.Resp, ctx.Req); err != nil { | ||||||
|  | 		ctx.ServerError("RegenerateSession", err) | ||||||
|  | 		return setting.AppSubURL + "/" | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Delete the openid, 2fa and linkaccount data | ||||||
|  | 	_ = ctx.Session.Delete("openid_verified_uri") | ||||||
|  | 	_ = ctx.Session.Delete("openid_signin_remember") | ||||||
|  | 	_ = ctx.Session.Delete("openid_determined_email") | ||||||
|  | 	_ = ctx.Session.Delete("openid_determined_username") | ||||||
|  | 	_ = ctx.Session.Delete("twofaUid") | ||||||
|  | 	_ = ctx.Session.Delete("twofaRemember") | ||||||
|  | 	_ = ctx.Session.Delete("u2fChallenge") | ||||||
|  | 	_ = ctx.Session.Delete("linkAccount") | ||||||
|  | 	if err := ctx.Session.Set("uid", u.ID); err != nil { | ||||||
|  | 		log.Error("Error setting uid %d in session: %v", u.ID, err) | ||||||
|  | 	} | ||||||
|  | 	if err := ctx.Session.Set("uname", u.Name); err != nil { | ||||||
|  | 		log.Error("Error setting uname %s session: %v", u.Name, err) | ||||||
|  | 	} | ||||||
|  | 	if err := ctx.Session.Release(); err != nil { | ||||||
|  | 		log.Error("Unable to store session: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Language setting of the user overwrites the one previously set | ||||||
|  | 	// If the user does not have a locale set, we save the current one. | ||||||
|  | 	if len(u.Language) == 0 { | ||||||
|  | 		u.Language = ctx.Locale.Language() | ||||||
|  | 		if err := user_model.UpdateUserCols(db.DefaultContext, u, "language"); err != nil { | ||||||
|  | 			ctx.ServerError("UpdateUserCols Language", fmt.Errorf("Error updating user language [user: %d, locale: %s]", u.ID, u.Language)) | ||||||
|  | 			return setting.AppSubURL + "/" | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	middleware.SetLocaleCookie(ctx.Resp, u.Language, 0) | ||||||
|  |  | ||||||
|  | 	if ctx.Locale.Language() != u.Language { | ||||||
|  | 		ctx.Locale = middleware.Locale(ctx.Resp, ctx.Req) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Clear whatever CSRF has right now, force to generate a new one | ||||||
|  | 	middleware.DeleteCSRFCookie(ctx.Resp) | ||||||
|  |  | ||||||
|  | 	// Register last login | ||||||
|  | 	u.SetLastLogin() | ||||||
|  | 	if err := user_model.UpdateUserCols(db.DefaultContext, u, "last_login_unix"); err != nil { | ||||||
|  | 		ctx.ServerError("UpdateUserCols", err) | ||||||
|  | 		return setting.AppSubURL + "/" | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if redirectTo := ctx.GetCookie("redirect_to"); len(redirectTo) > 0 && !utils.IsExternalURL(redirectTo) { | ||||||
|  | 		middleware.DeleteRedirectToCookie(ctx.Resp) | ||||||
|  | 		if obeyRedirect { | ||||||
|  | 			ctx.RedirectToFirst(redirectTo) | ||||||
|  | 		} | ||||||
|  | 		return redirectTo | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if obeyRedirect { | ||||||
|  | 		ctx.Redirect(setting.AppSubURL + "/") | ||||||
|  | 	} | ||||||
|  | 	return setting.AppSubURL + "/" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func getUserName(gothUser *goth.User) string { | ||||||
|  | 	switch setting.OAuth2Client.Username { | ||||||
|  | 	case setting.OAuth2UsernameEmail: | ||||||
|  | 		return strings.Split(gothUser.Email, "@")[0] | ||||||
|  | 	case setting.OAuth2UsernameNickname: | ||||||
|  | 		return gothUser.NickName | ||||||
|  | 	default: // OAuth2UsernameUserid | ||||||
|  | 		return gothUser.UserID | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // HandleSignOut resets the session and sets the cookies | ||||||
|  | func HandleSignOut(ctx *context.Context) { | ||||||
|  | 	_ = ctx.Session.Flush() | ||||||
|  | 	_ = ctx.Session.Destroy(ctx.Resp, ctx.Req) | ||||||
|  | 	ctx.DeleteCookie(setting.CookieUserName) | ||||||
|  | 	ctx.DeleteCookie(setting.CookieRememberName) | ||||||
|  | 	middleware.DeleteCSRFCookie(ctx.Resp) | ||||||
|  | 	middleware.DeleteLocaleCookie(ctx.Resp) | ||||||
|  | 	middleware.DeleteRedirectToCookie(ctx.Resp) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SignOut sign out from login status | ||||||
|  | func SignOut(ctx *context.Context) { | ||||||
|  | 	if ctx.User != nil { | ||||||
|  | 		eventsource.GetManager().SendMessageBlocking(ctx.User.ID, &eventsource.Event{ | ||||||
|  | 			Name: "logout", | ||||||
|  | 			Data: ctx.Session.ID(), | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | 	HandleSignOut(ctx) | ||||||
|  | 	ctx.Redirect(setting.AppSubURL + "/") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SignUp render the register page | ||||||
|  | func SignUp(ctx *context.Context) { | ||||||
|  | 	ctx.Data["Title"] = ctx.Tr("sign_up") | ||||||
|  |  | ||||||
|  | 	ctx.Data["SignUpLink"] = setting.AppSubURL + "/user/sign_up" | ||||||
|  |  | ||||||
|  | 	ctx.Data["EnableCaptcha"] = setting.Service.EnableCaptcha | ||||||
|  | 	ctx.Data["RecaptchaURL"] = setting.Service.RecaptchaURL | ||||||
|  | 	ctx.Data["Captcha"] = context.GetImageCaptcha() | ||||||
|  | 	ctx.Data["CaptchaType"] = setting.Service.CaptchaType | ||||||
|  | 	ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey | ||||||
|  | 	ctx.Data["HcaptchaSitekey"] = setting.Service.HcaptchaSitekey | ||||||
|  | 	ctx.Data["PageIsSignUp"] = true | ||||||
|  |  | ||||||
|  | 	//Show Disabled Registration message if DisableRegistration or AllowOnlyExternalRegistration options are true | ||||||
|  | 	ctx.Data["DisableRegistration"] = setting.Service.DisableRegistration || setting.Service.AllowOnlyExternalRegistration | ||||||
|  |  | ||||||
|  | 	ctx.HTML(http.StatusOK, tplSignUp) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SignUpPost response for sign up information submission | ||||||
|  | func SignUpPost(ctx *context.Context) { | ||||||
|  | 	form := web.GetForm(ctx).(*forms.RegisterForm) | ||||||
|  | 	ctx.Data["Title"] = ctx.Tr("sign_up") | ||||||
|  |  | ||||||
|  | 	ctx.Data["SignUpLink"] = setting.AppSubURL + "/user/sign_up" | ||||||
|  |  | ||||||
|  | 	ctx.Data["EnableCaptcha"] = setting.Service.EnableCaptcha | ||||||
|  | 	ctx.Data["RecaptchaURL"] = setting.Service.RecaptchaURL | ||||||
|  | 	ctx.Data["Captcha"] = context.GetImageCaptcha() | ||||||
|  | 	ctx.Data["CaptchaType"] = setting.Service.CaptchaType | ||||||
|  | 	ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey | ||||||
|  | 	ctx.Data["HcaptchaSitekey"] = setting.Service.HcaptchaSitekey | ||||||
|  | 	ctx.Data["PageIsSignUp"] = true | ||||||
|  |  | ||||||
|  | 	//Permission denied if DisableRegistration or AllowOnlyExternalRegistration options are true | ||||||
|  | 	if setting.Service.DisableRegistration || setting.Service.AllowOnlyExternalRegistration { | ||||||
|  | 		ctx.Error(http.StatusForbidden) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if ctx.HasError() { | ||||||
|  | 		ctx.HTML(http.StatusOK, tplSignUp) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if setting.Service.EnableCaptcha { | ||||||
|  | 		var valid bool | ||||||
|  | 		var err error | ||||||
|  | 		switch setting.Service.CaptchaType { | ||||||
|  | 		case setting.ImageCaptcha: | ||||||
|  | 			valid = context.GetImageCaptcha().VerifyReq(ctx.Req) | ||||||
|  | 		case setting.ReCaptcha: | ||||||
|  | 			valid, err = recaptcha.Verify(ctx, form.GRecaptchaResponse) | ||||||
|  | 		case setting.HCaptcha: | ||||||
|  | 			valid, err = hcaptcha.Verify(ctx, form.HcaptchaResponse) | ||||||
|  | 		default: | ||||||
|  | 			ctx.ServerError("Unknown Captcha Type", fmt.Errorf("Unknown Captcha Type: %s", setting.Service.CaptchaType)) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		if err != nil { | ||||||
|  | 			log.Debug("%s", err.Error()) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if !valid { | ||||||
|  | 			ctx.Data["Err_Captcha"] = true | ||||||
|  | 			ctx.RenderWithErr(ctx.Tr("form.captcha_incorrect"), tplSignUp, &form) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if !form.IsEmailDomainAllowed() { | ||||||
|  | 		ctx.RenderWithErr(ctx.Tr("auth.email_domain_blacklisted"), tplSignUp, &form) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if form.Password != form.Retype { | ||||||
|  | 		ctx.Data["Err_Password"] = true | ||||||
|  | 		ctx.RenderWithErr(ctx.Tr("form.password_not_match"), tplSignUp, &form) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	if len(form.Password) < setting.MinPasswordLength { | ||||||
|  | 		ctx.Data["Err_Password"] = true | ||||||
|  | 		ctx.RenderWithErr(ctx.Tr("auth.password_too_short", setting.MinPasswordLength), tplSignUp, &form) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	if !password.IsComplexEnough(form.Password) { | ||||||
|  | 		ctx.Data["Err_Password"] = true | ||||||
|  | 		ctx.RenderWithErr(password.BuildComplexityError(ctx), tplSignUp, &form) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	pwned, err := password.IsPwned(ctx, form.Password) | ||||||
|  | 	if pwned { | ||||||
|  | 		errMsg := ctx.Tr("auth.password_pwned") | ||||||
|  | 		if err != nil { | ||||||
|  | 			log.Error(err.Error()) | ||||||
|  | 			errMsg = ctx.Tr("auth.password_pwned_err") | ||||||
|  | 		} | ||||||
|  | 		ctx.Data["Err_Password"] = true | ||||||
|  | 		ctx.RenderWithErr(errMsg, tplSignUp, &form) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	u := &user_model.User{ | ||||||
|  | 		Name:         form.UserName, | ||||||
|  | 		Email:        form.Email, | ||||||
|  | 		Passwd:       form.Password, | ||||||
|  | 		IsActive:     !(setting.Service.RegisterEmailConfirm || setting.Service.RegisterManualConfirm), | ||||||
|  | 		IsRestricted: setting.Service.DefaultUserIsRestricted, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if !createAndHandleCreatedUser(ctx, tplSignUp, form, u, nil, false) { | ||||||
|  | 		// error already handled | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ctx.Flash.Success(ctx.Tr("auth.sign_up_successful")) | ||||||
|  | 	handleSignIn(ctx, u, false) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // createAndHandleCreatedUser calls createUserInContext and | ||||||
|  | // then handleUserCreated. | ||||||
|  | func createAndHandleCreatedUser(ctx *context.Context, tpl base.TplName, form interface{}, u *user_model.User, gothUser *goth.User, allowLink bool) bool { | ||||||
|  | 	if !createUserInContext(ctx, tpl, form, u, gothUser, allowLink) { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 	return handleUserCreated(ctx, u, gothUser) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // createUserInContext creates a user and handles errors within a given context. | ||||||
|  | // Optionally a template can be specified. | ||||||
|  | func createUserInContext(ctx *context.Context, tpl base.TplName, form interface{}, u *user_model.User, gothUser *goth.User, allowLink bool) (ok bool) { | ||||||
|  | 	if err := user_model.CreateUser(u); err != nil { | ||||||
|  | 		if allowLink && (user_model.IsErrUserAlreadyExist(err) || user_model.IsErrEmailAlreadyUsed(err)) { | ||||||
|  | 			if setting.OAuth2Client.AccountLinking == setting.OAuth2AccountLinkingAuto { | ||||||
|  | 				var user *user_model.User | ||||||
|  | 				user = &user_model.User{Name: u.Name} | ||||||
|  | 				hasUser, err := user_model.GetUser(user) | ||||||
|  | 				if !hasUser || err != nil { | ||||||
|  | 					user = &user_model.User{Email: u.Email} | ||||||
|  | 					hasUser, err = user_model.GetUser(user) | ||||||
|  | 					if !hasUser || err != nil { | ||||||
|  | 						ctx.ServerError("UserLinkAccount", err) | ||||||
|  | 						return | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				// TODO: probably we should respect 'remember' user's choice... | ||||||
|  | 				linkAccount(ctx, user, *gothUser, true) | ||||||
|  | 				return // user is already created here, all redirects are handled | ||||||
|  | 			} else if setting.OAuth2Client.AccountLinking == setting.OAuth2AccountLinkingLogin { | ||||||
|  | 				showLinkingLogin(ctx, *gothUser) | ||||||
|  | 				return // user will be created only after linking login | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// handle error without template | ||||||
|  | 		if len(tpl) == 0 { | ||||||
|  | 			ctx.ServerError("CreateUser", err) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// handle error with template | ||||||
|  | 		switch { | ||||||
|  | 		case user_model.IsErrUserAlreadyExist(err): | ||||||
|  | 			ctx.Data["Err_UserName"] = true | ||||||
|  | 			ctx.RenderWithErr(ctx.Tr("form.username_been_taken"), tpl, form) | ||||||
|  | 		case user_model.IsErrEmailAlreadyUsed(err): | ||||||
|  | 			ctx.Data["Err_Email"] = true | ||||||
|  | 			ctx.RenderWithErr(ctx.Tr("form.email_been_used"), tpl, form) | ||||||
|  | 		case user_model.IsErrEmailInvalid(err): | ||||||
|  | 			ctx.Data["Err_Email"] = true | ||||||
|  | 			ctx.RenderWithErr(ctx.Tr("form.email_invalid"), tpl, form) | ||||||
|  | 		case db.IsErrNameReserved(err): | ||||||
|  | 			ctx.Data["Err_UserName"] = true | ||||||
|  | 			ctx.RenderWithErr(ctx.Tr("user.form.name_reserved", err.(db.ErrNameReserved).Name), tpl, form) | ||||||
|  | 		case db.IsErrNamePatternNotAllowed(err): | ||||||
|  | 			ctx.Data["Err_UserName"] = true | ||||||
|  | 			ctx.RenderWithErr(ctx.Tr("user.form.name_pattern_not_allowed", err.(db.ErrNamePatternNotAllowed).Pattern), tpl, form) | ||||||
|  | 		case db.IsErrNameCharsNotAllowed(err): | ||||||
|  | 			ctx.Data["Err_UserName"] = true | ||||||
|  | 			ctx.RenderWithErr(ctx.Tr("user.form.name_chars_not_allowed", err.(db.ErrNameCharsNotAllowed).Name), tpl, form) | ||||||
|  | 		default: | ||||||
|  | 			ctx.ServerError("CreateUser", err) | ||||||
|  | 		} | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	log.Trace("Account created: %s", u.Name) | ||||||
|  | 	return true | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // handleUserCreated does additional steps after a new user is created. | ||||||
|  | // It auto-sets admin for the only user, updates the optional external user and | ||||||
|  | // sends a confirmation email if required. | ||||||
|  | func handleUserCreated(ctx *context.Context, u *user_model.User, gothUser *goth.User) (ok bool) { | ||||||
|  | 	// Auto-set admin for the only user. | ||||||
|  | 	if user_model.CountUsers() == 1 { | ||||||
|  | 		u.IsAdmin = true | ||||||
|  | 		u.IsActive = true | ||||||
|  | 		u.SetLastLogin() | ||||||
|  | 		if err := user_model.UpdateUserCols(db.DefaultContext, u, "is_admin", "is_active", "last_login_unix"); err != nil { | ||||||
|  | 			ctx.ServerError("UpdateUser", err) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// update external user information | ||||||
|  | 	if gothUser != nil { | ||||||
|  | 		if err := externalaccount.UpdateExternalUser(u, *gothUser); err != nil { | ||||||
|  | 			log.Error("UpdateExternalUser failed: %v", err) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Send confirmation email | ||||||
|  | 	if !u.IsActive && u.ID > 1 { | ||||||
|  | 		mailer.SendActivateAccountMail(ctx.Locale, u) | ||||||
|  |  | ||||||
|  | 		ctx.Data["IsSendRegisterMail"] = true | ||||||
|  | 		ctx.Data["Email"] = u.Email | ||||||
|  | 		ctx.Data["ActiveCodeLives"] = timeutil.MinutesToFriendly(setting.Service.ActiveCodeLives, ctx.Locale.Language()) | ||||||
|  | 		ctx.HTML(http.StatusOK, TplActivate) | ||||||
|  |  | ||||||
|  | 		if err := ctx.Cache.Put("MailResendLimit_"+u.LowerName, u.LowerName, 180); err != nil { | ||||||
|  | 			log.Error("Set cache(MailResendLimit) fail: %v", err) | ||||||
|  | 		} | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return true | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Activate render activate user page | ||||||
|  | func Activate(ctx *context.Context) { | ||||||
|  | 	code := ctx.FormString("code") | ||||||
|  |  | ||||||
|  | 	if len(code) == 0 { | ||||||
|  | 		ctx.Data["IsActivatePage"] = true | ||||||
|  | 		if ctx.User == nil || ctx.User.IsActive { | ||||||
|  | 			ctx.NotFound("invalid user", nil) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		// Resend confirmation email. | ||||||
|  | 		if setting.Service.RegisterEmailConfirm { | ||||||
|  | 			if ctx.Cache.IsExist("MailResendLimit_" + ctx.User.LowerName) { | ||||||
|  | 				ctx.Data["ResendLimited"] = true | ||||||
|  | 			} else { | ||||||
|  | 				ctx.Data["ActiveCodeLives"] = timeutil.MinutesToFriendly(setting.Service.ActiveCodeLives, ctx.Locale.Language()) | ||||||
|  | 				mailer.SendActivateAccountMail(ctx.Locale, ctx.User) | ||||||
|  |  | ||||||
|  | 				if err := ctx.Cache.Put("MailResendLimit_"+ctx.User.LowerName, ctx.User.LowerName, 180); err != nil { | ||||||
|  | 					log.Error("Set cache(MailResendLimit) fail: %v", err) | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} else { | ||||||
|  | 			ctx.Data["ServiceNotEnabled"] = true | ||||||
|  | 		} | ||||||
|  | 		ctx.HTML(http.StatusOK, TplActivate) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	user := user_model.VerifyUserActiveCode(code) | ||||||
|  | 	// if code is wrong | ||||||
|  | 	if user == nil { | ||||||
|  | 		ctx.Data["IsActivateFailed"] = true | ||||||
|  | 		ctx.HTML(http.StatusOK, TplActivate) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// if account is local account, verify password | ||||||
|  | 	if user.LoginSource == 0 { | ||||||
|  | 		ctx.Data["Code"] = code | ||||||
|  | 		ctx.Data["NeedsPassword"] = true | ||||||
|  | 		ctx.HTML(http.StatusOK, TplActivate) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	handleAccountActivation(ctx, user) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ActivatePost handles account activation with password check | ||||||
|  | func ActivatePost(ctx *context.Context) { | ||||||
|  | 	code := ctx.FormString("code") | ||||||
|  | 	if len(code) == 0 { | ||||||
|  | 		ctx.Redirect(setting.AppSubURL + "/user/activate") | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	user := user_model.VerifyUserActiveCode(code) | ||||||
|  | 	// if code is wrong | ||||||
|  | 	if user == nil { | ||||||
|  | 		ctx.Data["IsActivateFailed"] = true | ||||||
|  | 		ctx.HTML(http.StatusOK, TplActivate) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// if account is local account, verify password | ||||||
|  | 	if user.LoginSource == 0 { | ||||||
|  | 		password := ctx.FormString("password") | ||||||
|  | 		if len(password) == 0 { | ||||||
|  | 			ctx.Data["Code"] = code | ||||||
|  | 			ctx.Data["NeedsPassword"] = true | ||||||
|  | 			ctx.HTML(http.StatusOK, TplActivate) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		if !user.ValidatePassword(password) { | ||||||
|  | 			ctx.Data["IsActivateFailed"] = true | ||||||
|  | 			ctx.HTML(http.StatusOK, TplActivate) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	handleAccountActivation(ctx, user) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func handleAccountActivation(ctx *context.Context, user *user_model.User) { | ||||||
|  | 	user.IsActive = true | ||||||
|  | 	var err error | ||||||
|  | 	if user.Rands, err = user_model.GetUserSalt(); err != nil { | ||||||
|  | 		ctx.ServerError("UpdateUser", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	if err := user_model.UpdateUserCols(db.DefaultContext, user, "is_active", "rands"); err != nil { | ||||||
|  | 		if user_model.IsErrUserNotExist(err) { | ||||||
|  | 			ctx.NotFound("UpdateUserCols", err) | ||||||
|  | 		} else { | ||||||
|  | 			ctx.ServerError("UpdateUser", err) | ||||||
|  | 		} | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err := user_model.ActivateUserEmail(user.ID, user.Email, true); err != nil { | ||||||
|  | 		log.Error("Unable to activate email for user: %-v with email: %s: %v", user, user.Email, err) | ||||||
|  | 		ctx.ServerError("ActivateUserEmail", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	log.Trace("User activated: %s", user.Name) | ||||||
|  |  | ||||||
|  | 	if _, err := session.RegenerateSession(ctx.Resp, ctx.Req); err != nil { | ||||||
|  | 		log.Error("Unable to regenerate session for user: %-v with email: %s: %v", user, user.Email, err) | ||||||
|  | 		ctx.ServerError("ActivateUserEmail", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err := ctx.Session.Set("uid", user.ID); err != nil { | ||||||
|  | 		log.Error("Error setting uid in session[%s]: %v", ctx.Session.ID(), err) | ||||||
|  | 	} | ||||||
|  | 	if err := ctx.Session.Set("uname", user.Name); err != nil { | ||||||
|  | 		log.Error("Error setting uname in session[%s]: %v", ctx.Session.ID(), err) | ||||||
|  | 	} | ||||||
|  | 	if err := ctx.Session.Release(); err != nil { | ||||||
|  | 		log.Error("Error storing session[%s]: %v", ctx.Session.ID(), err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err := resetLocale(ctx, user); err != nil { | ||||||
|  | 		ctx.ServerError("resetLocale", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ctx.Flash.Success(ctx.Tr("auth.account_activated")) | ||||||
|  | 	ctx.Redirect(setting.AppSubURL + "/") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ActivateEmail render the activate email page | ||||||
|  | func ActivateEmail(ctx *context.Context) { | ||||||
|  | 	code := ctx.FormString("code") | ||||||
|  | 	emailStr := ctx.FormString("email") | ||||||
|  |  | ||||||
|  | 	// Verify code. | ||||||
|  | 	if email := user_model.VerifyActiveEmailCode(code, emailStr); email != nil { | ||||||
|  | 		if err := user_model.ActivateEmail(email); err != nil { | ||||||
|  | 			ctx.ServerError("ActivateEmail", err) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		log.Trace("Email activated: %s", email.Email) | ||||||
|  | 		ctx.Flash.Success(ctx.Tr("settings.add_email_success")) | ||||||
|  |  | ||||||
|  | 		if u, err := user_model.GetUserByID(email.UID); err != nil { | ||||||
|  | 			log.Warn("GetUserByID: %d", email.UID) | ||||||
|  | 		} else { | ||||||
|  | 			// Allow user to validate more emails | ||||||
|  | 			_ = ctx.Cache.Delete("MailResendLimit_" + u.LowerName) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// FIXME: e-mail verification does not require the user to be logged in, | ||||||
|  | 	// so this could be redirecting to the login page. | ||||||
|  | 	// Should users be logged in automatically here? (consider 2FA requirements, etc.) | ||||||
|  | 	ctx.Redirect(setting.AppSubURL + "/user/settings/account") | ||||||
|  | } | ||||||
							
								
								
									
										300
									
								
								routers/web/auth/linkaccount.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										300
									
								
								routers/web/auth/linkaccount.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,300 @@ | |||||||
|  | // Copyright 2017 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 auth | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"errors" | ||||||
|  | 	"fmt" | ||||||
|  | 	"net/http" | ||||||
|  | 	"strings" | ||||||
|  |  | ||||||
|  | 	"code.gitea.io/gitea/models/auth" | ||||||
|  | 	user_model "code.gitea.io/gitea/models/user" | ||||||
|  | 	"code.gitea.io/gitea/modules/base" | ||||||
|  | 	"code.gitea.io/gitea/modules/context" | ||||||
|  | 	"code.gitea.io/gitea/modules/hcaptcha" | ||||||
|  | 	"code.gitea.io/gitea/modules/log" | ||||||
|  | 	"code.gitea.io/gitea/modules/recaptcha" | ||||||
|  | 	"code.gitea.io/gitea/modules/session" | ||||||
|  | 	"code.gitea.io/gitea/modules/setting" | ||||||
|  | 	"code.gitea.io/gitea/modules/web" | ||||||
|  | 	auth_service "code.gitea.io/gitea/services/auth" | ||||||
|  | 	"code.gitea.io/gitea/services/externalaccount" | ||||||
|  | 	"code.gitea.io/gitea/services/forms" | ||||||
|  |  | ||||||
|  | 	"github.com/markbates/goth" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | var ( | ||||||
|  | 	tplLinkAccount base.TplName = "user/auth/link_account" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // LinkAccount shows the page where the user can decide to login or create a new account | ||||||
|  | func LinkAccount(ctx *context.Context) { | ||||||
|  | 	ctx.Data["DisablePassword"] = !setting.Service.RequireExternalRegistrationPassword || setting.Service.AllowOnlyExternalRegistration | ||||||
|  | 	ctx.Data["Title"] = ctx.Tr("link_account") | ||||||
|  | 	ctx.Data["LinkAccountMode"] = true | ||||||
|  | 	ctx.Data["EnableCaptcha"] = setting.Service.EnableCaptcha && setting.Service.RequireExternalRegistrationCaptcha | ||||||
|  | 	ctx.Data["Captcha"] = context.GetImageCaptcha() | ||||||
|  | 	ctx.Data["CaptchaType"] = setting.Service.CaptchaType | ||||||
|  | 	ctx.Data["RecaptchaURL"] = setting.Service.RecaptchaURL | ||||||
|  | 	ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey | ||||||
|  | 	ctx.Data["HcaptchaSitekey"] = setting.Service.HcaptchaSitekey | ||||||
|  | 	ctx.Data["DisableRegistration"] = setting.Service.DisableRegistration | ||||||
|  | 	ctx.Data["AllowOnlyInternalRegistration"] = setting.Service.AllowOnlyInternalRegistration | ||||||
|  | 	ctx.Data["ShowRegistrationButton"] = false | ||||||
|  |  | ||||||
|  | 	// use this to set the right link into the signIn and signUp templates in the link_account template | ||||||
|  | 	ctx.Data["SignInLink"] = setting.AppSubURL + "/user/link_account_signin" | ||||||
|  | 	ctx.Data["SignUpLink"] = setting.AppSubURL + "/user/link_account_signup" | ||||||
|  |  | ||||||
|  | 	gothUser := ctx.Session.Get("linkAccountGothUser") | ||||||
|  | 	if gothUser == nil { | ||||||
|  | 		ctx.ServerError("UserSignIn", errors.New("not in LinkAccount session")) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	gu, _ := gothUser.(goth.User) | ||||||
|  | 	uname := getUserName(&gu) | ||||||
|  | 	email := gu.Email | ||||||
|  | 	ctx.Data["user_name"] = uname | ||||||
|  | 	ctx.Data["email"] = email | ||||||
|  |  | ||||||
|  | 	if len(email) != 0 { | ||||||
|  | 		u, err := user_model.GetUserByEmail(email) | ||||||
|  | 		if err != nil && !user_model.IsErrUserNotExist(err) { | ||||||
|  | 			ctx.ServerError("UserSignIn", err) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		if u != nil { | ||||||
|  | 			ctx.Data["user_exists"] = true | ||||||
|  | 		} | ||||||
|  | 	} else if len(uname) != 0 { | ||||||
|  | 		u, err := user_model.GetUserByName(uname) | ||||||
|  | 		if err != nil && !user_model.IsErrUserNotExist(err) { | ||||||
|  | 			ctx.ServerError("UserSignIn", err) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		if u != nil { | ||||||
|  | 			ctx.Data["user_exists"] = true | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ctx.HTML(http.StatusOK, tplLinkAccount) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // LinkAccountPostSignIn handle the coupling of external account with another account using signIn | ||||||
|  | func LinkAccountPostSignIn(ctx *context.Context) { | ||||||
|  | 	signInForm := web.GetForm(ctx).(*forms.SignInForm) | ||||||
|  | 	ctx.Data["DisablePassword"] = !setting.Service.RequireExternalRegistrationPassword || setting.Service.AllowOnlyExternalRegistration | ||||||
|  | 	ctx.Data["Title"] = ctx.Tr("link_account") | ||||||
|  | 	ctx.Data["LinkAccountMode"] = true | ||||||
|  | 	ctx.Data["LinkAccountModeSignIn"] = true | ||||||
|  | 	ctx.Data["EnableCaptcha"] = setting.Service.EnableCaptcha && setting.Service.RequireExternalRegistrationCaptcha | ||||||
|  | 	ctx.Data["RecaptchaURL"] = setting.Service.RecaptchaURL | ||||||
|  | 	ctx.Data["Captcha"] = context.GetImageCaptcha() | ||||||
|  | 	ctx.Data["CaptchaType"] = setting.Service.CaptchaType | ||||||
|  | 	ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey | ||||||
|  | 	ctx.Data["HcaptchaSitekey"] = setting.Service.HcaptchaSitekey | ||||||
|  | 	ctx.Data["DisableRegistration"] = setting.Service.DisableRegistration | ||||||
|  | 	ctx.Data["ShowRegistrationButton"] = false | ||||||
|  |  | ||||||
|  | 	// use this to set the right link into the signIn and signUp templates in the link_account template | ||||||
|  | 	ctx.Data["SignInLink"] = setting.AppSubURL + "/user/link_account_signin" | ||||||
|  | 	ctx.Data["SignUpLink"] = setting.AppSubURL + "/user/link_account_signup" | ||||||
|  |  | ||||||
|  | 	gothUser := ctx.Session.Get("linkAccountGothUser") | ||||||
|  | 	if gothUser == nil { | ||||||
|  | 		ctx.ServerError("UserSignIn", errors.New("not in LinkAccount session")) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if ctx.HasError() { | ||||||
|  | 		ctx.HTML(http.StatusOK, tplLinkAccount) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	u, _, err := auth_service.UserSignIn(signInForm.UserName, signInForm.Password) | ||||||
|  | 	if err != nil { | ||||||
|  | 		if user_model.IsErrUserNotExist(err) { | ||||||
|  | 			ctx.Data["user_exists"] = true | ||||||
|  | 			ctx.RenderWithErr(ctx.Tr("form.username_password_incorrect"), tplLinkAccount, &signInForm) | ||||||
|  | 		} else { | ||||||
|  | 			ctx.ServerError("UserLinkAccount", err) | ||||||
|  | 		} | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	linkAccount(ctx, u, gothUser.(goth.User), signInForm.Remember) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func linkAccount(ctx *context.Context, u *user_model.User, gothUser goth.User, remember bool) { | ||||||
|  | 	updateAvatarIfNeed(gothUser.AvatarURL, u) | ||||||
|  |  | ||||||
|  | 	// If this user is enrolled in 2FA, we can't sign the user in just yet. | ||||||
|  | 	// Instead, redirect them to the 2FA authentication page. | ||||||
|  | 	// We deliberately ignore the skip local 2fa setting here because we are linking to a previous user here | ||||||
|  | 	_, err := auth.GetTwoFactorByUID(u.ID) | ||||||
|  | 	if err != nil { | ||||||
|  | 		if !auth.IsErrTwoFactorNotEnrolled(err) { | ||||||
|  | 			ctx.ServerError("UserLinkAccount", err) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		err = externalaccount.LinkAccountToUser(u, gothUser) | ||||||
|  | 		if err != nil { | ||||||
|  | 			ctx.ServerError("UserLinkAccount", err) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		handleSignIn(ctx, u, remember) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if _, err := session.RegenerateSession(ctx.Resp, ctx.Req); err != nil { | ||||||
|  | 		ctx.ServerError("RegenerateSession", 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) | ||||||
|  | 	} | ||||||
|  | 	if err := ctx.Session.Set("twofaRemember", remember); err != nil { | ||||||
|  | 		log.Error("Error setting twofaRemember in session: %v", err) | ||||||
|  | 	} | ||||||
|  | 	if err := ctx.Session.Set("linkAccount", true); err != nil { | ||||||
|  | 		log.Error("Error setting linkAccount in session: %v", err) | ||||||
|  | 	} | ||||||
|  | 	if err := ctx.Session.Release(); err != nil { | ||||||
|  | 		log.Error("Error storing session: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// If U2F is enrolled -> Redirect to U2F instead | ||||||
|  | 	regs, err := auth.GetU2FRegistrationsByUID(u.ID) | ||||||
|  | 	if err == nil && len(regs) > 0 { | ||||||
|  | 		ctx.Redirect(setting.AppSubURL + "/user/u2f") | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ctx.Redirect(setting.AppSubURL + "/user/two_factor") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // LinkAccountPostRegister handle the creation of a new account for an external account using signUp | ||||||
|  | func LinkAccountPostRegister(ctx *context.Context) { | ||||||
|  | 	form := web.GetForm(ctx).(*forms.RegisterForm) | ||||||
|  | 	// TODO Make insecure passwords optional for local accounts also, | ||||||
|  | 	//      once email-based Second-Factor Auth is available | ||||||
|  | 	ctx.Data["DisablePassword"] = !setting.Service.RequireExternalRegistrationPassword || setting.Service.AllowOnlyExternalRegistration | ||||||
|  | 	ctx.Data["Title"] = ctx.Tr("link_account") | ||||||
|  | 	ctx.Data["LinkAccountMode"] = true | ||||||
|  | 	ctx.Data["LinkAccountModeRegister"] = true | ||||||
|  | 	ctx.Data["EnableCaptcha"] = setting.Service.EnableCaptcha && setting.Service.RequireExternalRegistrationCaptcha | ||||||
|  | 	ctx.Data["RecaptchaURL"] = setting.Service.RecaptchaURL | ||||||
|  | 	ctx.Data["Captcha"] = context.GetImageCaptcha() | ||||||
|  | 	ctx.Data["CaptchaType"] = setting.Service.CaptchaType | ||||||
|  | 	ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey | ||||||
|  | 	ctx.Data["HcaptchaSitekey"] = setting.Service.HcaptchaSitekey | ||||||
|  | 	ctx.Data["DisableRegistration"] = setting.Service.DisableRegistration | ||||||
|  | 	ctx.Data["ShowRegistrationButton"] = false | ||||||
|  |  | ||||||
|  | 	// use this to set the right link into the signIn and signUp templates in the link_account template | ||||||
|  | 	ctx.Data["SignInLink"] = setting.AppSubURL + "/user/link_account_signin" | ||||||
|  | 	ctx.Data["SignUpLink"] = setting.AppSubURL + "/user/link_account_signup" | ||||||
|  |  | ||||||
|  | 	gothUserInterface := ctx.Session.Get("linkAccountGothUser") | ||||||
|  | 	if gothUserInterface == nil { | ||||||
|  | 		ctx.ServerError("UserSignUp", errors.New("not in LinkAccount session")) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	gothUser, ok := gothUserInterface.(goth.User) | ||||||
|  | 	if !ok { | ||||||
|  | 		ctx.ServerError("UserSignUp", fmt.Errorf("session linkAccountGothUser type is %t but not goth.User", gothUserInterface)) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if ctx.HasError() { | ||||||
|  | 		ctx.HTML(http.StatusOK, tplLinkAccount) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if setting.Service.DisableRegistration || setting.Service.AllowOnlyInternalRegistration { | ||||||
|  | 		ctx.Error(http.StatusForbidden) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if setting.Service.EnableCaptcha && setting.Service.RequireExternalRegistrationCaptcha { | ||||||
|  | 		var valid bool | ||||||
|  | 		var err error | ||||||
|  | 		switch setting.Service.CaptchaType { | ||||||
|  | 		case setting.ImageCaptcha: | ||||||
|  | 			valid = context.GetImageCaptcha().VerifyReq(ctx.Req) | ||||||
|  | 		case setting.ReCaptcha: | ||||||
|  | 			valid, err = recaptcha.Verify(ctx, form.GRecaptchaResponse) | ||||||
|  | 		case setting.HCaptcha: | ||||||
|  | 			valid, err = hcaptcha.Verify(ctx, form.HcaptchaResponse) | ||||||
|  | 		default: | ||||||
|  | 			ctx.ServerError("Unknown Captcha Type", fmt.Errorf("Unknown Captcha Type: %s", setting.Service.CaptchaType)) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		if err != nil { | ||||||
|  | 			log.Debug("%s", err.Error()) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if !valid { | ||||||
|  | 			ctx.Data["Err_Captcha"] = true | ||||||
|  | 			ctx.RenderWithErr(ctx.Tr("form.captcha_incorrect"), tplLinkAccount, &form) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if !form.IsEmailDomainAllowed() { | ||||||
|  | 		ctx.RenderWithErr(ctx.Tr("auth.email_domain_blacklisted"), tplLinkAccount, &form) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if setting.Service.AllowOnlyExternalRegistration || !setting.Service.RequireExternalRegistrationPassword { | ||||||
|  | 		// In user_model.User an empty password is classed as not set, so we set form.Password to empty. | ||||||
|  | 		// Eventually the database should be changed to indicate "Second Factor"-enabled accounts | ||||||
|  | 		// (accounts that do not introduce the security vulnerabilities of a password). | ||||||
|  | 		// If a user decides to circumvent second-factor security, and purposefully create a password, | ||||||
|  | 		// they can still do so using the "Recover Account" option. | ||||||
|  | 		form.Password = "" | ||||||
|  | 	} else { | ||||||
|  | 		if (len(strings.TrimSpace(form.Password)) > 0 || len(strings.TrimSpace(form.Retype)) > 0) && form.Password != form.Retype { | ||||||
|  | 			ctx.Data["Err_Password"] = true | ||||||
|  | 			ctx.RenderWithErr(ctx.Tr("form.password_not_match"), tplLinkAccount, &form) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		if len(strings.TrimSpace(form.Password)) > 0 && len(form.Password) < setting.MinPasswordLength { | ||||||
|  | 			ctx.Data["Err_Password"] = true | ||||||
|  | 			ctx.RenderWithErr(ctx.Tr("auth.password_too_short", setting.MinPasswordLength), tplLinkAccount, &form) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	authSource, err := auth.GetActiveOAuth2SourceByName(gothUser.Provider) | ||||||
|  | 	if err != nil { | ||||||
|  | 		ctx.ServerError("CreateUser", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	u := &user_model.User{ | ||||||
|  | 		Name:        form.UserName, | ||||||
|  | 		Email:       form.Email, | ||||||
|  | 		Passwd:      form.Password, | ||||||
|  | 		IsActive:    !(setting.Service.RegisterEmailConfirm || setting.Service.RegisterManualConfirm), | ||||||
|  | 		LoginType:   auth.OAuth2, | ||||||
|  | 		LoginSource: authSource.ID, | ||||||
|  | 		LoginName:   gothUser.UserID, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if !createAndHandleCreatedUser(ctx, tplLinkAccount, form, u, &gothUser, false) { | ||||||
|  | 		// error already handled | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	handleSignIn(ctx, u, false) | ||||||
|  | } | ||||||
							
								
								
									
										16
									
								
								routers/web/auth/main_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								routers/web/auth/main_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | |||||||
|  | // Copyright 2018 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 auth | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"path/filepath" | ||||||
|  | 	"testing" | ||||||
|  |  | ||||||
|  | 	"code.gitea.io/gitea/models/unittest" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestMain(m *testing.M) { | ||||||
|  | 	unittest.MainTest(m, filepath.Join("..", "..", "..")) | ||||||
|  | } | ||||||
| @@ -2,32 +2,40 @@ | |||||||
| // Use of this source code is governed by a MIT-style | // Use of this source code is governed by a MIT-style | ||||||
| // license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||||
| 
 | 
 | ||||||
| package user | package auth | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"encoding/base64" | 	"encoding/base64" | ||||||
|  | 	"errors" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"html" | 	"html" | ||||||
|  | 	"io" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"net/url" | 	"net/url" | ||||||
| 	"strings" | 	"strings" | ||||||
| 
 | 
 | ||||||
| 	"code.gitea.io/gitea/models" | 	"code.gitea.io/gitea/models" | ||||||
| 	"code.gitea.io/gitea/models/login" | 	"code.gitea.io/gitea/models/auth" | ||||||
|  | 	"code.gitea.io/gitea/models/db" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	"code.gitea.io/gitea/modules/base" | 	"code.gitea.io/gitea/modules/base" | ||||||
| 	"code.gitea.io/gitea/modules/context" | 	"code.gitea.io/gitea/modules/context" | ||||||
| 	"code.gitea.io/gitea/modules/json" | 	"code.gitea.io/gitea/modules/json" | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
|  | 	"code.gitea.io/gitea/modules/session" | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
| 	"code.gitea.io/gitea/modules/timeutil" | 	"code.gitea.io/gitea/modules/timeutil" | ||||||
| 	"code.gitea.io/gitea/modules/web" | 	"code.gitea.io/gitea/modules/web" | ||||||
| 	"code.gitea.io/gitea/services/auth" | 	"code.gitea.io/gitea/modules/web/middleware" | ||||||
|  | 	auth_service "code.gitea.io/gitea/services/auth" | ||||||
| 	"code.gitea.io/gitea/services/auth/source/oauth2" | 	"code.gitea.io/gitea/services/auth/source/oauth2" | ||||||
|  | 	"code.gitea.io/gitea/services/externalaccount" | ||||||
| 	"code.gitea.io/gitea/services/forms" | 	"code.gitea.io/gitea/services/forms" | ||||||
|  | 	user_service "code.gitea.io/gitea/services/user" | ||||||
| 
 | 
 | ||||||
| 	"gitea.com/go-chi/binding" | 	"gitea.com/go-chi/binding" | ||||||
| 	"github.com/golang-jwt/jwt" | 	"github.com/golang-jwt/jwt" | ||||||
|  | 	"github.com/markbates/goth" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| const ( | const ( | ||||||
| @@ -117,7 +125,7 @@ type AccessTokenResponse struct { | |||||||
| 	IDToken      string    `json:"id_token,omitempty"` | 	IDToken      string    `json:"id_token,omitempty"` | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func newAccessTokenResponse(grant *login.OAuth2Grant, serverKey, clientKey oauth2.JWTSigningKey) (*AccessTokenResponse, *AccessTokenError) { | func newAccessTokenResponse(grant *auth.OAuth2Grant, serverKey, clientKey oauth2.JWTSigningKey) (*AccessTokenResponse, *AccessTokenError) { | ||||||
| 	if setting.OAuth2.InvalidateRefreshTokens { | 	if setting.OAuth2.InvalidateRefreshTokens { | ||||||
| 		if err := grant.IncreaseCounter(); err != nil { | 		if err := grant.IncreaseCounter(); err != nil { | ||||||
| 			return nil, &AccessTokenError{ | 			return nil, &AccessTokenError{ | ||||||
| @@ -164,7 +172,7 @@ func newAccessTokenResponse(grant *login.OAuth2Grant, serverKey, clientKey oauth | |||||||
| 	// generate OpenID Connect id_token | 	// generate OpenID Connect id_token | ||||||
| 	signedIDToken := "" | 	signedIDToken := "" | ||||||
| 	if grant.ScopeContains("openid") { | 	if grant.ScopeContains("openid") { | ||||||
| 		app, err := login.GetOAuth2ApplicationByID(grant.ApplicationID) | 		app, err := auth.GetOAuth2ApplicationByID(grant.ApplicationID) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return nil, &AccessTokenError{ | 			return nil, &AccessTokenError{ | ||||||
| 				ErrorCode:        AccessTokenErrorCodeInvalidRequest, | 				ErrorCode:        AccessTokenErrorCodeInvalidRequest, | ||||||
| @@ -249,7 +257,7 @@ type userInfoResponse struct { | |||||||
| 
 | 
 | ||||||
| // InfoOAuth manages request for userinfo endpoint | // InfoOAuth manages request for userinfo endpoint | ||||||
| func InfoOAuth(ctx *context.Context) { | func InfoOAuth(ctx *context.Context) { | ||||||
| 	if ctx.User == nil || ctx.Data["AuthedMethod"] != (&auth.OAuth2{}).Name() { | 	if ctx.User == nil || ctx.Data["AuthedMethod"] != (&auth_service.OAuth2{}).Name() { | ||||||
| 		ctx.Resp.Header().Set("WWW-Authenticate", `Bearer realm=""`) | 		ctx.Resp.Header().Set("WWW-Authenticate", `Bearer realm=""`) | ||||||
| 		ctx.PlainText(http.StatusUnauthorized, "no valid authorization") | 		ctx.PlainText(http.StatusUnauthorized, "no valid authorization") | ||||||
| 		return | 		return | ||||||
| @@ -315,9 +323,9 @@ func IntrospectOAuth(ctx *context.Context) { | |||||||
| 	token, err := oauth2.ParseToken(form.Token, oauth2.DefaultSigningKey) | 	token, err := oauth2.ParseToken(form.Token, oauth2.DefaultSigningKey) | ||||||
| 	if err == nil { | 	if err == nil { | ||||||
| 		if token.Valid() == nil { | 		if token.Valid() == nil { | ||||||
| 			grant, err := login.GetOAuth2GrantByID(token.GrantID) | 			grant, err := auth.GetOAuth2GrantByID(token.GrantID) | ||||||
| 			if err == nil && grant != nil { | 			if err == nil && grant != nil { | ||||||
| 				app, err := login.GetOAuth2ApplicationByID(grant.ApplicationID) | 				app, err := auth.GetOAuth2ApplicationByID(grant.ApplicationID) | ||||||
| 				if err == nil && app != nil { | 				if err == nil && app != nil { | ||||||
| 					response.Active = true | 					response.Active = true | ||||||
| 					response.Scope = grant.Scope | 					response.Scope = grant.Scope | ||||||
| @@ -346,9 +354,9 @@ func AuthorizeOAuth(ctx *context.Context) { | |||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	app, err := login.GetOAuth2ApplicationByClientID(form.ClientID) | 	app, err := auth.GetOAuth2ApplicationByClientID(form.ClientID) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		if login.IsErrOauthClientIDInvalid(err) { | 		if auth.IsErrOauthClientIDInvalid(err) { | ||||||
| 			handleAuthorizeError(ctx, AuthorizeError{ | 			handleAuthorizeError(ctx, AuthorizeError{ | ||||||
| 				ErrorCode:        ErrorCodeUnauthorizedClient, | 				ErrorCode:        ErrorCodeUnauthorizedClient, | ||||||
| 				ErrorDescription: "Client ID not registered", | 				ErrorDescription: "Client ID not registered", | ||||||
| @@ -492,7 +500,7 @@ func GrantApplicationOAuth(ctx *context.Context) { | |||||||
| 		ctx.Error(http.StatusBadRequest) | 		ctx.Error(http.StatusBadRequest) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	app, err := login.GetOAuth2ApplicationByClientID(form.ClientID) | 	app, err := auth.GetOAuth2ApplicationByClientID(form.ClientID) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		ctx.ServerError("GetOAuth2ApplicationByClientID", err) | 		ctx.ServerError("GetOAuth2ApplicationByClientID", err) | ||||||
| 		return | 		return | ||||||
| @@ -630,7 +638,7 @@ func handleRefreshToken(ctx *context.Context, form forms.AccessTokenForm, server | |||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	// get grant before increasing counter | 	// get grant before increasing counter | ||||||
| 	grant, err := login.GetOAuth2GrantByID(token.GrantID) | 	grant, err := auth.GetOAuth2GrantByID(token.GrantID) | ||||||
| 	if err != nil || grant == nil { | 	if err != nil || grant == nil { | ||||||
| 		handleAccessTokenError(ctx, AccessTokenError{ | 		handleAccessTokenError(ctx, AccessTokenError{ | ||||||
| 			ErrorCode:        AccessTokenErrorCodeInvalidGrant, | 			ErrorCode:        AccessTokenErrorCodeInvalidGrant, | ||||||
| @@ -657,7 +665,7 @@ func handleRefreshToken(ctx *context.Context, form forms.AccessTokenForm, server | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func handleAuthorizationCode(ctx *context.Context, form forms.AccessTokenForm, serverKey, clientKey oauth2.JWTSigningKey) { | func handleAuthorizationCode(ctx *context.Context, form forms.AccessTokenForm, serverKey, clientKey oauth2.JWTSigningKey) { | ||||||
| 	app, err := login.GetOAuth2ApplicationByClientID(form.ClientID) | 	app, err := auth.GetOAuth2ApplicationByClientID(form.ClientID) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		handleAccessTokenError(ctx, AccessTokenError{ | 		handleAccessTokenError(ctx, AccessTokenError{ | ||||||
| 			ErrorCode:        AccessTokenErrorCodeInvalidClient, | 			ErrorCode:        AccessTokenErrorCodeInvalidClient, | ||||||
| @@ -679,7 +687,7 @@ func handleAuthorizationCode(ctx *context.Context, form forms.AccessTokenForm, s | |||||||
| 		}) | 		}) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	authorizationCode, err := login.GetOAuth2AuthorizationByCode(form.Code) | 	authorizationCode, err := auth.GetOAuth2AuthorizationByCode(form.Code) | ||||||
| 	if err != nil || authorizationCode == nil { | 	if err != nil || authorizationCode == nil { | ||||||
| 		handleAccessTokenError(ctx, AccessTokenError{ | 		handleAccessTokenError(ctx, AccessTokenError{ | ||||||
| 			ErrorCode:        AccessTokenErrorCodeUnauthorizedClient, | 			ErrorCode:        AccessTokenErrorCodeUnauthorizedClient, | ||||||
| @@ -750,3 +758,367 @@ func handleAuthorizeError(ctx *context.Context, authErr AuthorizeError, redirect | |||||||
| 	redirect.RawQuery = q.Encode() | 	redirect.RawQuery = q.Encode() | ||||||
| 	ctx.Redirect(redirect.String(), 302) | 	ctx.Redirect(redirect.String(), 302) | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | // SignInOAuth handles the OAuth2 login buttons | ||||||
|  | func SignInOAuth(ctx *context.Context) { | ||||||
|  | 	provider := ctx.Params(":provider") | ||||||
|  | 
 | ||||||
|  | 	authSource, err := auth.GetActiveOAuth2SourceByName(provider) | ||||||
|  | 	if err != nil { | ||||||
|  | 		ctx.ServerError("SignIn", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// try to do a direct callback flow, so we don't authenticate the user again but use the valid accesstoken to get the user | ||||||
|  | 	user, gothUser, err := oAuth2UserLoginCallback(authSource, ctx.Req, ctx.Resp) | ||||||
|  | 	if err == nil && user != nil { | ||||||
|  | 		// we got the user without going through the whole OAuth2 authentication flow again | ||||||
|  | 		handleOAuth2SignIn(ctx, authSource, user, gothUser) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if err = authSource.Cfg.(*oauth2.Source).Callout(ctx.Req, ctx.Resp); err != nil { | ||||||
|  | 		if strings.Contains(err.Error(), "no provider for ") { | ||||||
|  | 			if err = oauth2.ResetOAuth2(); err != nil { | ||||||
|  | 				ctx.ServerError("SignIn", err) | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 			if err = authSource.Cfg.(*oauth2.Source).Callout(ctx.Req, ctx.Resp); err != nil { | ||||||
|  | 				ctx.ServerError("SignIn", err) | ||||||
|  | 			} | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		ctx.ServerError("SignIn", err) | ||||||
|  | 	} | ||||||
|  | 	// redirect is done in oauth2.Auth | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // SignInOAuthCallback handles the callback from the given provider | ||||||
|  | func SignInOAuthCallback(ctx *context.Context) { | ||||||
|  | 	provider := ctx.Params(":provider") | ||||||
|  | 
 | ||||||
|  | 	// first look if the provider is still active | ||||||
|  | 	authSource, err := auth.GetActiveOAuth2SourceByName(provider) | ||||||
|  | 	if err != nil { | ||||||
|  | 		ctx.ServerError("SignIn", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if authSource == nil { | ||||||
|  | 		ctx.ServerError("SignIn", errors.New("No valid provider found, check configured callback url in provider")) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	u, gothUser, err := oAuth2UserLoginCallback(authSource, 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 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if u == nil { | ||||||
|  | 		if !setting.Service.AllowOnlyInternalRegistration && setting.OAuth2Client.EnableAutoRegistration { | ||||||
|  | 			// create new user with details from oauth2 provider | ||||||
|  | 			var missingFields []string | ||||||
|  | 			if gothUser.UserID == "" { | ||||||
|  | 				missingFields = append(missingFields, "sub") | ||||||
|  | 			} | ||||||
|  | 			if gothUser.Email == "" { | ||||||
|  | 				missingFields = append(missingFields, "email") | ||||||
|  | 			} | ||||||
|  | 			if setting.OAuth2Client.Username == setting.OAuth2UsernameNickname && gothUser.NickName == "" { | ||||||
|  | 				missingFields = append(missingFields, "nickname") | ||||||
|  | 			} | ||||||
|  | 			if len(missingFields) > 0 { | ||||||
|  | 				log.Error("OAuth2 Provider %s returned empty or missing fields: %s", authSource.Name, missingFields) | ||||||
|  | 				if authSource.IsOAuth2() && authSource.Cfg.(*oauth2.Source).Provider == "openidConnect" { | ||||||
|  | 					log.Error("You may need to change the 'OPENID_CONNECT_SCOPES' setting to request all required fields") | ||||||
|  | 				} | ||||||
|  | 				err = fmt.Errorf("OAuth2 Provider %s returned empty or missing fields: %s", authSource.Name, missingFields) | ||||||
|  | 				ctx.ServerError("CreateUser", err) | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 			u = &user_model.User{ | ||||||
|  | 				Name:         getUserName(&gothUser), | ||||||
|  | 				FullName:     gothUser.Name, | ||||||
|  | 				Email:        gothUser.Email, | ||||||
|  | 				IsActive:     !setting.OAuth2Client.RegisterEmailConfirm, | ||||||
|  | 				LoginType:    auth.OAuth2, | ||||||
|  | 				LoginSource:  authSource.ID, | ||||||
|  | 				LoginName:    gothUser.UserID, | ||||||
|  | 				IsRestricted: setting.Service.DefaultUserIsRestricted, | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			setUserGroupClaims(authSource, u, &gothUser) | ||||||
|  | 
 | ||||||
|  | 			if !createAndHandleCreatedUser(ctx, base.TplName(""), nil, u, &gothUser, setting.OAuth2Client.AccountLinking != setting.OAuth2AccountLinkingDisabled) { | ||||||
|  | 				// error already handled | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 		} else { | ||||||
|  | 			// no existing user is found, request attach or new account | ||||||
|  | 			showLinkingLogin(ctx, gothUser) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	handleOAuth2SignIn(ctx, authSource, 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 *auth.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 showLinkingLogin(ctx *context.Context, gothUser goth.User) { | ||||||
|  | 	if _, err := session.RegenerateSession(ctx.Resp, ctx.Req); err != nil { | ||||||
|  | 		ctx.ServerError("RegenerateSession", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if err := ctx.Session.Set("linkAccountGothUser", gothUser); err != nil { | ||||||
|  | 		log.Error("Error setting linkAccountGothUser in session: %v", err) | ||||||
|  | 	} | ||||||
|  | 	if err := ctx.Session.Release(); err != nil { | ||||||
|  | 		log.Error("Error storing session: %v", err) | ||||||
|  | 	} | ||||||
|  | 	ctx.Redirect(setting.AppSubURL + "/user/link_account") | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func updateAvatarIfNeed(url string, u *user_model.User) { | ||||||
|  | 	if setting.OAuth2Client.UpdateAvatar && len(url) > 0 { | ||||||
|  | 		resp, err := http.Get(url) | ||||||
|  | 		if err == nil { | ||||||
|  | 			defer func() { | ||||||
|  | 				_ = resp.Body.Close() | ||||||
|  | 			}() | ||||||
|  | 		} | ||||||
|  | 		// ignore any error | ||||||
|  | 		if err == nil && resp.StatusCode == http.StatusOK { | ||||||
|  | 			data, err := io.ReadAll(io.LimitReader(resp.Body, setting.Avatar.MaxFileSize+1)) | ||||||
|  | 			if err == nil && int64(len(data)) <= setting.Avatar.MaxFileSize { | ||||||
|  | 				_ = user_service.UploadAvatar(u, data) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func handleOAuth2SignIn(ctx *context.Context, source *auth.Source, u *user_model.User, gothUser goth.User) { | ||||||
|  | 	updateAvatarIfNeed(gothUser.AvatarURL, u) | ||||||
|  | 
 | ||||||
|  | 	needs2FA := false | ||||||
|  | 	if !source.Cfg.(*oauth2.Source).SkipLocalTwoFA { | ||||||
|  | 		_, err := auth.GetTwoFactorByUID(u.ID) | ||||||
|  | 		if err != nil && !auth.IsErrTwoFactorNotEnrolled(err) { | ||||||
|  | 			ctx.ServerError("UserSignIn", err) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		needs2FA = err == nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// If this user is enrolled in 2FA and this source doesn't override it, | ||||||
|  | 	// we can't sign the user in just yet. Instead, redirect them to the 2FA authentication page. | ||||||
|  | 	if !needs2FA { | ||||||
|  | 		if _, err := session.RegenerateSession(ctx.Resp, ctx.Req); err != nil { | ||||||
|  | 			ctx.ServerError("RegenerateSession", err) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if err := ctx.Session.Set("uid", u.ID); err != nil { | ||||||
|  | 			log.Error("Error setting uid in session: %v", err) | ||||||
|  | 		} | ||||||
|  | 		if err := ctx.Session.Set("uname", u.Name); err != nil { | ||||||
|  | 			log.Error("Error setting uname in session: %v", err) | ||||||
|  | 		} | ||||||
|  | 		if err := ctx.Session.Release(); err != nil { | ||||||
|  | 			log.Error("Error storing session: %v", err) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// Clear whatever CSRF has right now, force to generate a new one | ||||||
|  | 		middleware.DeleteCSRFCookie(ctx.Resp) | ||||||
|  | 
 | ||||||
|  | 		// Register last login | ||||||
|  | 		u.SetLastLogin() | ||||||
|  | 
 | ||||||
|  | 		// 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 := externalaccount.UpdateExternalUser(u, gothUser); err != nil { | ||||||
|  | 			log.Error("UpdateExternalUser failed: %v", err) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if err := resetLocale(ctx, u); err != nil { | ||||||
|  | 			ctx.ServerError("resetLocale", err) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if redirectTo := ctx.GetCookie("redirect_to"); len(redirectTo) > 0 { | ||||||
|  | 			middleware.DeleteRedirectToCookie(ctx.Resp) | ||||||
|  | 			ctx.RedirectToFirst(redirectTo) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		ctx.Redirect(setting.AppSubURL + "/") | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	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 | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if _, err := session.RegenerateSession(ctx.Resp, ctx.Req); err != nil { | ||||||
|  | 		ctx.ServerError("RegenerateSession", 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) | ||||||
|  | 	} | ||||||
|  | 	if err := ctx.Session.Set("twofaRemember", false); err != nil { | ||||||
|  | 		log.Error("Error setting twofaRemember in session: %v", err) | ||||||
|  | 	} | ||||||
|  | 	if err := ctx.Session.Release(); err != nil { | ||||||
|  | 		log.Error("Error storing session: %v", err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// If U2F is enrolled -> Redirect to U2F instead | ||||||
|  | 	regs, err := auth.GetU2FRegistrationsByUID(u.ID) | ||||||
|  | 	if err == nil && len(regs) > 0 { | ||||||
|  | 		ctx.Redirect(setting.AppSubURL + "/user/u2f") | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	ctx.Redirect(setting.AppSubURL + "/user/two_factor") | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // OAuth2UserLoginCallback attempts to handle the callback from the OAuth2 provider and if successful | ||||||
|  | // login the user | ||||||
|  | func oAuth2UserLoginCallback(authSource *auth.Source, request *http.Request, response http.ResponseWriter) (*user_model.User, goth.User, error) { | ||||||
|  | 	oauth2Source := authSource.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", authSource.Name, setting.OAuth2.MaxTokenLength) | ||||||
|  | 			err = fmt.Errorf("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", authSource.Name, setting.OAuth2.MaxTokenLength) | ||||||
|  | 		} | ||||||
|  | 		return 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:   auth.OAuth2, | ||||||
|  | 		LoginSource: authSource.ID, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	hasUser, err := user_model.GetUser(user) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, goth.User{}, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if hasUser { | ||||||
|  | 		return user, gothUser, nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// search in external linked users | ||||||
|  | 	externalLoginUser := &user_model.ExternalLoginUser{ | ||||||
|  | 		ExternalID:    gothUser.UserID, | ||||||
|  | 		LoginSourceID: authSource.ID, | ||||||
|  | 	} | ||||||
|  | 	hasUser, err = user_model.GetExternalLogin(externalLoginUser) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, goth.User{}, err | ||||||
|  | 	} | ||||||
|  | 	if hasUser { | ||||||
|  | 		user, err = user_model.GetUserByID(externalLoginUser.UserID) | ||||||
|  | 		return user, gothUser, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// no user found to login | ||||||
|  | 	return nil, gothUser, nil | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @@ -2,12 +2,12 @@ | |||||||
| // Use of this source code is governed by a MIT-style | // Use of this source code is governed by a MIT-style | ||||||
| // license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||||
| 
 | 
 | ||||||
| package user | package auth | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"testing" | 	"testing" | ||||||
| 
 | 
 | ||||||
| 	"code.gitea.io/gitea/models/login" | 	"code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	"code.gitea.io/gitea/services/auth/source/oauth2" | 	"code.gitea.io/gitea/services/auth/source/oauth2" | ||||||
| @@ -16,7 +16,7 @@ import ( | |||||||
| 	"github.com/stretchr/testify/assert" | 	"github.com/stretchr/testify/assert" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func createAndParseToken(t *testing.T, grant *login.OAuth2Grant) *oauth2.OIDCToken { | func createAndParseToken(t *testing.T, grant *auth.OAuth2Grant) *oauth2.OIDCToken { | ||||||
| 	signingKey, err := oauth2.CreateJWTSigningKey("HS256", make([]byte, 32)) | 	signingKey, err := oauth2.CreateJWTSigningKey("HS256", make([]byte, 32)) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.NotNil(t, signingKey) | 	assert.NotNil(t, signingKey) | ||||||
| @@ -43,7 +43,7 @@ func createAndParseToken(t *testing.T, grant *login.OAuth2Grant) *oauth2.OIDCTok | |||||||
| func TestNewAccessTokenResponse_OIDCToken(t *testing.T) { | func TestNewAccessTokenResponse_OIDCToken(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 
 | 
 | ||||||
| 	grants, err := login.GetOAuth2GrantsByUserID(3) | 	grants, err := auth.GetOAuth2GrantsByUserID(3) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.Len(t, grants, 1) | 	assert.Len(t, grants, 1) | ||||||
| 
 | 
 | ||||||
| @@ -59,7 +59,7 @@ func TestNewAccessTokenResponse_OIDCToken(t *testing.T) { | |||||||
| 	assert.False(t, oidcToken.EmailVerified) | 	assert.False(t, oidcToken.EmailVerified) | ||||||
| 
 | 
 | ||||||
| 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5}).(*user_model.User) | 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5}).(*user_model.User) | ||||||
| 	grants, err = login.GetOAuth2GrantsByUserID(user.ID) | 	grants, err = auth.GetOAuth2GrantsByUserID(user.ID) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.Len(t, grants, 1) | 	assert.Len(t, grants, 1) | ||||||
| 
 | 
 | ||||||
| @@ -2,7 +2,7 @@ | |||||||
| // Use of this source code is governed by a MIT-style | // Use of this source code is governed by a MIT-style | ||||||
| // license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||||
| 
 | 
 | ||||||
| package user | package auth | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
							
								
								
									
										346
									
								
								routers/web/auth/password.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										346
									
								
								routers/web/auth/password.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,346 @@ | |||||||
|  | // Copyright 2019 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 auth | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"errors" | ||||||
|  | 	"net/http" | ||||||
|  |  | ||||||
|  | 	"code.gitea.io/gitea/models/auth" | ||||||
|  | 	"code.gitea.io/gitea/models/db" | ||||||
|  | 	user_model "code.gitea.io/gitea/models/user" | ||||||
|  | 	"code.gitea.io/gitea/modules/base" | ||||||
|  | 	"code.gitea.io/gitea/modules/context" | ||||||
|  | 	"code.gitea.io/gitea/modules/log" | ||||||
|  | 	"code.gitea.io/gitea/modules/password" | ||||||
|  | 	"code.gitea.io/gitea/modules/setting" | ||||||
|  | 	"code.gitea.io/gitea/modules/timeutil" | ||||||
|  | 	"code.gitea.io/gitea/modules/web" | ||||||
|  | 	"code.gitea.io/gitea/modules/web/middleware" | ||||||
|  | 	"code.gitea.io/gitea/routers/utils" | ||||||
|  | 	"code.gitea.io/gitea/services/forms" | ||||||
|  | 	"code.gitea.io/gitea/services/mailer" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | var ( | ||||||
|  | 	// tplMustChangePassword template for updating a user's password | ||||||
|  | 	tplMustChangePassword base.TplName = "user/auth/change_passwd" | ||||||
|  | 	tplForgotPassword     base.TplName = "user/auth/forgot_passwd" | ||||||
|  | 	tplResetPassword      base.TplName = "user/auth/reset_passwd" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // ForgotPasswd render the forget password page | ||||||
|  | func ForgotPasswd(ctx *context.Context) { | ||||||
|  | 	ctx.Data["Title"] = ctx.Tr("auth.forgot_password_title") | ||||||
|  |  | ||||||
|  | 	if setting.MailService == nil { | ||||||
|  | 		log.Warn(ctx.Tr("auth.disable_forgot_password_mail_admin")) | ||||||
|  | 		ctx.Data["IsResetDisable"] = true | ||||||
|  | 		ctx.HTML(http.StatusOK, tplForgotPassword) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ctx.Data["Email"] = ctx.FormString("email") | ||||||
|  |  | ||||||
|  | 	ctx.Data["IsResetRequest"] = true | ||||||
|  | 	ctx.HTML(http.StatusOK, tplForgotPassword) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ForgotPasswdPost response for forget password request | ||||||
|  | func ForgotPasswdPost(ctx *context.Context) { | ||||||
|  | 	ctx.Data["Title"] = ctx.Tr("auth.forgot_password_title") | ||||||
|  |  | ||||||
|  | 	if setting.MailService == nil { | ||||||
|  | 		ctx.NotFound("ForgotPasswdPost", nil) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	ctx.Data["IsResetRequest"] = true | ||||||
|  |  | ||||||
|  | 	email := ctx.FormString("email") | ||||||
|  | 	ctx.Data["Email"] = email | ||||||
|  |  | ||||||
|  | 	u, err := user_model.GetUserByEmail(email) | ||||||
|  | 	if err != nil { | ||||||
|  | 		if user_model.IsErrUserNotExist(err) { | ||||||
|  | 			ctx.Data["ResetPwdCodeLives"] = timeutil.MinutesToFriendly(setting.Service.ResetPwdCodeLives, ctx.Locale.Language()) | ||||||
|  | 			ctx.Data["IsResetSent"] = true | ||||||
|  | 			ctx.HTML(http.StatusOK, tplForgotPassword) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		ctx.ServerError("user.ResetPasswd(check existence)", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if !u.IsLocal() && !u.IsOAuth2() { | ||||||
|  | 		ctx.Data["Err_Email"] = true | ||||||
|  | 		ctx.RenderWithErr(ctx.Tr("auth.non_local_account"), tplForgotPassword, nil) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if ctx.Cache.IsExist("MailResendLimit_" + u.LowerName) { | ||||||
|  | 		ctx.Data["ResendLimited"] = true | ||||||
|  | 		ctx.HTML(http.StatusOK, tplForgotPassword) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	mailer.SendResetPasswordMail(u) | ||||||
|  |  | ||||||
|  | 	if err = ctx.Cache.Put("MailResendLimit_"+u.LowerName, u.LowerName, 180); err != nil { | ||||||
|  | 		log.Error("Set cache(MailResendLimit) fail: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ctx.Data["ResetPwdCodeLives"] = timeutil.MinutesToFriendly(setting.Service.ResetPwdCodeLives, ctx.Locale.Language()) | ||||||
|  | 	ctx.Data["IsResetSent"] = true | ||||||
|  | 	ctx.HTML(http.StatusOK, tplForgotPassword) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func commonResetPassword(ctx *context.Context) (*user_model.User, *auth.TwoFactor) { | ||||||
|  | 	code := ctx.FormString("code") | ||||||
|  |  | ||||||
|  | 	ctx.Data["Title"] = ctx.Tr("auth.reset_password") | ||||||
|  | 	ctx.Data["Code"] = code | ||||||
|  |  | ||||||
|  | 	if nil != ctx.User { | ||||||
|  | 		ctx.Data["user_signed_in"] = true | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if len(code) == 0 { | ||||||
|  | 		ctx.Flash.Error(ctx.Tr("auth.invalid_code")) | ||||||
|  | 		return nil, nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Fail early, don't frustrate the user | ||||||
|  | 	u := user_model.VerifyUserActiveCode(code) | ||||||
|  | 	if u == nil { | ||||||
|  | 		ctx.Flash.Error(ctx.Tr("auth.invalid_code")) | ||||||
|  | 		return nil, nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	twofa, err := auth.GetTwoFactorByUID(u.ID) | ||||||
|  | 	if err != nil { | ||||||
|  | 		if !auth.IsErrTwoFactorNotEnrolled(err) { | ||||||
|  | 			ctx.Error(http.StatusInternalServerError, "CommonResetPassword", err.Error()) | ||||||
|  | 			return nil, nil | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		ctx.Data["has_two_factor"] = true | ||||||
|  | 		ctx.Data["scratch_code"] = ctx.FormBool("scratch_code") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Show the user that they are affecting the account that they intended to | ||||||
|  | 	ctx.Data["user_email"] = u.Email | ||||||
|  |  | ||||||
|  | 	if nil != ctx.User && u.ID != ctx.User.ID { | ||||||
|  | 		ctx.Flash.Error(ctx.Tr("auth.reset_password_wrong_user", ctx.User.Email, u.Email)) | ||||||
|  | 		return nil, nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return u, twofa | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ResetPasswd render the account recovery page | ||||||
|  | func ResetPasswd(ctx *context.Context) { | ||||||
|  | 	ctx.Data["IsResetForm"] = true | ||||||
|  |  | ||||||
|  | 	commonResetPassword(ctx) | ||||||
|  | 	if ctx.Written() { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ctx.HTML(http.StatusOK, tplResetPassword) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ResetPasswdPost response from account recovery request | ||||||
|  | func ResetPasswdPost(ctx *context.Context) { | ||||||
|  | 	u, twofa := commonResetPassword(ctx) | ||||||
|  | 	if ctx.Written() { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if u == nil { | ||||||
|  | 		// Flash error has been set | ||||||
|  | 		ctx.HTML(http.StatusOK, tplResetPassword) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Validate password length. | ||||||
|  | 	passwd := ctx.FormString("password") | ||||||
|  | 	if len(passwd) < setting.MinPasswordLength { | ||||||
|  | 		ctx.Data["IsResetForm"] = true | ||||||
|  | 		ctx.Data["Err_Password"] = true | ||||||
|  | 		ctx.RenderWithErr(ctx.Tr("auth.password_too_short", setting.MinPasswordLength), tplResetPassword, nil) | ||||||
|  | 		return | ||||||
|  | 	} else if !password.IsComplexEnough(passwd) { | ||||||
|  | 		ctx.Data["IsResetForm"] = true | ||||||
|  | 		ctx.Data["Err_Password"] = true | ||||||
|  | 		ctx.RenderWithErr(password.BuildComplexityError(ctx), tplResetPassword, nil) | ||||||
|  | 		return | ||||||
|  | 	} else if pwned, err := password.IsPwned(ctx, passwd); pwned || err != nil { | ||||||
|  | 		errMsg := ctx.Tr("auth.password_pwned") | ||||||
|  | 		if err != nil { | ||||||
|  | 			log.Error(err.Error()) | ||||||
|  | 			errMsg = ctx.Tr("auth.password_pwned_err") | ||||||
|  | 		} | ||||||
|  | 		ctx.Data["IsResetForm"] = true | ||||||
|  | 		ctx.Data["Err_Password"] = true | ||||||
|  | 		ctx.RenderWithErr(errMsg, tplResetPassword, nil) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Handle two-factor | ||||||
|  | 	regenerateScratchToken := false | ||||||
|  | 	if twofa != nil { | ||||||
|  | 		if ctx.FormBool("scratch_code") { | ||||||
|  | 			if !twofa.VerifyScratchToken(ctx.FormString("token")) { | ||||||
|  | 				ctx.Data["IsResetForm"] = true | ||||||
|  | 				ctx.Data["Err_Token"] = true | ||||||
|  | 				ctx.RenderWithErr(ctx.Tr("auth.twofa_scratch_token_incorrect"), tplResetPassword, nil) | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 			regenerateScratchToken = true | ||||||
|  | 		} else { | ||||||
|  | 			passcode := ctx.FormString("passcode") | ||||||
|  | 			ok, err := twofa.ValidateTOTP(passcode) | ||||||
|  | 			if err != nil { | ||||||
|  | 				ctx.Error(http.StatusInternalServerError, "ValidateTOTP", err.Error()) | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 			if !ok || twofa.LastUsedPasscode == passcode { | ||||||
|  | 				ctx.Data["IsResetForm"] = true | ||||||
|  | 				ctx.Data["Err_Passcode"] = true | ||||||
|  | 				ctx.RenderWithErr(ctx.Tr("auth.twofa_passcode_incorrect"), tplResetPassword, nil) | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			twofa.LastUsedPasscode = passcode | ||||||
|  | 			if err = auth.UpdateTwoFactor(twofa); err != nil { | ||||||
|  | 				ctx.ServerError("ResetPasswdPost: UpdateTwoFactor", err) | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	var err error | ||||||
|  | 	if u.Rands, err = user_model.GetUserSalt(); err != nil { | ||||||
|  | 		ctx.ServerError("UpdateUser", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	if err = u.SetPassword(passwd); err != nil { | ||||||
|  | 		ctx.ServerError("UpdateUser", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	u.MustChangePassword = false | ||||||
|  | 	if err := user_model.UpdateUserCols(db.DefaultContext, u, "must_change_password", "passwd", "passwd_hash_algo", "rands", "salt"); err != nil { | ||||||
|  | 		ctx.ServerError("UpdateUser", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	log.Trace("User password reset: %s", u.Name) | ||||||
|  | 	ctx.Data["IsResetFailed"] = true | ||||||
|  | 	remember := len(ctx.FormString("remember")) != 0 | ||||||
|  |  | ||||||
|  | 	if regenerateScratchToken { | ||||||
|  | 		// Invalidate the scratch token. | ||||||
|  | 		_, err = twofa.GenerateScratchToken() | ||||||
|  | 		if err != nil { | ||||||
|  | 			ctx.ServerError("UserSignIn", err) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		if err = auth.UpdateTwoFactor(twofa); err != nil { | ||||||
|  | 			ctx.ServerError("UserSignIn", err) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		handleSignInFull(ctx, u, remember, false) | ||||||
|  | 		if ctx.Written() { | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		ctx.Flash.Info(ctx.Tr("auth.twofa_scratch_used")) | ||||||
|  | 		ctx.Redirect(setting.AppSubURL + "/user/settings/security") | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	handleSignIn(ctx, u, remember) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // MustChangePassword renders the page to change a user's password | ||||||
|  | func MustChangePassword(ctx *context.Context) { | ||||||
|  | 	ctx.Data["Title"] = ctx.Tr("auth.must_change_password") | ||||||
|  | 	ctx.Data["ChangePasscodeLink"] = setting.AppSubURL + "/user/settings/change_password" | ||||||
|  | 	ctx.Data["MustChangePassword"] = true | ||||||
|  | 	ctx.HTML(http.StatusOK, tplMustChangePassword) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // MustChangePasswordPost response for updating a user's password after his/her | ||||||
|  | // account was created by an admin | ||||||
|  | func MustChangePasswordPost(ctx *context.Context) { | ||||||
|  | 	form := web.GetForm(ctx).(*forms.MustChangePasswordForm) | ||||||
|  | 	ctx.Data["Title"] = ctx.Tr("auth.must_change_password") | ||||||
|  | 	ctx.Data["ChangePasscodeLink"] = setting.AppSubURL + "/user/settings/change_password" | ||||||
|  | 	if ctx.HasError() { | ||||||
|  | 		ctx.HTML(http.StatusOK, tplMustChangePassword) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	u := ctx.User | ||||||
|  | 	// Make sure only requests for users who are eligible to change their password via | ||||||
|  | 	// this method passes through | ||||||
|  | 	if !u.MustChangePassword { | ||||||
|  | 		ctx.ServerError("MustUpdatePassword", errors.New("cannot update password.. Please visit the settings page")) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if form.Password != form.Retype { | ||||||
|  | 		ctx.Data["Err_Password"] = true | ||||||
|  | 		ctx.RenderWithErr(ctx.Tr("form.password_not_match"), tplMustChangePassword, &form) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if len(form.Password) < setting.MinPasswordLength { | ||||||
|  | 		ctx.Data["Err_Password"] = true | ||||||
|  | 		ctx.RenderWithErr(ctx.Tr("auth.password_too_short", setting.MinPasswordLength), tplMustChangePassword, &form) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if !password.IsComplexEnough(form.Password) { | ||||||
|  | 		ctx.Data["Err_Password"] = true | ||||||
|  | 		ctx.RenderWithErr(password.BuildComplexityError(ctx), tplMustChangePassword, &form) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	pwned, err := password.IsPwned(ctx, form.Password) | ||||||
|  | 	if pwned { | ||||||
|  | 		ctx.Data["Err_Password"] = true | ||||||
|  | 		errMsg := ctx.Tr("auth.password_pwned") | ||||||
|  | 		if err != nil { | ||||||
|  | 			log.Error(err.Error()) | ||||||
|  | 			errMsg = ctx.Tr("auth.password_pwned_err") | ||||||
|  | 		} | ||||||
|  | 		ctx.RenderWithErr(errMsg, tplMustChangePassword, &form) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err = u.SetPassword(form.Password); err != nil { | ||||||
|  | 		ctx.ServerError("UpdateUser", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	u.MustChangePassword = false | ||||||
|  |  | ||||||
|  | 	if err := user_model.UpdateUserCols(db.DefaultContext, u, "must_change_password", "passwd", "passwd_hash_algo", "salt"); err != nil { | ||||||
|  | 		ctx.ServerError("UpdateUser", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ctx.Flash.Success(ctx.Tr("settings.change_password_success")) | ||||||
|  |  | ||||||
|  | 	log.Trace("User updated password: %s", u.Name) | ||||||
|  |  | ||||||
|  | 	if redirectTo := ctx.GetCookie("redirect_to"); len(redirectTo) > 0 && !utils.IsExternalURL(redirectTo) { | ||||||
|  | 		middleware.DeleteRedirectToCookie(ctx.Resp) | ||||||
|  | 		ctx.RedirectToFirst(redirectTo) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ctx.Redirect(setting.AppSubURL + "/") | ||||||
|  | } | ||||||
							
								
								
									
										136
									
								
								routers/web/auth/u2f.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								routers/web/auth/u2f.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,136 @@ | |||||||
|  | // Copyright 2017 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 auth | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"errors" | ||||||
|  | 	"net/http" | ||||||
|  |  | ||||||
|  | 	"code.gitea.io/gitea/models/auth" | ||||||
|  | 	user_model "code.gitea.io/gitea/models/user" | ||||||
|  | 	"code.gitea.io/gitea/modules/base" | ||||||
|  | 	"code.gitea.io/gitea/modules/context" | ||||||
|  | 	"code.gitea.io/gitea/modules/log" | ||||||
|  | 	"code.gitea.io/gitea/modules/setting" | ||||||
|  | 	"code.gitea.io/gitea/modules/web" | ||||||
|  | 	"code.gitea.io/gitea/services/externalaccount" | ||||||
|  |  | ||||||
|  | 	"github.com/tstranex/u2f" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | var tplU2F base.TplName = "user/auth/u2f" | ||||||
|  |  | ||||||
|  | // U2F shows the U2F login page | ||||||
|  | func U2F(ctx *context.Context) { | ||||||
|  | 	ctx.Data["Title"] = ctx.Tr("twofa") | ||||||
|  | 	ctx.Data["RequireU2F"] = true | ||||||
|  | 	// Check auto-login. | ||||||
|  | 	if checkAutoLogin(ctx) { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Ensure user is in a 2FA session. | ||||||
|  | 	if ctx.Session.Get("twofaUid") == nil { | ||||||
|  | 		ctx.ServerError("UserSignIn", errors.New("not in U2F session")) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// See whether TOTP is also available. | ||||||
|  | 	if ctx.Session.Get("totpEnrolled") != nil { | ||||||
|  | 		ctx.Data["TOTPEnrolled"] = true | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ctx.HTML(http.StatusOK, tplU2F) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // U2FChallenge submits a sign challenge to the browser | ||||||
|  | func U2FChallenge(ctx *context.Context) { | ||||||
|  | 	// Ensure user is in a U2F session. | ||||||
|  | 	idSess := ctx.Session.Get("twofaUid") | ||||||
|  | 	if idSess == nil { | ||||||
|  | 		ctx.ServerError("UserSignIn", errors.New("not in U2F session")) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	id := idSess.(int64) | ||||||
|  | 	regs, err := auth.GetU2FRegistrationsByUID(id) | ||||||
|  | 	if err != nil { | ||||||
|  | 		ctx.ServerError("UserSignIn", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	if len(regs) == 0 { | ||||||
|  | 		ctx.ServerError("UserSignIn", errors.New("no device registered")) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	challenge, err := u2f.NewChallenge(setting.U2F.AppID, setting.U2F.TrustedFacets) | ||||||
|  | 	if err != nil { | ||||||
|  | 		ctx.ServerError("u2f.NewChallenge", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	if err := ctx.Session.Set("u2fChallenge", challenge); err != nil { | ||||||
|  | 		ctx.ServerError("UserSignIn: unable to set u2fChallenge in session", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	if err := ctx.Session.Release(); err != nil { | ||||||
|  | 		ctx.ServerError("UserSignIn: unable to store session", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ctx.JSON(http.StatusOK, challenge.SignRequest(regs.ToRegistrations())) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // U2FSign authenticates the user by signResp | ||||||
|  | func U2FSign(ctx *context.Context) { | ||||||
|  | 	signResp := web.GetForm(ctx).(*u2f.SignResponse) | ||||||
|  | 	challSess := ctx.Session.Get("u2fChallenge") | ||||||
|  | 	idSess := ctx.Session.Get("twofaUid") | ||||||
|  | 	if challSess == nil || idSess == nil { | ||||||
|  | 		ctx.ServerError("UserSignIn", errors.New("not in U2F session")) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	challenge := challSess.(*u2f.Challenge) | ||||||
|  | 	id := idSess.(int64) | ||||||
|  | 	regs, err := auth.GetU2FRegistrationsByUID(id) | ||||||
|  | 	if err != nil { | ||||||
|  | 		ctx.ServerError("UserSignIn", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	for _, reg := range regs { | ||||||
|  | 		r, err := reg.Parse() | ||||||
|  | 		if err != nil { | ||||||
|  | 			log.Error("parsing u2f registration: %v", err) | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		newCounter, authErr := r.Authenticate(*signResp, *challenge, reg.Counter) | ||||||
|  | 		if authErr == nil { | ||||||
|  | 			reg.Counter = newCounter | ||||||
|  | 			user, err := user_model.GetUserByID(id) | ||||||
|  | 			if err != nil { | ||||||
|  | 				ctx.ServerError("UserSignIn", err) | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 			remember := ctx.Session.Get("twofaRemember").(bool) | ||||||
|  | 			if err := reg.UpdateCounter(); err != nil { | ||||||
|  | 				ctx.ServerError("UserSignIn", err) | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			if ctx.Session.Get("linkAccount") != nil { | ||||||
|  | 				if err := externalaccount.LinkAccountFromStore(ctx.Session, user); err != nil { | ||||||
|  | 					ctx.ServerError("UserSignIn", err) | ||||||
|  | 					return | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			redirect := handleSignInFull(ctx, user, remember, false) | ||||||
|  | 			if ctx.Written() { | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 			if redirect == "" { | ||||||
|  | 				redirect = setting.AppSubURL + "/" | ||||||
|  | 			} | ||||||
|  | 			ctx.PlainText(http.StatusOK, redirect) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	ctx.Error(http.StatusUnauthorized) | ||||||
|  | } | ||||||
| @@ -17,7 +17,7 @@ import ( | |||||||
| 	"code.gitea.io/gitea/modules/json" | 	"code.gitea.io/gitea/modules/json" | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
| 	"code.gitea.io/gitea/routers/web/user" | 	"code.gitea.io/gitea/routers/web/auth" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // Events listens for events | // Events listens for events | ||||||
| @@ -133,7 +133,7 @@ loop: | |||||||
| 					}).WriteTo(ctx.Resp) | 					}).WriteTo(ctx.Resp) | ||||||
| 					ctx.Resp.Flush() | 					ctx.Resp.Flush() | ||||||
| 					go unregister() | 					go unregister() | ||||||
| 					user.HandleSignOut(ctx) | 					auth.HandleSignOut(ctx) | ||||||
| 					break loop | 					break loop | ||||||
| 				} | 				} | ||||||
| 				// Replace the event - we don't want to expose the session ID to the user | 				// Replace the event - we don't want to expose the session ID to the user | ||||||
|   | |||||||
| @@ -13,6 +13,7 @@ import ( | |||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
| 	"code.gitea.io/gitea/modules/web/middleware" | 	"code.gitea.io/gitea/modules/web/middleware" | ||||||
|  | 	"code.gitea.io/gitea/routers/web/auth" | ||||||
| 	"code.gitea.io/gitea/routers/web/user" | 	"code.gitea.io/gitea/routers/web/user" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -26,7 +27,7 @@ func Home(ctx *context.Context) { | |||||||
| 	if ctx.IsSigned { | 	if ctx.IsSigned { | ||||||
| 		if !ctx.User.IsActive && setting.Service.RegisterEmailConfirm { | 		if !ctx.User.IsActive && setting.Service.RegisterEmailConfirm { | ||||||
| 			ctx.Data["Title"] = ctx.Tr("auth.active_your_account") | 			ctx.Data["Title"] = ctx.Tr("auth.active_your_account") | ||||||
| 			ctx.HTML(http.StatusOK, user.TplActivate) | 			ctx.HTML(http.StatusOK, auth.TplActivate) | ||||||
| 		} else if !ctx.User.IsActive || ctx.User.ProhibitLogin { | 		} else if !ctx.User.IsActive || ctx.User.ProhibitLogin { | ||||||
| 			log.Info("Failed authentication attempt for %s from %s", ctx.User.Name, ctx.RemoteAddr()) | 			log.Info("Failed authentication attempt for %s from %s", ctx.User.Name, ctx.RemoteAddr()) | ||||||
| 			ctx.Data["Title"] = ctx.Tr("auth.prohibit_login") | 			ctx.Data["Title"] = ctx.Tr("auth.prohibit_login") | ||||||
|   | |||||||
| @@ -20,8 +20,8 @@ import ( | |||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" | 	"code.gitea.io/gitea/models" | ||||||
|  | 	"code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	"code.gitea.io/gitea/models/login" |  | ||||||
| 	"code.gitea.io/gitea/models/perm" | 	"code.gitea.io/gitea/models/perm" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	"code.gitea.io/gitea/models/unit" | 	"code.gitea.io/gitea/models/unit" | ||||||
| @@ -179,12 +179,12 @@ func httpBase(ctx *context.Context) (h *serviceHandler) { | |||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if ctx.IsBasicAuth && ctx.Data["IsApiToken"] != true { | 		if ctx.IsBasicAuth && ctx.Data["IsApiToken"] != true { | ||||||
| 			_, err = login.GetTwoFactorByUID(ctx.User.ID) | 			_, err = auth.GetTwoFactorByUID(ctx.User.ID) | ||||||
| 			if err == nil { | 			if err == nil { | ||||||
| 				// TODO: This response should be changed to "invalid credentials" for security reasons once the expectation behind it (creating an app token to authenticate) is properly documented | 				// TODO: This response should be changed to "invalid credentials" for security reasons once the expectation behind it (creating an app token to authenticate) is properly documented | ||||||
| 				ctx.PlainText(http.StatusUnauthorized, "Users with two-factor authentication enabled cannot perform HTTP/HTTPS operations via plain username and password. Please create and use a personal access token on the user settings page") | 				ctx.PlainText(http.StatusUnauthorized, "Users with two-factor authentication enabled cannot perform HTTP/HTTPS operations via plain username and password. Please create and use a personal access token on the user settings page") | ||||||
| 				return | 				return | ||||||
| 			} else if !login.IsErrTwoFactorNotEnrolled(err) { | 			} else if !auth.IsErrTwoFactorNotEnrolled(err) { | ||||||
| 				ctx.ServerError("IsErrTwoFactorNotEnrolled", err) | 				ctx.ServerError("IsErrTwoFactorNotEnrolled", err) | ||||||
| 				return | 				return | ||||||
| 			} | 			} | ||||||
|   | |||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -9,7 +9,7 @@ import ( | |||||||
| 	"net/http" | 	"net/http" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" | 	"code.gitea.io/gitea/models" | ||||||
| 	"code.gitea.io/gitea/models/login" | 	"code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/modules/base" | 	"code.gitea.io/gitea/modules/base" | ||||||
| 	"code.gitea.io/gitea/modules/context" | 	"code.gitea.io/gitea/modules/context" | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
| @@ -93,12 +93,12 @@ func loadApplicationsData(ctx *context.Context) { | |||||||
| 	ctx.Data["Tokens"] = tokens | 	ctx.Data["Tokens"] = tokens | ||||||
| 	ctx.Data["EnableOAuth2"] = setting.OAuth2.Enable | 	ctx.Data["EnableOAuth2"] = setting.OAuth2.Enable | ||||||
| 	if setting.OAuth2.Enable { | 	if setting.OAuth2.Enable { | ||||||
| 		ctx.Data["Applications"], err = login.GetOAuth2ApplicationsByUserID(ctx.User.ID) | 		ctx.Data["Applications"], err = auth.GetOAuth2ApplicationsByUserID(ctx.User.ID) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			ctx.ServerError("GetOAuth2ApplicationsByUserID", err) | 			ctx.ServerError("GetOAuth2ApplicationsByUserID", err) | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| 		ctx.Data["Grants"], err = login.GetOAuth2GrantsByUserID(ctx.User.ID) | 		ctx.Data["Grants"], err = auth.GetOAuth2GrantsByUserID(ctx.User.ID) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			ctx.ServerError("GetOAuth2GrantsByUserID", err) | 			ctx.ServerError("GetOAuth2GrantsByUserID", err) | ||||||
| 			return | 			return | ||||||
|   | |||||||
| @@ -216,7 +216,6 @@ func KeysPost(ctx *context.Context) { | |||||||
|  |  | ||||||
| // DeleteKey response for delete user's SSH/GPG key | // DeleteKey response for delete user's SSH/GPG key | ||||||
| func DeleteKey(ctx *context.Context) { | func DeleteKey(ctx *context.Context) { | ||||||
|  |  | ||||||
| 	switch ctx.FormString("type") { | 	switch ctx.FormString("type") { | ||||||
| 	case "gpg": | 	case "gpg": | ||||||
| 		if err := asymkey_model.DeleteGPGKey(ctx.User, ctx.FormInt64("id")); err != nil { | 		if err := asymkey_model.DeleteGPGKey(ctx.User, ctx.FormInt64("id")); err != nil { | ||||||
|   | |||||||
| @@ -8,7 +8,7 @@ import ( | |||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"net/http" | 	"net/http" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models/login" | 	"code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/modules/base" | 	"code.gitea.io/gitea/modules/base" | ||||||
| 	"code.gitea.io/gitea/modules/context" | 	"code.gitea.io/gitea/modules/context" | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| @@ -34,7 +34,7 @@ func OAuthApplicationsPost(ctx *context.Context) { | |||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	// TODO validate redirect URI | 	// TODO validate redirect URI | ||||||
| 	app, err := login.CreateOAuth2Application(login.CreateOAuth2ApplicationOptions{ | 	app, err := auth.CreateOAuth2Application(auth.CreateOAuth2ApplicationOptions{ | ||||||
| 		Name:         form.Name, | 		Name:         form.Name, | ||||||
| 		RedirectURIs: []string{form.RedirectURI}, | 		RedirectURIs: []string{form.RedirectURI}, | ||||||
| 		UserID:       ctx.User.ID, | 		UserID:       ctx.User.ID, | ||||||
| @@ -67,7 +67,7 @@ func OAuthApplicationsEdit(ctx *context.Context) { | |||||||
| 	} | 	} | ||||||
| 	// TODO validate redirect URI | 	// TODO validate redirect URI | ||||||
| 	var err error | 	var err error | ||||||
| 	if ctx.Data["App"], err = login.UpdateOAuth2Application(login.UpdateOAuth2ApplicationOptions{ | 	if ctx.Data["App"], err = auth.UpdateOAuth2Application(auth.UpdateOAuth2ApplicationOptions{ | ||||||
| 		ID:           ctx.ParamsInt64("id"), | 		ID:           ctx.ParamsInt64("id"), | ||||||
| 		Name:         form.Name, | 		Name:         form.Name, | ||||||
| 		RedirectURIs: []string{form.RedirectURI}, | 		RedirectURIs: []string{form.RedirectURI}, | ||||||
| @@ -85,9 +85,9 @@ func OAuthApplicationsRegenerateSecret(ctx *context.Context) { | |||||||
| 	ctx.Data["Title"] = ctx.Tr("settings") | 	ctx.Data["Title"] = ctx.Tr("settings") | ||||||
| 	ctx.Data["PageIsSettingsApplications"] = true | 	ctx.Data["PageIsSettingsApplications"] = true | ||||||
|  |  | ||||||
| 	app, err := login.GetOAuth2ApplicationByID(ctx.ParamsInt64("id")) | 	app, err := auth.GetOAuth2ApplicationByID(ctx.ParamsInt64("id")) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		if login.IsErrOAuthApplicationNotFound(err) { | 		if auth.IsErrOAuthApplicationNotFound(err) { | ||||||
| 			ctx.NotFound("Application not found", err) | 			ctx.NotFound("Application not found", err) | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| @@ -110,9 +110,9 @@ func OAuthApplicationsRegenerateSecret(ctx *context.Context) { | |||||||
|  |  | ||||||
| // OAuth2ApplicationShow displays the given application | // OAuth2ApplicationShow displays the given application | ||||||
| func OAuth2ApplicationShow(ctx *context.Context) { | func OAuth2ApplicationShow(ctx *context.Context) { | ||||||
| 	app, err := login.GetOAuth2ApplicationByID(ctx.ParamsInt64("id")) | 	app, err := auth.GetOAuth2ApplicationByID(ctx.ParamsInt64("id")) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		if login.IsErrOAuthApplicationNotFound(err) { | 		if auth.IsErrOAuthApplicationNotFound(err) { | ||||||
| 			ctx.NotFound("Application not found", err) | 			ctx.NotFound("Application not found", err) | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| @@ -129,7 +129,7 @@ func OAuth2ApplicationShow(ctx *context.Context) { | |||||||
|  |  | ||||||
| // DeleteOAuth2Application deletes the given oauth2 application | // DeleteOAuth2Application deletes the given oauth2 application | ||||||
| func DeleteOAuth2Application(ctx *context.Context) { | func DeleteOAuth2Application(ctx *context.Context) { | ||||||
| 	if err := login.DeleteOAuth2Application(ctx.FormInt64("id"), ctx.User.ID); err != nil { | 	if err := auth.DeleteOAuth2Application(ctx.FormInt64("id"), ctx.User.ID); err != nil { | ||||||
| 		ctx.ServerError("DeleteOAuth2Application", err) | 		ctx.ServerError("DeleteOAuth2Application", err) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| @@ -147,7 +147,7 @@ func RevokeOAuth2Grant(ctx *context.Context) { | |||||||
| 		ctx.ServerError("RevokeOAuth2Grant", fmt.Errorf("user id or grant id is zero")) | 		ctx.ServerError("RevokeOAuth2Grant", fmt.Errorf("user id or grant id is zero")) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	if err := login.RevokeOAuth2Grant(ctx.FormInt64("id"), ctx.User.ID); err != nil { | 	if err := auth.RevokeOAuth2Grant(ctx.FormInt64("id"), ctx.User.ID); err != nil { | ||||||
| 		ctx.ServerError("RevokeOAuth2Grant", err) | 		ctx.ServerError("RevokeOAuth2Grant", err) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -3,7 +3,7 @@ | |||||||
| // Use of this source code is governed by a MIT-style | // Use of this source code is governed by a MIT-style | ||||||
| // license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||||
| 
 | 
 | ||||||
| package setting | package security | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"bytes" | 	"bytes" | ||||||
| @@ -13,7 +13,7 @@ import ( | |||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"strings" | 	"strings" | ||||||
| 
 | 
 | ||||||
| 	"code.gitea.io/gitea/models/login" | 	"code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/modules/context" | 	"code.gitea.io/gitea/modules/context" | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
| @@ -29,9 +29,9 @@ func RegenerateScratchTwoFactor(ctx *context.Context) { | |||||||
| 	ctx.Data["Title"] = ctx.Tr("settings") | 	ctx.Data["Title"] = ctx.Tr("settings") | ||||||
| 	ctx.Data["PageIsSettingsSecurity"] = true | 	ctx.Data["PageIsSettingsSecurity"] = true | ||||||
| 
 | 
 | ||||||
| 	t, err := login.GetTwoFactorByUID(ctx.User.ID) | 	t, err := auth.GetTwoFactorByUID(ctx.User.ID) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		if login.IsErrTwoFactorNotEnrolled(err) { | 		if auth.IsErrTwoFactorNotEnrolled(err) { | ||||||
| 			ctx.Flash.Error(ctx.Tr("settings.twofa_not_enrolled")) | 			ctx.Flash.Error(ctx.Tr("settings.twofa_not_enrolled")) | ||||||
| 			ctx.Redirect(setting.AppSubURL + "/user/settings/security") | 			ctx.Redirect(setting.AppSubURL + "/user/settings/security") | ||||||
| 		} | 		} | ||||||
| @@ -45,7 +45,7 @@ func RegenerateScratchTwoFactor(ctx *context.Context) { | |||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if err = login.UpdateTwoFactor(t); err != nil { | 	if err = auth.UpdateTwoFactor(t); err != nil { | ||||||
| 		ctx.ServerError("SettingsTwoFactor: Failed to UpdateTwoFactor", err) | 		ctx.ServerError("SettingsTwoFactor: Failed to UpdateTwoFactor", err) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| @@ -59,9 +59,9 @@ func DisableTwoFactor(ctx *context.Context) { | |||||||
| 	ctx.Data["Title"] = ctx.Tr("settings") | 	ctx.Data["Title"] = ctx.Tr("settings") | ||||||
| 	ctx.Data["PageIsSettingsSecurity"] = true | 	ctx.Data["PageIsSettingsSecurity"] = true | ||||||
| 
 | 
 | ||||||
| 	t, err := login.GetTwoFactorByUID(ctx.User.ID) | 	t, err := auth.GetTwoFactorByUID(ctx.User.ID) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		if login.IsErrTwoFactorNotEnrolled(err) { | 		if auth.IsErrTwoFactorNotEnrolled(err) { | ||||||
| 			ctx.Flash.Error(ctx.Tr("settings.twofa_not_enrolled")) | 			ctx.Flash.Error(ctx.Tr("settings.twofa_not_enrolled")) | ||||||
| 			ctx.Redirect(setting.AppSubURL + "/user/settings/security") | 			ctx.Redirect(setting.AppSubURL + "/user/settings/security") | ||||||
| 		} | 		} | ||||||
| @@ -69,8 +69,8 @@ func DisableTwoFactor(ctx *context.Context) { | |||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if err = login.DeleteTwoFactorByID(t.ID, ctx.User.ID); err != nil { | 	if err = auth.DeleteTwoFactorByID(t.ID, ctx.User.ID); err != nil { | ||||||
| 		if login.IsErrTwoFactorNotEnrolled(err) { | 		if auth.IsErrTwoFactorNotEnrolled(err) { | ||||||
| 			// There is a potential DB race here - we must have been disabled by another request in the intervening period | 			// There is a potential DB race here - we must have been disabled by another request in the intervening period | ||||||
| 			ctx.Flash.Success(ctx.Tr("settings.twofa_disabled")) | 			ctx.Flash.Success(ctx.Tr("settings.twofa_disabled")) | ||||||
| 			ctx.Redirect(setting.AppSubURL + "/user/settings/security") | 			ctx.Redirect(setting.AppSubURL + "/user/settings/security") | ||||||
| @@ -146,7 +146,7 @@ func EnrollTwoFactor(ctx *context.Context) { | |||||||
| 	ctx.Data["Title"] = ctx.Tr("settings") | 	ctx.Data["Title"] = ctx.Tr("settings") | ||||||
| 	ctx.Data["PageIsSettingsSecurity"] = true | 	ctx.Data["PageIsSettingsSecurity"] = true | ||||||
| 
 | 
 | ||||||
| 	t, err := login.GetTwoFactorByUID(ctx.User.ID) | 	t, err := auth.GetTwoFactorByUID(ctx.User.ID) | ||||||
| 	if t != nil { | 	if t != nil { | ||||||
| 		// already enrolled - we should redirect back! | 		// already enrolled - we should redirect back! | ||||||
| 		log.Warn("Trying to re-enroll %-v in twofa when already enrolled", ctx.User) | 		log.Warn("Trying to re-enroll %-v in twofa when already enrolled", ctx.User) | ||||||
| @@ -154,7 +154,7 @@ func EnrollTwoFactor(ctx *context.Context) { | |||||||
| 		ctx.Redirect(setting.AppSubURL + "/user/settings/security") | 		ctx.Redirect(setting.AppSubURL + "/user/settings/security") | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	if err != nil && !login.IsErrTwoFactorNotEnrolled(err) { | 	if err != nil && !auth.IsErrTwoFactorNotEnrolled(err) { | ||||||
| 		ctx.ServerError("SettingsTwoFactor: GetTwoFactorByUID", err) | 		ctx.ServerError("SettingsTwoFactor: GetTwoFactorByUID", err) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| @@ -172,14 +172,14 @@ func EnrollTwoFactorPost(ctx *context.Context) { | |||||||
| 	ctx.Data["Title"] = ctx.Tr("settings") | 	ctx.Data["Title"] = ctx.Tr("settings") | ||||||
| 	ctx.Data["PageIsSettingsSecurity"] = true | 	ctx.Data["PageIsSettingsSecurity"] = true | ||||||
| 
 | 
 | ||||||
| 	t, err := login.GetTwoFactorByUID(ctx.User.ID) | 	t, err := auth.GetTwoFactorByUID(ctx.User.ID) | ||||||
| 	if t != nil { | 	if t != nil { | ||||||
| 		// already enrolled | 		// already enrolled | ||||||
| 		ctx.Flash.Error(ctx.Tr("settings.twofa_is_enrolled")) | 		ctx.Flash.Error(ctx.Tr("settings.twofa_is_enrolled")) | ||||||
| 		ctx.Redirect(setting.AppSubURL + "/user/settings/security") | 		ctx.Redirect(setting.AppSubURL + "/user/settings/security") | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	if err != nil && !login.IsErrTwoFactorNotEnrolled(err) { | 	if err != nil && !auth.IsErrTwoFactorNotEnrolled(err) { | ||||||
| 		ctx.ServerError("SettingsTwoFactor: Failed to check if already enrolled with GetTwoFactorByUID", err) | 		ctx.ServerError("SettingsTwoFactor: Failed to check if already enrolled with GetTwoFactorByUID", err) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| @@ -209,7 +209,7 @@ func EnrollTwoFactorPost(ctx *context.Context) { | |||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	t = &login.TwoFactor{ | 	t = &auth.TwoFactor{ | ||||||
| 		UID: ctx.User.ID, | 		UID: ctx.User.ID, | ||||||
| 	} | 	} | ||||||
| 	err = t.SetSecret(secret) | 	err = t.SetSecret(secret) | ||||||
| @@ -238,7 +238,7 @@ func EnrollTwoFactorPost(ctx *context.Context) { | |||||||
| 		log.Error("Unable to save changes to the session: %v", err) | 		log.Error("Unable to save changes to the session: %v", err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if err = login.NewTwoFactor(t); err != nil { | 	if err = auth.NewTwoFactor(t); err != nil { | ||||||
| 		// FIXME: We need to handle a unique constraint fail here it's entirely possible that another request has beaten us. | 		// FIXME: We need to handle a unique constraint fail here it's entirely possible that another request has beaten us. | ||||||
| 		// If there is a unique constraint fail we should just tolerate the error | 		// If there is a unique constraint fail we should just tolerate the error | ||||||
| 		ctx.ServerError("SettingsTwoFactor: Failed to save two factor", err) | 		ctx.ServerError("SettingsTwoFactor: Failed to save two factor", err) | ||||||
| @@ -2,7 +2,7 @@ | |||||||
| // Use of this source code is governed by a MIT-style | // Use of this source code is governed by a MIT-style | ||||||
| // license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||||
| 
 | 
 | ||||||
| package setting | package security | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"net/http" | 	"net/http" | ||||||
| @@ -3,13 +3,13 @@ | |||||||
| // Use of this source code is governed by a MIT-style | // Use of this source code is governed by a MIT-style | ||||||
| // license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||||
| 
 | 
 | ||||||
| package setting | package security | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 
 | 
 | ||||||
| 	"code.gitea.io/gitea/models" | 	"code.gitea.io/gitea/models" | ||||||
| 	"code.gitea.io/gitea/models/login" | 	"code.gitea.io/gitea/models/auth" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	"code.gitea.io/gitea/modules/base" | 	"code.gitea.io/gitea/modules/base" | ||||||
| 	"code.gitea.io/gitea/modules/context" | 	"code.gitea.io/gitea/modules/context" | ||||||
| @@ -17,8 +17,8 @@ import ( | |||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| const ( | const ( | ||||||
| 	tplSettingsSecurity    base.TplName = "user/settings/security" | 	tplSettingsSecurity    base.TplName = "user/settings/security/security" | ||||||
| 	tplSettingsTwofaEnroll base.TplName = "user/settings/twofa_enroll" | 	tplSettingsTwofaEnroll base.TplName = "user/settings/security/twofa_enroll" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // Security render change user's password page and 2FA | // Security render change user's password page and 2FA | ||||||
| @@ -56,14 +56,14 @@ func DeleteAccountLink(ctx *context.Context) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func loadSecurityData(ctx *context.Context) { | func loadSecurityData(ctx *context.Context) { | ||||||
| 	enrolled, err := login.HasTwoFactorByUID(ctx.User.ID) | 	enrolled, err := auth.HasTwoFactorByUID(ctx.User.ID) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		ctx.ServerError("SettingsTwoFactor", err) | 		ctx.ServerError("SettingsTwoFactor", err) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	ctx.Data["TOTPEnrolled"] = enrolled | 	ctx.Data["TOTPEnrolled"] = enrolled | ||||||
| 
 | 
 | ||||||
| 	ctx.Data["U2FRegistrations"], err = login.GetU2FRegistrationsByUID(ctx.User.ID) | 	ctx.Data["U2FRegistrations"], err = auth.GetU2FRegistrationsByUID(ctx.User.ID) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		ctx.ServerError("GetU2FRegistrationsByUID", err) | 		ctx.ServerError("GetU2FRegistrationsByUID", err) | ||||||
| 		return | 		return | ||||||
| @@ -82,10 +82,10 @@ func loadSecurityData(ctx *context.Context) { | |||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// map the provider display name with the LoginSource | 	// map the provider display name with the AuthSource | ||||||
| 	sources := make(map[*login.Source]string) | 	sources := make(map[*auth.Source]string) | ||||||
| 	for _, externalAccount := range accountLinks { | 	for _, externalAccount := range accountLinks { | ||||||
| 		if loginSource, err := login.GetSourceByID(externalAccount.LoginSourceID); err == nil { | 		if authSource, err := auth.GetSourceByID(externalAccount.LoginSourceID); err == nil { | ||||||
| 			var providerDisplayName string | 			var providerDisplayName string | ||||||
| 
 | 
 | ||||||
| 			type DisplayNamed interface { | 			type DisplayNamed interface { | ||||||
| @@ -96,14 +96,14 @@ func loadSecurityData(ctx *context.Context) { | |||||||
| 				Name() string | 				Name() string | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			if displayNamed, ok := loginSource.Cfg.(DisplayNamed); ok { | 			if displayNamed, ok := authSource.Cfg.(DisplayNamed); ok { | ||||||
| 				providerDisplayName = displayNamed.DisplayName() | 				providerDisplayName = displayNamed.DisplayName() | ||||||
| 			} else if named, ok := loginSource.Cfg.(Named); ok { | 			} else if named, ok := authSource.Cfg.(Named); ok { | ||||||
| 				providerDisplayName = named.Name() | 				providerDisplayName = named.Name() | ||||||
| 			} else { | 			} else { | ||||||
| 				providerDisplayName = loginSource.Name | 				providerDisplayName = authSource.Name | ||||||
| 			} | 			} | ||||||
| 			sources[loginSource] = providerDisplayName | 			sources[authSource] = providerDisplayName | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	ctx.Data["AccountLinks"] = sources | 	ctx.Data["AccountLinks"] = sources | ||||||
| @@ -2,13 +2,13 @@ | |||||||
| // Use of this source code is governed by a MIT-style | // Use of this source code is governed by a MIT-style | ||||||
| // license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||||
| 
 | 
 | ||||||
| package setting | package security | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"errors" | 	"errors" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 
 | 
 | ||||||
| 	"code.gitea.io/gitea/models/login" | 	"code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/modules/context" | 	"code.gitea.io/gitea/modules/context" | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
| @@ -34,7 +34,7 @@ func U2FRegister(ctx *context.Context) { | |||||||
| 		ctx.ServerError("Unable to set session key for u2fChallenge", err) | 		ctx.ServerError("Unable to set session key for u2fChallenge", err) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	regs, err := login.GetU2FRegistrationsByUID(ctx.User.ID) | 	regs, err := auth.GetU2FRegistrationsByUID(ctx.User.ID) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		ctx.ServerError("GetU2FRegistrationsByUID", err) | 		ctx.ServerError("GetU2FRegistrationsByUID", err) | ||||||
| 		return | 		return | ||||||
| @@ -78,7 +78,7 @@ func U2FRegisterPost(ctx *context.Context) { | |||||||
| 		ctx.ServerError("u2f.Register", err) | 		ctx.ServerError("u2f.Register", err) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	if _, err = login.CreateRegistration(ctx.User.ID, name, reg); err != nil { | 	if _, err = auth.CreateRegistration(ctx.User.ID, name, reg); err != nil { | ||||||
| 		ctx.ServerError("u2f.Register", err) | 		ctx.ServerError("u2f.Register", err) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| @@ -88,9 +88,9 @@ func U2FRegisterPost(ctx *context.Context) { | |||||||
| // U2FDelete deletes an security key by id | // U2FDelete deletes an security key by id | ||||||
| func U2FDelete(ctx *context.Context) { | func U2FDelete(ctx *context.Context) { | ||||||
| 	form := web.GetForm(ctx).(*forms.U2FDeleteForm) | 	form := web.GetForm(ctx).(*forms.U2FDeleteForm) | ||||||
| 	reg, err := login.GetU2FRegistrationByID(form.ID) | 	reg, err := auth.GetU2FRegistrationByID(form.ID) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		if login.IsErrU2FRegistrationNotExist(err) { | 		if auth.IsErrU2FRegistrationNotExist(err) { | ||||||
| 			ctx.Status(200) | 			ctx.Status(200) | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| @@ -101,7 +101,7 @@ func U2FDelete(ctx *context.Context) { | |||||||
| 		ctx.Status(401) | 		ctx.Status(401) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	if err := login.DeleteRegistration(reg); err != nil { | 	if err := auth.DeleteRegistration(reg); err != nil { | ||||||
| 		ctx.ServerError("DeleteRegistration", err) | 		ctx.ServerError("DeleteRegistration", err) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| @@ -24,14 +24,16 @@ import ( | |||||||
| 	"code.gitea.io/gitea/modules/web" | 	"code.gitea.io/gitea/modules/web" | ||||||
| 	"code.gitea.io/gitea/routers/api/v1/misc" | 	"code.gitea.io/gitea/routers/api/v1/misc" | ||||||
| 	"code.gitea.io/gitea/routers/web/admin" | 	"code.gitea.io/gitea/routers/web/admin" | ||||||
|  | 	"code.gitea.io/gitea/routers/web/auth" | ||||||
| 	"code.gitea.io/gitea/routers/web/dev" | 	"code.gitea.io/gitea/routers/web/dev" | ||||||
| 	"code.gitea.io/gitea/routers/web/events" | 	"code.gitea.io/gitea/routers/web/events" | ||||||
| 	"code.gitea.io/gitea/routers/web/explore" | 	"code.gitea.io/gitea/routers/web/explore" | ||||||
| 	"code.gitea.io/gitea/routers/web/org" | 	"code.gitea.io/gitea/routers/web/org" | ||||||
| 	"code.gitea.io/gitea/routers/web/repo" | 	"code.gitea.io/gitea/routers/web/repo" | ||||||
| 	"code.gitea.io/gitea/routers/web/user" | 	"code.gitea.io/gitea/routers/web/user" | ||||||
| 	userSetting "code.gitea.io/gitea/routers/web/user/setting" | 	user_setting "code.gitea.io/gitea/routers/web/user/setting" | ||||||
| 	"code.gitea.io/gitea/services/auth" | 	"code.gitea.io/gitea/routers/web/user/setting/security" | ||||||
|  | 	auth_service "code.gitea.io/gitea/services/auth" | ||||||
| 	"code.gitea.io/gitea/services/forms" | 	"code.gitea.io/gitea/services/forms" | ||||||
| 	"code.gitea.io/gitea/services/lfs" | 	"code.gitea.io/gitea/services/lfs" | ||||||
| 	"code.gitea.io/gitea/services/mailer" | 	"code.gitea.io/gitea/services/mailer" | ||||||
| @@ -154,7 +156,7 @@ func Routes(sessioner func(http.Handler) http.Handler) *web.Route { | |||||||
| 	common = append(common, context.Contexter()) | 	common = append(common, context.Contexter()) | ||||||
|  |  | ||||||
| 	// Get user from session if logged in. | 	// Get user from session if logged in. | ||||||
| 	common = append(common, context.Auth(auth.NewGroup(auth.Methods()...))) | 	common = append(common, context.Auth(auth_service.NewGroup(auth_service.Methods()...))) | ||||||
|  |  | ||||||
| 	// GetHead allows a HEAD request redirect to GET if HEAD method is not defined for that route | 	// GetHead allows a HEAD request redirect to GET if HEAD method is not defined for that route | ||||||
| 	common = append(common, middleware.GetHead) | 	common = append(common, middleware.GetHead) | ||||||
| @@ -233,7 +235,7 @@ func RegisterRoutes(m *web.Route) { | |||||||
| 	// for health check | 	// for health check | ||||||
| 	m.Get("/", Home) | 	m.Get("/", Home) | ||||||
| 	m.Group("/.well-known", func() { | 	m.Group("/.well-known", func() { | ||||||
| 		m.Get("/openid-configuration", user.OIDCWellKnown) | 		m.Get("/openid-configuration", auth.OIDCWellKnown) | ||||||
| 		if setting.Federation.Enabled { | 		if setting.Federation.Enabled { | ||||||
| 			m.Get("/nodeinfo", NodeInfoLinks) | 			m.Get("/nodeinfo", NodeInfoLinks) | ||||||
| 		} | 		} | ||||||
| @@ -257,42 +259,42 @@ func RegisterRoutes(m *web.Route) { | |||||||
|  |  | ||||||
| 	// ***** START: User ***** | 	// ***** START: User ***** | ||||||
| 	m.Group("/user", func() { | 	m.Group("/user", func() { | ||||||
| 		m.Get("/login", user.SignIn) | 		m.Get("/login", auth.SignIn) | ||||||
| 		m.Post("/login", bindIgnErr(forms.SignInForm{}), user.SignInPost) | 		m.Post("/login", bindIgnErr(forms.SignInForm{}), auth.SignInPost) | ||||||
| 		m.Group("", func() { | 		m.Group("", func() { | ||||||
| 			m.Combo("/login/openid"). | 			m.Combo("/login/openid"). | ||||||
| 				Get(user.SignInOpenID). | 				Get(auth.SignInOpenID). | ||||||
| 				Post(bindIgnErr(forms.SignInOpenIDForm{}), user.SignInOpenIDPost) | 				Post(bindIgnErr(forms.SignInOpenIDForm{}), auth.SignInOpenIDPost) | ||||||
| 		}, openIDSignInEnabled) | 		}, openIDSignInEnabled) | ||||||
| 		m.Group("/openid", func() { | 		m.Group("/openid", func() { | ||||||
| 			m.Combo("/connect"). | 			m.Combo("/connect"). | ||||||
| 				Get(user.ConnectOpenID). | 				Get(auth.ConnectOpenID). | ||||||
| 				Post(bindIgnErr(forms.ConnectOpenIDForm{}), user.ConnectOpenIDPost) | 				Post(bindIgnErr(forms.ConnectOpenIDForm{}), auth.ConnectOpenIDPost) | ||||||
| 			m.Group("/register", func() { | 			m.Group("/register", func() { | ||||||
| 				m.Combo(""). | 				m.Combo(""). | ||||||
| 					Get(user.RegisterOpenID, openIDSignUpEnabled). | 					Get(auth.RegisterOpenID, openIDSignUpEnabled). | ||||||
| 					Post(bindIgnErr(forms.SignUpOpenIDForm{}), user.RegisterOpenIDPost) | 					Post(bindIgnErr(forms.SignUpOpenIDForm{}), auth.RegisterOpenIDPost) | ||||||
| 			}, openIDSignUpEnabled) | 			}, openIDSignUpEnabled) | ||||||
| 		}, openIDSignInEnabled) | 		}, openIDSignInEnabled) | ||||||
| 		m.Get("/sign_up", user.SignUp) | 		m.Get("/sign_up", auth.SignUp) | ||||||
| 		m.Post("/sign_up", bindIgnErr(forms.RegisterForm{}), user.SignUpPost) | 		m.Post("/sign_up", bindIgnErr(forms.RegisterForm{}), auth.SignUpPost) | ||||||
| 		m.Group("/oauth2", func() { | 		m.Group("/oauth2", func() { | ||||||
| 			m.Get("/{provider}", user.SignInOAuth) | 			m.Get("/{provider}", auth.SignInOAuth) | ||||||
| 			m.Get("/{provider}/callback", user.SignInOAuthCallback) | 			m.Get("/{provider}/callback", auth.SignInOAuthCallback) | ||||||
| 		}) | 		}) | ||||||
| 		m.Get("/link_account", user.LinkAccount) | 		m.Get("/link_account", auth.LinkAccount) | ||||||
| 		m.Post("/link_account_signin", bindIgnErr(forms.SignInForm{}), user.LinkAccountPostSignIn) | 		m.Post("/link_account_signin", bindIgnErr(forms.SignInForm{}), auth.LinkAccountPostSignIn) | ||||||
| 		m.Post("/link_account_signup", bindIgnErr(forms.RegisterForm{}), user.LinkAccountPostRegister) | 		m.Post("/link_account_signup", bindIgnErr(forms.RegisterForm{}), auth.LinkAccountPostRegister) | ||||||
| 		m.Group("/two_factor", func() { | 		m.Group("/two_factor", func() { | ||||||
| 			m.Get("", user.TwoFactor) | 			m.Get("", auth.TwoFactor) | ||||||
| 			m.Post("", bindIgnErr(forms.TwoFactorAuthForm{}), user.TwoFactorPost) | 			m.Post("", bindIgnErr(forms.TwoFactorAuthForm{}), auth.TwoFactorPost) | ||||||
| 			m.Get("/scratch", user.TwoFactorScratch) | 			m.Get("/scratch", auth.TwoFactorScratch) | ||||||
| 			m.Post("/scratch", bindIgnErr(forms.TwoFactorScratchAuthForm{}), user.TwoFactorScratchPost) | 			m.Post("/scratch", bindIgnErr(forms.TwoFactorScratchAuthForm{}), auth.TwoFactorScratchPost) | ||||||
| 		}) | 		}) | ||||||
| 		m.Group("/u2f", func() { | 		m.Group("/u2f", func() { | ||||||
| 			m.Get("", user.U2F) | 			m.Get("", auth.U2F) | ||||||
| 			m.Get("/challenge", user.U2FChallenge) | 			m.Get("/challenge", auth.U2FChallenge) | ||||||
| 			m.Post("/sign", bindIgnErr(u2f.SignResponse{}), user.U2FSign) | 			m.Post("/sign", bindIgnErr(u2f.SignResponse{}), auth.U2FSign) | ||||||
|  |  | ||||||
| 		}) | 		}) | ||||||
| 	}, reqSignOut) | 	}, reqSignOut) | ||||||
| @@ -300,71 +302,71 @@ func RegisterRoutes(m *web.Route) { | |||||||
| 	m.Any("/user/events", events.Events) | 	m.Any("/user/events", events.Events) | ||||||
|  |  | ||||||
| 	m.Group("/login/oauth", func() { | 	m.Group("/login/oauth", func() { | ||||||
| 		m.Get("/authorize", bindIgnErr(forms.AuthorizationForm{}), user.AuthorizeOAuth) | 		m.Get("/authorize", bindIgnErr(forms.AuthorizationForm{}), auth.AuthorizeOAuth) | ||||||
| 		m.Post("/grant", bindIgnErr(forms.GrantApplicationForm{}), user.GrantApplicationOAuth) | 		m.Post("/grant", bindIgnErr(forms.GrantApplicationForm{}), auth.GrantApplicationOAuth) | ||||||
| 		// TODO manage redirection | 		// TODO manage redirection | ||||||
| 		m.Post("/authorize", bindIgnErr(forms.AuthorizationForm{}), user.AuthorizeOAuth) | 		m.Post("/authorize", bindIgnErr(forms.AuthorizationForm{}), auth.AuthorizeOAuth) | ||||||
| 	}, ignSignInAndCsrf, reqSignIn) | 	}, ignSignInAndCsrf, reqSignIn) | ||||||
| 	m.Get("/login/oauth/userinfo", ignSignInAndCsrf, user.InfoOAuth) | 	m.Get("/login/oauth/userinfo", ignSignInAndCsrf, auth.InfoOAuth) | ||||||
| 	m.Post("/login/oauth/access_token", CorsHandler(), bindIgnErr(forms.AccessTokenForm{}), ignSignInAndCsrf, user.AccessTokenOAuth) | 	m.Post("/login/oauth/access_token", CorsHandler(), bindIgnErr(forms.AccessTokenForm{}), ignSignInAndCsrf, auth.AccessTokenOAuth) | ||||||
| 	m.Get("/login/oauth/keys", ignSignInAndCsrf, user.OIDCKeys) | 	m.Get("/login/oauth/keys", ignSignInAndCsrf, auth.OIDCKeys) | ||||||
| 	m.Post("/login/oauth/introspect", CorsHandler(), bindIgnErr(forms.IntrospectTokenForm{}), ignSignInAndCsrf, user.IntrospectOAuth) | 	m.Post("/login/oauth/introspect", CorsHandler(), bindIgnErr(forms.IntrospectTokenForm{}), ignSignInAndCsrf, auth.IntrospectOAuth) | ||||||
|  |  | ||||||
| 	m.Group("/user/settings", func() { | 	m.Group("/user/settings", func() { | ||||||
| 		m.Get("", userSetting.Profile) | 		m.Get("", user_setting.Profile) | ||||||
| 		m.Post("", bindIgnErr(forms.UpdateProfileForm{}), userSetting.ProfilePost) | 		m.Post("", bindIgnErr(forms.UpdateProfileForm{}), user_setting.ProfilePost) | ||||||
| 		m.Get("/change_password", user.MustChangePassword) | 		m.Get("/change_password", auth.MustChangePassword) | ||||||
| 		m.Post("/change_password", bindIgnErr(forms.MustChangePasswordForm{}), user.MustChangePasswordPost) | 		m.Post("/change_password", bindIgnErr(forms.MustChangePasswordForm{}), auth.MustChangePasswordPost) | ||||||
| 		m.Post("/avatar", bindIgnErr(forms.AvatarForm{}), userSetting.AvatarPost) | 		m.Post("/avatar", bindIgnErr(forms.AvatarForm{}), user_setting.AvatarPost) | ||||||
| 		m.Post("/avatar/delete", userSetting.DeleteAvatar) | 		m.Post("/avatar/delete", user_setting.DeleteAvatar) | ||||||
| 		m.Group("/account", func() { | 		m.Group("/account", func() { | ||||||
| 			m.Combo("").Get(userSetting.Account).Post(bindIgnErr(forms.ChangePasswordForm{}), userSetting.AccountPost) | 			m.Combo("").Get(user_setting.Account).Post(bindIgnErr(forms.ChangePasswordForm{}), user_setting.AccountPost) | ||||||
| 			m.Post("/email", bindIgnErr(forms.AddEmailForm{}), userSetting.EmailPost) | 			m.Post("/email", bindIgnErr(forms.AddEmailForm{}), user_setting.EmailPost) | ||||||
| 			m.Post("/email/delete", userSetting.DeleteEmail) | 			m.Post("/email/delete", user_setting.DeleteEmail) | ||||||
| 			m.Post("/delete", userSetting.DeleteAccount) | 			m.Post("/delete", user_setting.DeleteAccount) | ||||||
| 		}) | 		}) | ||||||
| 		m.Group("/appearance", func() { | 		m.Group("/appearance", func() { | ||||||
| 			m.Get("", userSetting.Appearance) | 			m.Get("", user_setting.Appearance) | ||||||
| 			m.Post("/language", bindIgnErr(forms.UpdateLanguageForm{}), userSetting.UpdateUserLang) | 			m.Post("/language", bindIgnErr(forms.UpdateLanguageForm{}), user_setting.UpdateUserLang) | ||||||
| 			m.Post("/theme", bindIgnErr(forms.UpdateThemeForm{}), userSetting.UpdateUIThemePost) | 			m.Post("/theme", bindIgnErr(forms.UpdateThemeForm{}), user_setting.UpdateUIThemePost) | ||||||
| 		}) | 		}) | ||||||
| 		m.Group("/security", func() { | 		m.Group("/security", func() { | ||||||
| 			m.Get("", userSetting.Security) | 			m.Get("", security.Security) | ||||||
| 			m.Group("/two_factor", func() { | 			m.Group("/two_factor", func() { | ||||||
| 				m.Post("/regenerate_scratch", userSetting.RegenerateScratchTwoFactor) | 				m.Post("/regenerate_scratch", security.RegenerateScratchTwoFactor) | ||||||
| 				m.Post("/disable", userSetting.DisableTwoFactor) | 				m.Post("/disable", security.DisableTwoFactor) | ||||||
| 				m.Get("/enroll", userSetting.EnrollTwoFactor) | 				m.Get("/enroll", security.EnrollTwoFactor) | ||||||
| 				m.Post("/enroll", bindIgnErr(forms.TwoFactorAuthForm{}), userSetting.EnrollTwoFactorPost) | 				m.Post("/enroll", bindIgnErr(forms.TwoFactorAuthForm{}), security.EnrollTwoFactorPost) | ||||||
| 			}) | 			}) | ||||||
| 			m.Group("/u2f", func() { | 			m.Group("/u2f", func() { | ||||||
| 				m.Post("/request_register", bindIgnErr(forms.U2FRegistrationForm{}), userSetting.U2FRegister) | 				m.Post("/request_register", bindIgnErr(forms.U2FRegistrationForm{}), security.U2FRegister) | ||||||
| 				m.Post("/register", bindIgnErr(u2f.RegisterResponse{}), userSetting.U2FRegisterPost) | 				m.Post("/register", bindIgnErr(u2f.RegisterResponse{}), security.U2FRegisterPost) | ||||||
| 				m.Post("/delete", bindIgnErr(forms.U2FDeleteForm{}), userSetting.U2FDelete) | 				m.Post("/delete", bindIgnErr(forms.U2FDeleteForm{}), security.U2FDelete) | ||||||
| 			}) | 			}) | ||||||
| 			m.Group("/openid", func() { | 			m.Group("/openid", func() { | ||||||
| 				m.Post("", bindIgnErr(forms.AddOpenIDForm{}), userSetting.OpenIDPost) | 				m.Post("", bindIgnErr(forms.AddOpenIDForm{}), security.OpenIDPost) | ||||||
| 				m.Post("/delete", userSetting.DeleteOpenID) | 				m.Post("/delete", security.DeleteOpenID) | ||||||
| 				m.Post("/toggle_visibility", userSetting.ToggleOpenIDVisibility) | 				m.Post("/toggle_visibility", security.ToggleOpenIDVisibility) | ||||||
| 			}, openIDSignInEnabled) | 			}, openIDSignInEnabled) | ||||||
| 			m.Post("/account_link", userSetting.DeleteAccountLink) | 			m.Post("/account_link", security.DeleteAccountLink) | ||||||
| 		}) | 		}) | ||||||
| 		m.Group("/applications/oauth2", func() { | 		m.Group("/applications/oauth2", func() { | ||||||
| 			m.Get("/{id}", userSetting.OAuth2ApplicationShow) | 			m.Get("/{id}", user_setting.OAuth2ApplicationShow) | ||||||
| 			m.Post("/{id}", bindIgnErr(forms.EditOAuth2ApplicationForm{}), userSetting.OAuthApplicationsEdit) | 			m.Post("/{id}", bindIgnErr(forms.EditOAuth2ApplicationForm{}), user_setting.OAuthApplicationsEdit) | ||||||
| 			m.Post("/{id}/regenerate_secret", userSetting.OAuthApplicationsRegenerateSecret) | 			m.Post("/{id}/regenerate_secret", user_setting.OAuthApplicationsRegenerateSecret) | ||||||
| 			m.Post("", bindIgnErr(forms.EditOAuth2ApplicationForm{}), userSetting.OAuthApplicationsPost) | 			m.Post("", bindIgnErr(forms.EditOAuth2ApplicationForm{}), user_setting.OAuthApplicationsPost) | ||||||
| 			m.Post("/delete", userSetting.DeleteOAuth2Application) | 			m.Post("/delete", user_setting.DeleteOAuth2Application) | ||||||
| 			m.Post("/revoke", userSetting.RevokeOAuth2Grant) | 			m.Post("/revoke", user_setting.RevokeOAuth2Grant) | ||||||
| 		}) | 		}) | ||||||
| 		m.Combo("/applications").Get(userSetting.Applications). | 		m.Combo("/applications").Get(user_setting.Applications). | ||||||
| 			Post(bindIgnErr(forms.NewAccessTokenForm{}), userSetting.ApplicationsPost) | 			Post(bindIgnErr(forms.NewAccessTokenForm{}), user_setting.ApplicationsPost) | ||||||
| 		m.Post("/applications/delete", userSetting.DeleteApplication) | 		m.Post("/applications/delete", user_setting.DeleteApplication) | ||||||
| 		m.Combo("/keys").Get(userSetting.Keys). | 		m.Combo("/keys").Get(user_setting.Keys). | ||||||
| 			Post(bindIgnErr(forms.AddKeyForm{}), userSetting.KeysPost) | 			Post(bindIgnErr(forms.AddKeyForm{}), user_setting.KeysPost) | ||||||
| 		m.Post("/keys/delete", userSetting.DeleteKey) | 		m.Post("/keys/delete", user_setting.DeleteKey) | ||||||
| 		m.Get("/organization", userSetting.Organization) | 		m.Get("/organization", user_setting.Organization) | ||||||
| 		m.Get("/repos", userSetting.Repos) | 		m.Get("/repos", user_setting.Repos) | ||||||
| 		m.Post("/repos/unadopted", userSetting.AdoptOrDeleteRepository) | 		m.Post("/repos/unadopted", user_setting.AdoptOrDeleteRepository) | ||||||
| 	}, reqSignIn, func(ctx *context.Context) { | 	}, reqSignIn, func(ctx *context.Context) { | ||||||
| 		ctx.Data["PageIsUserSettings"] = true | 		ctx.Data["PageIsUserSettings"] = true | ||||||
| 		ctx.Data["AllThemes"] = setting.UI.Themes | 		ctx.Data["AllThemes"] = setting.UI.Themes | ||||||
| @@ -372,15 +374,15 @@ func RegisterRoutes(m *web.Route) { | |||||||
|  |  | ||||||
| 	m.Group("/user", func() { | 	m.Group("/user", func() { | ||||||
| 		// r.Get("/feeds", binding.Bind(auth.FeedsForm{}), user.Feeds) | 		// r.Get("/feeds", binding.Bind(auth.FeedsForm{}), user.Feeds) | ||||||
| 		m.Get("/activate", user.Activate, reqSignIn) | 		m.Get("/activate", auth.Activate, reqSignIn) | ||||||
| 		m.Post("/activate", user.ActivatePost, reqSignIn) | 		m.Post("/activate", auth.ActivatePost, reqSignIn) | ||||||
| 		m.Any("/activate_email", user.ActivateEmail) | 		m.Any("/activate_email", auth.ActivateEmail) | ||||||
| 		m.Get("/avatar/{username}/{size}", user.AvatarByUserName) | 		m.Get("/avatar/{username}/{size}", user.AvatarByUserName) | ||||||
| 		m.Get("/recover_account", user.ResetPasswd) | 		m.Get("/recover_account", auth.ResetPasswd) | ||||||
| 		m.Post("/recover_account", user.ResetPasswdPost) | 		m.Post("/recover_account", auth.ResetPasswdPost) | ||||||
| 		m.Get("/forgot_password", user.ForgotPasswd) | 		m.Get("/forgot_password", auth.ForgotPasswd) | ||||||
| 		m.Post("/forgot_password", user.ForgotPasswdPost) | 		m.Post("/forgot_password", auth.ForgotPasswdPost) | ||||||
| 		m.Post("/logout", user.SignOut) | 		m.Post("/logout", auth.SignOut) | ||||||
| 		m.Get("/task/{task}", user.TaskStatus) | 		m.Get("/task/{task}", user.TaskStatus) | ||||||
| 	}) | 	}) | ||||||
| 	// ***** END: User ***** | 	// ***** END: User ***** | ||||||
|   | |||||||
| @@ -10,8 +10,8 @@ import ( | |||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" | 	"code.gitea.io/gitea/models" | ||||||
| 	asymkey_model "code.gitea.io/gitea/models/asymkey" | 	asymkey_model "code.gitea.io/gitea/models/asymkey" | ||||||
|  | 	"code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	"code.gitea.io/gitea/models/login" |  | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	"code.gitea.io/gitea/modules/git" | 	"code.gitea.io/gitea/modules/git" | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| @@ -150,8 +150,8 @@ Loop: | |||||||
| 				return false, "", nil, &ErrWontSign{pubkey} | 				return false, "", nil, &ErrWontSign{pubkey} | ||||||
| 			} | 			} | ||||||
| 		case twofa: | 		case twofa: | ||||||
| 			twofaModel, err := login.GetTwoFactorByUID(u.ID) | 			twofaModel, err := auth.GetTwoFactorByUID(u.ID) | ||||||
| 			if err != nil && !login.IsErrTwoFactorNotEnrolled(err) { | 			if err != nil && !auth.IsErrTwoFactorNotEnrolled(err) { | ||||||
| 				return false, "", nil, err | 				return false, "", nil, err | ||||||
| 			} | 			} | ||||||
| 			if twofaModel == nil { | 			if twofaModel == nil { | ||||||
| @@ -186,8 +186,8 @@ Loop: | |||||||
| 				return false, "", nil, &ErrWontSign{pubkey} | 				return false, "", nil, &ErrWontSign{pubkey} | ||||||
| 			} | 			} | ||||||
| 		case twofa: | 		case twofa: | ||||||
| 			twofaModel, err := login.GetTwoFactorByUID(u.ID) | 			twofaModel, err := auth.GetTwoFactorByUID(u.ID) | ||||||
| 			if err != nil && !login.IsErrTwoFactorNotEnrolled(err) { | 			if err != nil && !auth.IsErrTwoFactorNotEnrolled(err) { | ||||||
| 				return false, "", nil, err | 				return false, "", nil, err | ||||||
| 			} | 			} | ||||||
| 			if twofaModel == nil { | 			if twofaModel == nil { | ||||||
| @@ -239,8 +239,8 @@ Loop: | |||||||
| 				return false, "", nil, &ErrWontSign{pubkey} | 				return false, "", nil, &ErrWontSign{pubkey} | ||||||
| 			} | 			} | ||||||
| 		case twofa: | 		case twofa: | ||||||
| 			twofaModel, err := login.GetTwoFactorByUID(u.ID) | 			twofaModel, err := auth.GetTwoFactorByUID(u.ID) | ||||||
| 			if err != nil && !login.IsErrTwoFactorNotEnrolled(err) { | 			if err != nil && !auth.IsErrTwoFactorNotEnrolled(err) { | ||||||
| 				return false, "", nil, err | 				return false, "", nil, err | ||||||
| 			} | 			} | ||||||
| 			if twofaModel == nil { | 			if twofaModel == nil { | ||||||
| @@ -301,8 +301,8 @@ Loop: | |||||||
| 				return false, "", nil, &ErrWontSign{pubkey} | 				return false, "", nil, &ErrWontSign{pubkey} | ||||||
| 			} | 			} | ||||||
| 		case twofa: | 		case twofa: | ||||||
| 			twofaModel, err := login.GetTwoFactorByUID(u.ID) | 			twofaModel, err := auth.GetTwoFactorByUID(u.ID) | ||||||
| 			if err != nil && !login.IsErrTwoFactorNotEnrolled(err) { | 			if err != nil && !auth.IsErrTwoFactorNotEnrolled(err) { | ||||||
| 				return false, "", nil, err | 				return false, "", nil, err | ||||||
| 			} | 			} | ||||||
| 			if twofaModel == nil { | 			if twofaModel == nil { | ||||||
|   | |||||||
| @@ -8,7 +8,7 @@ import ( | |||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
| 	asymkey_model "code.gitea.io/gitea/models/asymkey" | 	asymkey_model "code.gitea.io/gitea/models/asymkey" | ||||||
| 	"code.gitea.io/gitea/models/login" | 	"code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
|  |  | ||||||
| @@ -19,7 +19,7 @@ func TestAddLdapSSHPublicKeys(t *testing.T) { | |||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
|  |  | ||||||
| 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) | 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) | ||||||
| 	s := &login.Source{ID: 1} | 	s := &auth.Source{ID: 1} | ||||||
|  |  | ||||||
| 	testCases := []struct { | 	testCases := []struct { | ||||||
| 		keyString   string | 		keyString   string | ||||||
|   | |||||||
| @@ -11,8 +11,8 @@ import ( | |||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" | 	"code.gitea.io/gitea/models" | ||||||
|  | 	"code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	"code.gitea.io/gitea/models/login" |  | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| 	"code.gitea.io/gitea/modules/timeutil" | 	"code.gitea.io/gitea/modules/timeutil" | ||||||
| @@ -37,8 +37,8 @@ func CheckOAuthAccessToken(accessToken string) int64 { | |||||||
| 		log.Trace("oauth2.ParseToken: %v", err) | 		log.Trace("oauth2.ParseToken: %v", err) | ||||||
| 		return 0 | 		return 0 | ||||||
| 	} | 	} | ||||||
| 	var grant *login.OAuth2Grant | 	var grant *auth.OAuth2Grant | ||||||
| 	if grant, err = login.GetOAuth2GrantByID(token.GrantID); err != nil || grant == nil { | 	if grant, err = auth.GetOAuth2GrantByID(token.GrantID); err != nil || grant == nil { | ||||||
| 		return 0 | 		return 0 | ||||||
| 	} | 	} | ||||||
| 	if token.Type != oauth2.TypeAccessToken { | 	if token.Type != oauth2.TypeAccessToken { | ||||||
|   | |||||||
| @@ -7,8 +7,8 @@ package auth | |||||||
| import ( | import ( | ||||||
| 	"strings" | 	"strings" | ||||||
|  |  | ||||||
|  | 	"code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	"code.gitea.io/gitea/models/login" |  | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| 	"code.gitea.io/gitea/services/auth/source/oauth2" | 	"code.gitea.io/gitea/services/auth/source/oauth2" | ||||||
| @@ -21,7 +21,7 @@ import ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| // UserSignIn validates user name and password. | // UserSignIn validates user name and password. | ||||||
| func UserSignIn(username, password string) (*user_model.User, *login.Source, error) { | func UserSignIn(username, password string) (*user_model.User, *auth.Source, error) { | ||||||
| 	var user *user_model.User | 	var user *user_model.User | ||||||
| 	if strings.Contains(username, "@") { | 	if strings.Contains(username, "@") { | ||||||
| 		user = &user_model.User{Email: strings.ToLower(strings.TrimSpace(username))} | 		user = &user_model.User{Email: strings.ToLower(strings.TrimSpace(username))} | ||||||
| @@ -50,13 +50,13 @@ func UserSignIn(username, password string) (*user_model.User, *login.Source, err | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if hasUser { | 	if hasUser { | ||||||
| 		source, err := login.GetSourceByID(user.LoginSource) | 		source, err := auth.GetSourceByID(user.LoginSource) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return nil, nil, err | 			return nil, nil, err | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if !source.IsActive { | 		if !source.IsActive { | ||||||
| 			return nil, nil, oauth2.ErrLoginSourceNotActived | 			return nil, nil, oauth2.ErrAuthSourceNotActived | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		authenticator, ok := source.Cfg.(PasswordAuthenticator) | 		authenticator, ok := source.Cfg.(PasswordAuthenticator) | ||||||
| @@ -78,7 +78,7 @@ func UserSignIn(username, password string) (*user_model.User, *login.Source, err | |||||||
| 		return user, source, nil | 		return user, source, nil | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	sources, err := login.AllActiveSources() | 	sources, err := auth.AllActiveSources() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, nil, err | 		return nil, nil, err | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -5,18 +5,18 @@ | |||||||
| package auth | package auth | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
|  | 	"code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	"code.gitea.io/gitea/models/login" |  | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // DeleteLoginSource deletes a LoginSource record in DB. | // DeleteSource deletes a AuthSource record in DB. | ||||||
| func DeleteLoginSource(source *login.Source) error { | func DeleteSource(source *auth.Source) error { | ||||||
| 	count, err := db.GetEngine(db.DefaultContext).Count(&user_model.User{LoginSource: source.ID}) | 	count, err := db.GetEngine(db.DefaultContext).Count(&user_model.User{LoginSource: source.ID}) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} else if count > 0 { | 	} else if count > 0 { | ||||||
| 		return login.ErrSourceInUse{ | 		return auth.ErrSourceInUse{ | ||||||
| 			ID: source.ID, | 			ID: source.ID, | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| @@ -25,17 +25,17 @@ func DeleteLoginSource(source *login.Source) error { | |||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} else if count > 0 { | 	} else if count > 0 { | ||||||
| 		return login.ErrSourceInUse{ | 		return auth.ErrSourceInUse{ | ||||||
| 			ID: source.ID, | 			ID: source.ID, | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if registerableSource, ok := source.Cfg.(login.RegisterableSource); ok { | 	if registerableSource, ok := source.Cfg.(auth.RegisterableSource); ok { | ||||||
| 		if err := registerableSource.UnregisterSource(); err != nil { | 		if err := registerableSource.UnregisterSource(); err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	_, err = db.GetEngine(db.DefaultContext).ID(source.ID).Delete(new(login.Source)) | 	_, err = db.GetEngine(db.DefaultContext).ID(source.ID).Delete(new(auth.Source)) | ||||||
| 	return err | 	return err | ||||||
| } | } | ||||||
| @@ -5,7 +5,7 @@ | |||||||
| package db_test | package db_test | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"code.gitea.io/gitea/models/login" | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/services/auth" | 	"code.gitea.io/gitea/services/auth" | ||||||
| 	"code.gitea.io/gitea/services/auth/source/db" | 	"code.gitea.io/gitea/services/auth/source/db" | ||||||
| ) | ) | ||||||
| @@ -15,7 +15,7 @@ import ( | |||||||
|  |  | ||||||
| type sourceInterface interface { | type sourceInterface interface { | ||||||
| 	auth.PasswordAuthenticator | 	auth.PasswordAuthenticator | ||||||
| 	login.Config | 	auth_model.Config | ||||||
| } | } | ||||||
|  |  | ||||||
| var _ (sourceInterface) = &db.Source{} | var _ (sourceInterface) = &db.Source{} | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ | |||||||
| package db | package db | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"code.gitea.io/gitea/models/login" | 	"code.gitea.io/gitea/models/auth" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -29,6 +29,6 @@ func (source *Source) Authenticate(user *user_model.User, login, password string | |||||||
| } | } | ||||||
|  |  | ||||||
| func init() { | func init() { | ||||||
| 	login.RegisterTypeConfig(login.NoType, &Source{}) | 	auth.RegisterTypeConfig(auth.NoType, &Source{}) | ||||||
| 	login.RegisterTypeConfig(login.Plain, &Source{}) | 	auth.RegisterTypeConfig(auth.Plain, &Source{}) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ | |||||||
| package ldap_test | package ldap_test | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"code.gitea.io/gitea/models/login" | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/services/auth" | 	"code.gitea.io/gitea/services/auth" | ||||||
| 	"code.gitea.io/gitea/services/auth/source/ldap" | 	"code.gitea.io/gitea/services/auth/source/ldap" | ||||||
| ) | ) | ||||||
| @@ -17,12 +17,12 @@ type sourceInterface interface { | |||||||
| 	auth.PasswordAuthenticator | 	auth.PasswordAuthenticator | ||||||
| 	auth.SynchronizableSource | 	auth.SynchronizableSource | ||||||
| 	auth.LocalTwoFASkipper | 	auth.LocalTwoFASkipper | ||||||
| 	login.SSHKeyProvider | 	auth_model.SSHKeyProvider | ||||||
| 	login.Config | 	auth_model.Config | ||||||
| 	login.SkipVerifiable | 	auth_model.SkipVerifiable | ||||||
| 	login.HasTLSer | 	auth_model.HasTLSer | ||||||
| 	login.UseTLSer | 	auth_model.UseTLSer | ||||||
| 	login.SourceSettable | 	auth_model.SourceSettable | ||||||
| } | } | ||||||
|  |  | ||||||
| var _ (sourceInterface) = &ldap.Source{} | var _ (sourceInterface) = &ldap.Source{} | ||||||
|   | |||||||
| @@ -7,7 +7,7 @@ package ldap | |||||||
| import ( | import ( | ||||||
| 	"strings" | 	"strings" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models/login" | 	"code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/modules/json" | 	"code.gitea.io/gitea/modules/json" | ||||||
| 	"code.gitea.io/gitea/modules/secret" | 	"code.gitea.io/gitea/modules/secret" | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
| @@ -55,8 +55,8 @@ type Source struct { | |||||||
| 	UserUID               string // User Attribute listed in Group | 	UserUID               string // User Attribute listed in Group | ||||||
| 	SkipLocalTwoFA        bool   `json:",omitempty"` // Skip Local 2fa for users authenticated with this source | 	SkipLocalTwoFA        bool   `json:",omitempty"` // Skip Local 2fa for users authenticated with this source | ||||||
|  |  | ||||||
| 	// reference to the loginSource | 	// reference to the authSource | ||||||
| 	loginSource *login.Source | 	authSource *auth.Source | ||||||
| } | } | ||||||
|  |  | ||||||
| // FromDB fills up a LDAPConfig from serialized format. | // FromDB fills up a LDAPConfig from serialized format. | ||||||
| @@ -109,12 +109,12 @@ func (source *Source) ProvidesSSHKeys() bool { | |||||||
| 	return len(strings.TrimSpace(source.AttributeSSHPublicKey)) > 0 | 	return len(strings.TrimSpace(source.AttributeSSHPublicKey)) > 0 | ||||||
| } | } | ||||||
|  |  | ||||||
| // SetLoginSource sets the related LoginSource | // SetAuthSource sets the related AuthSource | ||||||
| func (source *Source) SetLoginSource(loginSource *login.Source) { | func (source *Source) SetAuthSource(authSource *auth.Source) { | ||||||
| 	source.loginSource = loginSource | 	source.authSource = authSource | ||||||
| } | } | ||||||
|  |  | ||||||
| func init() { | func init() { | ||||||
| 	login.RegisterTypeConfig(login.LDAP, &Source{}) | 	auth.RegisterTypeConfig(auth.LDAP, &Source{}) | ||||||
| 	login.RegisterTypeConfig(login.DLDAP, &Source{}) | 	auth.RegisterTypeConfig(auth.DLDAP, &Source{}) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -9,8 +9,8 @@ import ( | |||||||
| 	"strings" | 	"strings" | ||||||
|  |  | ||||||
| 	asymkey_model "code.gitea.io/gitea/models/asymkey" | 	asymkey_model "code.gitea.io/gitea/models/asymkey" | ||||||
|  | 	"code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	"code.gitea.io/gitea/models/login" |  | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	"code.gitea.io/gitea/services/mailer" | 	"code.gitea.io/gitea/services/mailer" | ||||||
| 	user_service "code.gitea.io/gitea/services/user" | 	user_service "code.gitea.io/gitea/services/user" | ||||||
| @@ -19,7 +19,7 @@ import ( | |||||||
| // Authenticate queries if login/password is valid against the LDAP directory pool, | // Authenticate queries if login/password is valid against the LDAP directory pool, | ||||||
| // and create a local user if success when enabled. | // and create a local user if success when enabled. | ||||||
| func (source *Source) Authenticate(user *user_model.User, userName, password string) (*user_model.User, error) { | func (source *Source) Authenticate(user *user_model.User, userName, password string) (*user_model.User, error) { | ||||||
| 	sr := source.SearchEntry(userName, password, source.loginSource.Type == login.DLDAP) | 	sr := source.SearchEntry(userName, password, source.authSource.Type == auth.DLDAP) | ||||||
| 	if sr == nil { | 	if sr == nil { | ||||||
| 		// User not in LDAP, do nothing | 		// User not in LDAP, do nothing | ||||||
| 		return nil, user_model.ErrUserNotExist{Name: userName} | 		return nil, user_model.ErrUserNotExist{Name: userName} | ||||||
| @@ -59,7 +59,7 @@ func (source *Source) Authenticate(user *user_model.User, userName, password str | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if user != nil { | 	if user != nil { | ||||||
| 		if isAttributeSSHPublicKeySet && asymkey_model.SynchronizePublicKeys(user, source.loginSource, sr.SSHPublicKey) { | 		if isAttributeSSHPublicKeySet && asymkey_model.SynchronizePublicKeys(user, source.authSource, sr.SSHPublicKey) { | ||||||
| 			return user, asymkey_model.RewriteAllPublicKeys() | 			return user, asymkey_model.RewriteAllPublicKeys() | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| @@ -80,8 +80,8 @@ func (source *Source) Authenticate(user *user_model.User, userName, password str | |||||||
| 		Name:         sr.Username, | 		Name:         sr.Username, | ||||||
| 		FullName:     composeFullName(sr.Name, sr.Surname, sr.Username), | 		FullName:     composeFullName(sr.Name, sr.Surname, sr.Username), | ||||||
| 		Email:        sr.Mail, | 		Email:        sr.Mail, | ||||||
| 		LoginType:    source.loginSource.Type, | 		LoginType:    source.authSource.Type, | ||||||
| 		LoginSource:  source.loginSource.ID, | 		LoginSource:  source.authSource.ID, | ||||||
| 		LoginName:    userName, | 		LoginName:    userName, | ||||||
| 		IsActive:     true, | 		IsActive:     true, | ||||||
| 		IsAdmin:      sr.IsAdmin, | 		IsAdmin:      sr.IsAdmin, | ||||||
| @@ -95,7 +95,7 @@ func (source *Source) Authenticate(user *user_model.User, userName, password str | |||||||
|  |  | ||||||
| 	mailer.SendRegisterNotifyMail(user) | 	mailer.SendRegisterNotifyMail(user) | ||||||
|  |  | ||||||
| 	if isAttributeSSHPublicKeySet && asymkey_model.AddPublicKeysBySource(user, source.loginSource, sr.SSHPublicKey) { | 	if isAttributeSSHPublicKeySet && asymkey_model.AddPublicKeysBySource(user, source.authSource, sr.SSHPublicKey) { | ||||||
| 		err = asymkey_model.RewriteAllPublicKeys() | 		err = asymkey_model.RewriteAllPublicKeys() | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -19,22 +19,22 @@ import ( | |||||||
|  |  | ||||||
| // Sync causes this ldap source to synchronize its users with the db | // Sync causes this ldap source to synchronize its users with the db | ||||||
| func (source *Source) Sync(ctx context.Context, updateExisting bool) error { | func (source *Source) Sync(ctx context.Context, updateExisting bool) error { | ||||||
| 	log.Trace("Doing: SyncExternalUsers[%s]", source.loginSource.Name) | 	log.Trace("Doing: SyncExternalUsers[%s]", source.authSource.Name) | ||||||
|  |  | ||||||
| 	var existingUsers []int | 	var existingUsers []int | ||||||
| 	isAttributeSSHPublicKeySet := len(strings.TrimSpace(source.AttributeSSHPublicKey)) > 0 | 	isAttributeSSHPublicKeySet := len(strings.TrimSpace(source.AttributeSSHPublicKey)) > 0 | ||||||
| 	var sshKeysNeedUpdate bool | 	var sshKeysNeedUpdate bool | ||||||
|  |  | ||||||
| 	// Find all users with this login type - FIXME: Should this be an iterator? | 	// Find all users with this login type - FIXME: Should this be an iterator? | ||||||
| 	users, err := user_model.GetUsersBySource(source.loginSource) | 	users, err := user_model.GetUsersBySource(source.authSource) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Error("SyncExternalUsers: %v", err) | 		log.Error("SyncExternalUsers: %v", err) | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	select { | 	select { | ||||||
| 	case <-ctx.Done(): | 	case <-ctx.Done(): | ||||||
| 		log.Warn("SyncExternalUsers: Cancelled before update of %s", source.loginSource.Name) | 		log.Warn("SyncExternalUsers: Cancelled before update of %s", source.authSource.Name) | ||||||
| 		return db.ErrCancelledf("Before update of %s", source.loginSource.Name) | 		return db.ErrCancelledf("Before update of %s", source.authSource.Name) | ||||||
| 	default: | 	default: | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -44,7 +44,7 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error { | |||||||
|  |  | ||||||
| 	sr, err := source.SearchEntries() | 	sr, err := source.SearchEntries() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Error("SyncExternalUsers LDAP source failure [%s], skipped", source.loginSource.Name) | 		log.Error("SyncExternalUsers LDAP source failure [%s], skipped", source.authSource.Name) | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -65,7 +65,7 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error { | |||||||
| 	for _, su := range sr { | 	for _, su := range sr { | ||||||
| 		select { | 		select { | ||||||
| 		case <-ctx.Done(): | 		case <-ctx.Done(): | ||||||
| 			log.Warn("SyncExternalUsers: Cancelled at update of %s before completed update of users", source.loginSource.Name) | 			log.Warn("SyncExternalUsers: Cancelled at update of %s before completed update of users", source.authSource.Name) | ||||||
| 			// Rewrite authorized_keys file if LDAP Public SSH Key attribute is set and any key was added or removed | 			// Rewrite authorized_keys file if LDAP Public SSH Key attribute is set and any key was added or removed | ||||||
| 			if sshKeysNeedUpdate { | 			if sshKeysNeedUpdate { | ||||||
| 				err = asymkey_model.RewriteAllPublicKeys() | 				err = asymkey_model.RewriteAllPublicKeys() | ||||||
| @@ -73,7 +73,7 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error { | |||||||
| 					log.Error("RewriteAllPublicKeys: %v", err) | 					log.Error("RewriteAllPublicKeys: %v", err) | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 			return db.ErrCancelledf("During update of %s before completed update of users", source.loginSource.Name) | 			return db.ErrCancelledf("During update of %s before completed update of users", source.authSource.Name) | ||||||
| 		default: | 		default: | ||||||
| 		} | 		} | ||||||
| 		if len(su.Username) == 0 { | 		if len(su.Username) == 0 { | ||||||
| @@ -96,14 +96,14 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error { | |||||||
| 		fullName := composeFullName(su.Name, su.Surname, su.Username) | 		fullName := composeFullName(su.Name, su.Surname, su.Username) | ||||||
| 		// If no existing user found, create one | 		// If no existing user found, create one | ||||||
| 		if usr == nil { | 		if usr == nil { | ||||||
| 			log.Trace("SyncExternalUsers[%s]: Creating user %s", source.loginSource.Name, su.Username) | 			log.Trace("SyncExternalUsers[%s]: Creating user %s", source.authSource.Name, su.Username) | ||||||
|  |  | ||||||
| 			usr = &user_model.User{ | 			usr = &user_model.User{ | ||||||
| 				LowerName:    su.LowerName, | 				LowerName:    su.LowerName, | ||||||
| 				Name:         su.Username, | 				Name:         su.Username, | ||||||
| 				FullName:     fullName, | 				FullName:     fullName, | ||||||
| 				LoginType:    source.loginSource.Type, | 				LoginType:    source.authSource.Type, | ||||||
| 				LoginSource:  source.loginSource.ID, | 				LoginSource:  source.authSource.ID, | ||||||
| 				LoginName:    su.Username, | 				LoginName:    su.Username, | ||||||
| 				Email:        su.Mail, | 				Email:        su.Mail, | ||||||
| 				IsAdmin:      su.IsAdmin, | 				IsAdmin:      su.IsAdmin, | ||||||
| @@ -114,12 +114,12 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error { | |||||||
| 			err = user_model.CreateUser(usr) | 			err = user_model.CreateUser(usr) | ||||||
|  |  | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				log.Error("SyncExternalUsers[%s]: Error creating user %s: %v", source.loginSource.Name, su.Username, err) | 				log.Error("SyncExternalUsers[%s]: Error creating user %s: %v", source.authSource.Name, su.Username, err) | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			if err == nil && isAttributeSSHPublicKeySet { | 			if err == nil && isAttributeSSHPublicKeySet { | ||||||
| 				log.Trace("SyncExternalUsers[%s]: Adding LDAP Public SSH Keys for user %s", source.loginSource.Name, usr.Name) | 				log.Trace("SyncExternalUsers[%s]: Adding LDAP Public SSH Keys for user %s", source.authSource.Name, usr.Name) | ||||||
| 				if asymkey_model.AddPublicKeysBySource(usr, source.loginSource, su.SSHPublicKey) { | 				if asymkey_model.AddPublicKeysBySource(usr, source.authSource, su.SSHPublicKey) { | ||||||
| 					sshKeysNeedUpdate = true | 					sshKeysNeedUpdate = true | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| @@ -129,7 +129,7 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error { | |||||||
| 			} | 			} | ||||||
| 		} else if updateExisting { | 		} else if updateExisting { | ||||||
| 			// Synchronize SSH Public Key if that attribute is set | 			// Synchronize SSH Public Key if that attribute is set | ||||||
| 			if isAttributeSSHPublicKeySet && asymkey_model.SynchronizePublicKeys(usr, source.loginSource, su.SSHPublicKey) { | 			if isAttributeSSHPublicKeySet && asymkey_model.SynchronizePublicKeys(usr, source.authSource, su.SSHPublicKey) { | ||||||
| 				sshKeysNeedUpdate = true | 				sshKeysNeedUpdate = true | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| @@ -140,7 +140,7 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error { | |||||||
| 				usr.FullName != fullName || | 				usr.FullName != fullName || | ||||||
| 				!usr.IsActive { | 				!usr.IsActive { | ||||||
|  |  | ||||||
| 				log.Trace("SyncExternalUsers[%s]: Updating user %s", source.loginSource.Name, usr.Name) | 				log.Trace("SyncExternalUsers[%s]: Updating user %s", source.authSource.Name, usr.Name) | ||||||
|  |  | ||||||
| 				usr.FullName = fullName | 				usr.FullName = fullName | ||||||
| 				usr.Email = su.Mail | 				usr.Email = su.Mail | ||||||
| @@ -156,7 +156,7 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error { | |||||||
|  |  | ||||||
| 				err = user_model.UpdateUserCols(db.DefaultContext, usr, "full_name", "email", "is_admin", "is_restricted", "is_active") | 				err = user_model.UpdateUserCols(db.DefaultContext, usr, "full_name", "email", "is_admin", "is_restricted", "is_active") | ||||||
| 				if err != nil { | 				if err != nil { | ||||||
| 					log.Error("SyncExternalUsers[%s]: Error updating user %s: %v", source.loginSource.Name, usr.Name, err) | 					log.Error("SyncExternalUsers[%s]: Error updating user %s: %v", source.authSource.Name, usr.Name, err) | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| @@ -179,8 +179,8 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error { | |||||||
|  |  | ||||||
| 	select { | 	select { | ||||||
| 	case <-ctx.Done(): | 	case <-ctx.Done(): | ||||||
| 		log.Warn("SyncExternalUsers: Cancelled during update of %s before delete users", source.loginSource.Name) | 		log.Warn("SyncExternalUsers: Cancelled during update of %s before delete users", source.authSource.Name) | ||||||
| 		return db.ErrCancelledf("During update of %s before delete users", source.loginSource.Name) | 		return db.ErrCancelledf("During update of %s before delete users", source.authSource.Name) | ||||||
| 	default: | 	default: | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -192,12 +192,12 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error { | |||||||
| 				existPos++ | 				existPos++ | ||||||
| 			} | 			} | ||||||
| 			if usr.IsActive && (existPos >= len(existingUsers) || i < existingUsers[existPos]) { | 			if usr.IsActive && (existPos >= len(existingUsers) || i < existingUsers[existPos]) { | ||||||
| 				log.Trace("SyncExternalUsers[%s]: Deactivating user %s", source.loginSource.Name, usr.Name) | 				log.Trace("SyncExternalUsers[%s]: Deactivating user %s", source.authSource.Name, usr.Name) | ||||||
|  |  | ||||||
| 				usr.IsActive = false | 				usr.IsActive = false | ||||||
| 				err = user_model.UpdateUserCols(db.DefaultContext, usr, "is_active") | 				err = user_model.UpdateUserCols(db.DefaultContext, usr, "is_active") | ||||||
| 				if err != nil { | 				if err != nil { | ||||||
| 					log.Error("SyncExternalUsers[%s]: Error deactivating user %s: %v", source.loginSource.Name, usr.Name, err) | 					log.Error("SyncExternalUsers[%s]: Error deactivating user %s: %v", source.authSource.Name, usr.Name, err) | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ | |||||||
| package oauth2_test | package oauth2_test | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"code.gitea.io/gitea/models/login" | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/services/auth" | 	"code.gitea.io/gitea/services/auth" | ||||||
| 	"code.gitea.io/gitea/services/auth/source/oauth2" | 	"code.gitea.io/gitea/services/auth/source/oauth2" | ||||||
| ) | ) | ||||||
| @@ -14,9 +14,9 @@ import ( | |||||||
| // It tightly binds the interfaces and implementation without breaking go import cycles | // It tightly binds the interfaces and implementation without breaking go import cycles | ||||||
|  |  | ||||||
| type sourceInterface interface { | type sourceInterface interface { | ||||||
| 	login.Config | 	auth_model.Config | ||||||
| 	login.SourceSettable | 	auth_model.SourceSettable | ||||||
| 	login.RegisterableSource | 	auth_model.RegisterableSource | ||||||
| 	auth.PasswordAuthenticator | 	auth.PasswordAuthenticator | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -9,7 +9,7 @@ import ( | |||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"sync" | 	"sync" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models/login" | 	"code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
|  |  | ||||||
| @@ -52,19 +52,19 @@ func Init() error { | |||||||
| 	// Unlock our mutex | 	// Unlock our mutex | ||||||
| 	gothRWMutex.Unlock() | 	gothRWMutex.Unlock() | ||||||
|  |  | ||||||
| 	return initOAuth2LoginSources() | 	return initOAuth2Sources() | ||||||
| } | } | ||||||
|  |  | ||||||
| // ResetOAuth2 clears existing OAuth2 providers and loads them from DB | // ResetOAuth2 clears existing OAuth2 providers and loads them from DB | ||||||
| func ResetOAuth2() error { | func ResetOAuth2() error { | ||||||
| 	ClearProviders() | 	ClearProviders() | ||||||
| 	return initOAuth2LoginSources() | 	return initOAuth2Sources() | ||||||
| } | } | ||||||
|  |  | ||||||
| // initOAuth2LoginSources is used to load and register all active OAuth2 providers | // initOAuth2Sources is used to load and register all active OAuth2 providers | ||||||
| func initOAuth2LoginSources() error { | func initOAuth2Sources() error { | ||||||
| 	loginSources, _ := login.GetActiveOAuth2ProviderLoginSources() | 	authSources, _ := auth.GetActiveOAuth2ProviderSources() | ||||||
| 	for _, source := range loginSources { | 	for _, source := range authSources { | ||||||
| 		oauth2Source, ok := source.Cfg.(*Source) | 		oauth2Source, ok := source.Cfg.(*Source) | ||||||
| 		if !ok { | 		if !ok { | ||||||
| 			continue | 			continue | ||||||
|   | |||||||
| @@ -9,7 +9,7 @@ import ( | |||||||
| 	"net/url" | 	"net/url" | ||||||
| 	"sort" | 	"sort" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models/login" | 	"code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
|  |  | ||||||
| @@ -55,7 +55,7 @@ func NewImagedProvider(image string, provider GothProvider) *ImagedProvider { | |||||||
| } | } | ||||||
|  |  | ||||||
| // Providers contains the map of registered OAuth2 providers in Gitea (based on goth) | // Providers contains the map of registered OAuth2 providers in Gitea (based on goth) | ||||||
| // key is used to map the OAuth2Provider with the goth provider type (also in LoginSource.OAuth2Config.Provider) | // key is used to map the OAuth2Provider with the goth provider type (also in AuthSource.OAuth2Config.Provider) | ||||||
| // value is used to store display data | // value is used to store display data | ||||||
| var gothProviders = map[string]GothProvider{} | var gothProviders = map[string]GothProvider{} | ||||||
|  |  | ||||||
| @@ -88,14 +88,14 @@ func GetOAuth2Providers() []Provider { | |||||||
| func GetActiveOAuth2Providers() ([]string, map[string]Provider, error) { | func GetActiveOAuth2Providers() ([]string, map[string]Provider, error) { | ||||||
| 	// Maybe also separate used and unused providers so we can force the registration of only 1 active provider for each type | 	// Maybe also separate used and unused providers so we can force the registration of only 1 active provider for each type | ||||||
|  |  | ||||||
| 	loginSources, err := login.GetActiveOAuth2ProviderLoginSources() | 	authSources, err := auth.GetActiveOAuth2ProviderSources() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, nil, err | 		return nil, nil, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	var orderedKeys []string | 	var orderedKeys []string | ||||||
| 	providers := make(map[string]Provider) | 	providers := make(map[string]Provider) | ||||||
| 	for _, source := range loginSources { | 	for _, source := range authSources { | ||||||
| 		prov := gothProviders[source.Cfg.(*Source).Provider] | 		prov := gothProviders[source.Cfg.(*Source).Provider] | ||||||
| 		if source.Cfg.(*Source).IconURL != "" { | 		if source.Cfg.(*Source).IconURL != "" { | ||||||
| 			prov = &ImagedProvider{prov, source.Cfg.(*Source).IconURL} | 			prov = &ImagedProvider{prov, source.Cfg.(*Source).IconURL} | ||||||
| @@ -140,8 +140,8 @@ func ClearProviders() { | |||||||
| } | } | ||||||
|  |  | ||||||
| var ( | var ( | ||||||
| 	// ErrLoginSourceNotActived login source is not actived error | 	// ErrAuthSourceNotActived login source is not actived error | ||||||
| 	ErrLoginSourceNotActived = errors.New("Login source is not actived") | 	ErrAuthSourceNotActived = errors.New("auth source is not actived") | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // used to create different types of goth providers | // used to create different types of goth providers | ||||||
| @@ -153,7 +153,7 @@ func createProvider(providerName string, source *Source) (goth.Provider, error) | |||||||
|  |  | ||||||
| 	p, ok := gothProviders[source.Provider] | 	p, ok := gothProviders[source.Provider] | ||||||
| 	if !ok { | 	if !ok { | ||||||
| 		return nil, ErrLoginSourceNotActived | 		return nil, ErrAuthSourceNotActived | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	provider, err = p.CreateGothProvider(providerName, callbackURL, source) | 	provider, err = p.CreateGothProvider(providerName, callbackURL, source) | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ | |||||||
| package oauth2 | package oauth2 | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"code.gitea.io/gitea/models/login" | 	"code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/modules/json" | 	"code.gitea.io/gitea/modules/json" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -33,8 +33,8 @@ type Source struct { | |||||||
| 	RestrictedGroup    string | 	RestrictedGroup    string | ||||||
| 	SkipLocalTwoFA     bool `json:",omitempty"` | 	SkipLocalTwoFA     bool `json:",omitempty"` | ||||||
|  |  | ||||||
| 	// reference to the loginSource | 	// reference to the authSource | ||||||
| 	loginSource *login.Source | 	authSource *auth.Source | ||||||
| } | } | ||||||
|  |  | ||||||
| // FromDB fills up an OAuth2Config from serialized format. | // FromDB fills up an OAuth2Config from serialized format. | ||||||
| @@ -47,11 +47,11 @@ func (source *Source) ToDB() ([]byte, error) { | |||||||
| 	return json.Marshal(source) | 	return json.Marshal(source) | ||||||
| } | } | ||||||
|  |  | ||||||
| // SetLoginSource sets the related LoginSource | // SetAuthSource sets the related AuthSource | ||||||
| func (source *Source) SetLoginSource(loginSource *login.Source) { | func (source *Source) SetAuthSource(authSource *auth.Source) { | ||||||
| 	source.loginSource = loginSource | 	source.authSource = authSource | ||||||
| } | } | ||||||
|  |  | ||||||
| func init() { | func init() { | ||||||
| 	login.RegisterTypeConfig(login.OAuth2, &Source{}) | 	auth.RegisterTypeConfig(auth.OAuth2, &Source{}) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -14,7 +14,7 @@ import ( | |||||||
| // Callout redirects request/response pair to authenticate against the provider | // Callout redirects request/response pair to authenticate against the provider | ||||||
| func (source *Source) Callout(request *http.Request, response http.ResponseWriter) error { | func (source *Source) Callout(request *http.Request, response http.ResponseWriter) error { | ||||||
| 	// not sure if goth is thread safe (?) when using multiple providers | 	// not sure if goth is thread safe (?) when using multiple providers | ||||||
| 	request.Header.Set(ProviderHeaderKey, source.loginSource.Name) | 	request.Header.Set(ProviderHeaderKey, source.authSource.Name) | ||||||
|  |  | ||||||
| 	// don't use the default gothic begin handler to prevent issues when some error occurs | 	// don't use the default gothic begin handler to prevent issues when some error occurs | ||||||
| 	// normally the gothic library will write some custom stuff to the response instead of our own nice error page | 	// normally the gothic library will write some custom stuff to the response instead of our own nice error page | ||||||
| @@ -34,7 +34,7 @@ func (source *Source) Callout(request *http.Request, response http.ResponseWrite | |||||||
| // this will trigger a new authentication request, but because we save it in the session we can use that | // this will trigger a new authentication request, but because we save it in the session we can use that | ||||||
| func (source *Source) Callback(request *http.Request, response http.ResponseWriter) (goth.User, error) { | func (source *Source) Callback(request *http.Request, response http.ResponseWriter) (goth.User, error) { | ||||||
| 	// not sure if goth is thread safe (?) when using multiple providers | 	// not sure if goth is thread safe (?) when using multiple providers | ||||||
| 	request.Header.Set(ProviderHeaderKey, source.loginSource.Name) | 	request.Header.Set(ProviderHeaderKey, source.authSource.Name) | ||||||
|  |  | ||||||
| 	gothRWMutex.RLock() | 	gothRWMutex.RLock() | ||||||
| 	defer gothRWMutex.RUnlock() | 	defer gothRWMutex.RUnlock() | ||||||
|   | |||||||
| @@ -10,13 +10,13 @@ import ( | |||||||
|  |  | ||||||
| // RegisterSource causes an OAuth2 configuration to be registered | // RegisterSource causes an OAuth2 configuration to be registered | ||||||
| func (source *Source) RegisterSource() error { | func (source *Source) RegisterSource() error { | ||||||
| 	err := RegisterProviderWithGothic(source.loginSource.Name, source) | 	err := RegisterProviderWithGothic(source.authSource.Name, source) | ||||||
| 	return wrapOpenIDConnectInitializeError(err, source.loginSource.Name, source) | 	return wrapOpenIDConnectInitializeError(err, source.authSource.Name, source) | ||||||
| } | } | ||||||
|  |  | ||||||
| // UnregisterSource causes an OAuth2 configuration to be unregistered | // UnregisterSource causes an OAuth2 configuration to be unregistered | ||||||
| func (source *Source) UnregisterSource() error { | func (source *Source) UnregisterSource() error { | ||||||
| 	RemoveProviderFromGothic(source.loginSource.Name) | 	RemoveProviderFromGothic(source.authSource.Name) | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ | |||||||
| package pam_test | package pam_test | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"code.gitea.io/gitea/models/login" | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/services/auth" | 	"code.gitea.io/gitea/services/auth" | ||||||
| 	"code.gitea.io/gitea/services/auth/source/pam" | 	"code.gitea.io/gitea/services/auth/source/pam" | ||||||
| ) | ) | ||||||
| @@ -15,8 +15,8 @@ import ( | |||||||
|  |  | ||||||
| type sourceInterface interface { | type sourceInterface interface { | ||||||
| 	auth.PasswordAuthenticator | 	auth.PasswordAuthenticator | ||||||
| 	login.Config | 	auth_model.Config | ||||||
| 	login.SourceSettable | 	auth_model.SourceSettable | ||||||
| } | } | ||||||
|  |  | ||||||
| var _ (sourceInterface) = &pam.Source{} | var _ (sourceInterface) = &pam.Source{} | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ | |||||||
| package pam | package pam | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"code.gitea.io/gitea/models/login" | 	"code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/modules/json" | 	"code.gitea.io/gitea/modules/json" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -22,8 +22,8 @@ type Source struct { | |||||||
| 	EmailDomain    string | 	EmailDomain    string | ||||||
| 	SkipLocalTwoFA bool `json:",omitempty"` // Skip Local 2fa for users authenticated with this source | 	SkipLocalTwoFA bool `json:",omitempty"` // Skip Local 2fa for users authenticated with this source | ||||||
|  |  | ||||||
| 	// reference to the loginSource | 	// reference to the authSource | ||||||
| 	loginSource *login.Source | 	authSource *auth.Source | ||||||
| } | } | ||||||
|  |  | ||||||
| // FromDB fills up a PAMConfig from serialized format. | // FromDB fills up a PAMConfig from serialized format. | ||||||
| @@ -36,11 +36,11 @@ func (source *Source) ToDB() ([]byte, error) { | |||||||
| 	return json.Marshal(source) | 	return json.Marshal(source) | ||||||
| } | } | ||||||
|  |  | ||||||
| // SetLoginSource sets the related LoginSource | // SetAuthSource sets the related AuthSource | ||||||
| func (source *Source) SetLoginSource(loginSource *login.Source) { | func (source *Source) SetAuthSource(authSource *auth.Source) { | ||||||
| 	source.loginSource = loginSource | 	source.authSource = authSource | ||||||
| } | } | ||||||
|  |  | ||||||
| func init() { | func init() { | ||||||
| 	login.RegisterTypeConfig(login.PAM, &Source{}) | 	auth.RegisterTypeConfig(auth.PAM, &Source{}) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -8,7 +8,7 @@ import ( | |||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"strings" | 	"strings" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models/login" | 	"code.gitea.io/gitea/models/auth" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	"code.gitea.io/gitea/modules/auth/pam" | 	"code.gitea.io/gitea/modules/auth/pam" | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
| @@ -55,8 +55,8 @@ func (source *Source) Authenticate(user *user_model.User, userName, password str | |||||||
| 		Name:        username, | 		Name:        username, | ||||||
| 		Email:       email, | 		Email:       email, | ||||||
| 		Passwd:      password, | 		Passwd:      password, | ||||||
| 		LoginType:   login.PAM, | 		LoginType:   auth.PAM, | ||||||
| 		LoginSource: source.loginSource.ID, | 		LoginSource: source.authSource.ID, | ||||||
| 		LoginName:   userName, // This is what the user typed in | 		LoginName:   userName, // This is what the user typed in | ||||||
| 		IsActive:    true, | 		IsActive:    true, | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ | |||||||
| package smtp_test | package smtp_test | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"code.gitea.io/gitea/models/login" | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/services/auth" | 	"code.gitea.io/gitea/services/auth" | ||||||
| 	"code.gitea.io/gitea/services/auth/source/smtp" | 	"code.gitea.io/gitea/services/auth/source/smtp" | ||||||
| ) | ) | ||||||
| @@ -15,11 +15,11 @@ import ( | |||||||
|  |  | ||||||
| type sourceInterface interface { | type sourceInterface interface { | ||||||
| 	auth.PasswordAuthenticator | 	auth.PasswordAuthenticator | ||||||
| 	login.Config | 	auth_model.Config | ||||||
| 	login.SkipVerifiable | 	auth_model.SkipVerifiable | ||||||
| 	login.HasTLSer | 	auth_model.HasTLSer | ||||||
| 	login.UseTLSer | 	auth_model.UseTLSer | ||||||
| 	login.SourceSettable | 	auth_model.SourceSettable | ||||||
| } | } | ||||||
|  |  | ||||||
| var _ (sourceInterface) = &smtp.Source{} | var _ (sourceInterface) = &smtp.Source{} | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ | |||||||
| package smtp | package smtp | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"code.gitea.io/gitea/models/login" | 	"code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/modules/json" | 	"code.gitea.io/gitea/modules/json" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -28,8 +28,8 @@ type Source struct { | |||||||
| 	DisableHelo    bool | 	DisableHelo    bool | ||||||
| 	SkipLocalTwoFA bool `json:",omitempty"` | 	SkipLocalTwoFA bool `json:",omitempty"` | ||||||
|  |  | ||||||
| 	// reference to the loginSource | 	// reference to the authSource | ||||||
| 	loginSource *login.Source | 	authSource *auth.Source | ||||||
| } | } | ||||||
|  |  | ||||||
| // FromDB fills up an SMTPConfig from serialized format. | // FromDB fills up an SMTPConfig from serialized format. | ||||||
| @@ -57,11 +57,11 @@ func (source *Source) UseTLS() bool { | |||||||
| 	return source.ForceSMTPS || source.Port == 465 | 	return source.ForceSMTPS || source.Port == 465 | ||||||
| } | } | ||||||
|  |  | ||||||
| // SetLoginSource sets the related LoginSource | // SetAuthSource sets the related AuthSource | ||||||
| func (source *Source) SetLoginSource(loginSource *login.Source) { | func (source *Source) SetAuthSource(authSource *auth.Source) { | ||||||
| 	source.loginSource = loginSource | 	source.authSource = authSource | ||||||
| } | } | ||||||
|  |  | ||||||
| func init() { | func init() { | ||||||
| 	login.RegisterTypeConfig(login.SMTP, &Source{}) | 	auth.RegisterTypeConfig(auth.SMTP, &Source{}) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -10,7 +10,7 @@ import ( | |||||||
| 	"net/textproto" | 	"net/textproto" | ||||||
| 	"strings" | 	"strings" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models/login" | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	"code.gitea.io/gitea/modules/util" | 	"code.gitea.io/gitea/modules/util" | ||||||
| 	"code.gitea.io/gitea/services/mailer" | 	"code.gitea.io/gitea/services/mailer" | ||||||
| @@ -71,8 +71,8 @@ func (source *Source) Authenticate(user *user_model.User, userName, password str | |||||||
| 		Name:        strings.ToLower(username), | 		Name:        strings.ToLower(username), | ||||||
| 		Email:       userName, | 		Email:       userName, | ||||||
| 		Passwd:      password, | 		Passwd:      password, | ||||||
| 		LoginType:   login.SMTP, | 		LoginType:   auth_model.SMTP, | ||||||
| 		LoginSource: source.loginSource.ID, | 		LoginSource: source.authSource.ID, | ||||||
| 		LoginName:   userName, | 		LoginName:   userName, | ||||||
| 		IsActive:    true, | 		IsActive:    true, | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ | |||||||
| package sspi_test | package sspi_test | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"code.gitea.io/gitea/models/login" | 	"code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/services/auth/source/sspi" | 	"code.gitea.io/gitea/services/auth/source/sspi" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -13,7 +13,7 @@ import ( | |||||||
| // It tightly binds the interfaces and implementation without breaking go import cycles | // It tightly binds the interfaces and implementation without breaking go import cycles | ||||||
|  |  | ||||||
| type sourceInterface interface { | type sourceInterface interface { | ||||||
| 	login.Config | 	auth.Config | ||||||
| } | } | ||||||
|  |  | ||||||
| var _ (sourceInterface) = &sspi.Source{} | var _ (sourceInterface) = &sspi.Source{} | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ | |||||||
| package sspi | package sspi | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"code.gitea.io/gitea/models/login" | 	"code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/modules/json" | 	"code.gitea.io/gitea/modules/json" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -36,5 +36,5 @@ func (cfg *Source) ToDB() ([]byte, error) { | |||||||
| } | } | ||||||
|  |  | ||||||
| func init() { | func init() { | ||||||
| 	login.RegisterTypeConfig(login.SSPI, &Source{}) | 	auth.RegisterTypeConfig(auth.SSPI, &Source{}) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -9,8 +9,8 @@ import ( | |||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"strings" | 	"strings" | ||||||
|  |  | ||||||
|  | 	"code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/avatars" | 	"code.gitea.io/gitea/models/avatars" | ||||||
| 	"code.gitea.io/gitea/models/login" |  | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	"code.gitea.io/gitea/modules/base" | 	"code.gitea.io/gitea/modules/base" | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| @@ -154,7 +154,7 @@ func (s *SSPI) Verify(req *http.Request, w http.ResponseWriter, store DataStore, | |||||||
|  |  | ||||||
| // getConfig retrieves the SSPI configuration from login sources | // getConfig retrieves the SSPI configuration from login sources | ||||||
| func (s *SSPI) getConfig() (*sspi.Source, error) { | func (s *SSPI) getConfig() (*sspi.Source, error) { | ||||||
| 	sources, err := login.ActiveSources(login.SSPI) | 	sources, err := auth.ActiveSources(auth.SSPI) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| @@ -250,7 +250,7 @@ func sanitizeUsername(username string, cfg *sspi.Source) string { | |||||||
| // fails (or if negotiation should continue), which would prevent other authentication methods | // fails (or if negotiation should continue), which would prevent other authentication methods | ||||||
| // to execute at all. | // to execute at all. | ||||||
| func specialInit() { | func specialInit() { | ||||||
| 	if login.IsSSPIEnabled() { | 	if auth.IsSSPIEnabled() { | ||||||
| 		Register(&SSPI{}) | 		Register(&SSPI{}) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
| @@ -7,8 +7,8 @@ package auth | |||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
|  |  | ||||||
|  | 	"code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	"code.gitea.io/gitea/models/login" |  | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -16,7 +16,7 @@ import ( | |||||||
| func SyncExternalUsers(ctx context.Context, updateExisting bool) error { | func SyncExternalUsers(ctx context.Context, updateExisting bool) error { | ||||||
| 	log.Trace("Doing: SyncExternalUsers") | 	log.Trace("Doing: SyncExternalUsers") | ||||||
|  |  | ||||||
| 	ls, err := login.Sources() | 	ls, err := auth.Sources() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Error("SyncExternalUsers: %v", err) | 		log.Error("SyncExternalUsers: %v", err) | ||||||
| 		return err | 		return err | ||||||
|   | |||||||
| @@ -8,7 +8,7 @@ import ( | |||||||
| 	"strings" | 	"strings" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" | 	"code.gitea.io/gitea/models" | ||||||
| 	"code.gitea.io/gitea/models/login" | 	"code.gitea.io/gitea/models/auth" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	"code.gitea.io/gitea/modules/structs" | 	"code.gitea.io/gitea/modules/structs" | ||||||
|  |  | ||||||
| @@ -16,14 +16,14 @@ import ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| func toExternalLoginUser(user *user_model.User, gothUser goth.User) (*user_model.ExternalLoginUser, error) { | func toExternalLoginUser(user *user_model.User, gothUser goth.User) (*user_model.ExternalLoginUser, error) { | ||||||
| 	loginSource, err := login.GetActiveOAuth2LoginSourceByName(gothUser.Provider) | 	authSource, err := auth.GetActiveOAuth2SourceByName(gothUser.Provider) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 	return &user_model.ExternalLoginUser{ | 	return &user_model.ExternalLoginUser{ | ||||||
| 		ExternalID:        gothUser.UserID, | 		ExternalID:        gothUser.UserID, | ||||||
| 		UserID:            user.ID, | 		UserID:            user.ID, | ||||||
| 		LoginSourceID:     loginSource.ID, | 		LoginSourceID:     authSource.ID, | ||||||
| 		RawData:           gothUser.RawData, | 		RawData:           gothUser.RawData, | ||||||
| 		Provider:          gothUser.Provider, | 		Provider:          gothUser.Provider, | ||||||
| 		Email:             gothUser.Email, | 		Email:             gothUser.Email, | ||||||
|   | |||||||
| @@ -3,11 +3,11 @@ | |||||||
| 	{{template "user/settings/navbar" .}} | 	{{template "user/settings/navbar" .}} | ||||||
| 	<div class="ui container"> | 	<div class="ui container"> | ||||||
| 		{{template "base/alert" .}} | 		{{template "base/alert" .}} | ||||||
| 		{{template "user/settings/security_twofa" .}} | 		{{template "user/settings/security/twofa" .}} | ||||||
| 		{{template "user/settings/security_u2f" .}} | 		{{template "user/settings/security/u2f" .}} | ||||||
| 		{{template "user/settings/security_accountlinks" .}} | 		{{template "user/settings/security/accountlinks" .}} | ||||||
| 		{{if .EnableOpenIDSignIn}} | 		{{if .EnableOpenIDSignIn}} | ||||||
| 		{{template "user/settings/security_openid" .}} | 		{{template "user/settings/security/openid" .}} | ||||||
| 		{{end}} | 		{{end}} | ||||||
| 	</div> | 	</div> | ||||||
| </div> | </div> | ||||||
		Reference in New Issue
	
	Block a user