706 lines · 19.7 KB · Go
RawDownload
// Copyright 2025 The Forgejo Authors. All rights reserved.// SPDX-License-Identifier: GPL-3.0-or-later package cmd import (	"context"	"testing" 	"forgejo.org/models/auth"	"forgejo.org/modules/test"	"forgejo.org/services/auth/source/oauth2" 	"github.com/stretchr/testify/assert"	"github.com/stretchr/testify/require"	"github.com/urfave/cli/v3") func TestAddOauth(t *testing.T) {	// Mock cli functions to do not exit on error	defer test.MockVariableValue(&cli.OsExiter, func(code int) {})() 	// Test cases	cases := []struct {		args   []string		source *auth.Source		errMsg string	}{		// case 0		{			args: []string{				"oauth-test",				"--name", "oauth2 (via openidConnect) source full",				"--provider", "openidConnect",				"--key", "client id",				"--secret", "client secret",				"--auto-discover-url", "https://example.com/.well-known/openid-configuration",				"--use-custom-urls", "",				"--custom-tenant-id", "tenant id",				"--custom-auth-url", "https://example.com/auth",				"--custom-token-url", "https://example.com/token",				"--custom-profile-url", "https://example.com/profile",				"--custom-email-url", "https://example.com/email",				"--icon-url", "https://example.com/icon.svg",				"--skip-local-2fa",				"--scopes", "address",				"--scopes", "email",				"--scopes", "phone",				"--scopes", "profile",				"--attribute-ssh-public-key", "ssh_public_key",				"--required-claim-name", "can_access",				"--required-claim-value", "yes",				"--group-claim-name", "groups",				"--admin-group", "admin",				"--restricted-group", "restricted",				"--group-team-map", `{"org_a_team_1": {"organization-a": ["Team 1"]}, "org_a_all_teams": {"organization-a": ["Team 1", "Team 2", "Team 3"]}}`,				"--group-team-map-removal",				"--allow-username-change",			},			source: &auth.Source{				Type:     auth.OAuth2,				Name:     "oauth2 (via openidConnect) source full",				IsActive: true,				Cfg: &oauth2.Source{					Provider:                      "openidConnect",					ClientID:                      "client id",					ClientSecret:                  "client secret",					OpenIDConnectAutoDiscoveryURL: "https://example.com/.well-known/openid-configuration",					CustomURLMapping: &oauth2.CustomURLMapping{						AuthURL:    "https://example.com/auth",						TokenURL:   "https://example.com/token",						ProfileURL: "https://example.com/profile",						EmailURL:   "https://example.com/email",						Tenant:     "tenant id",					},					IconURL:               "https://example.com/icon.svg",					Scopes:                []string{"address", "email", "phone", "profile"},					AttributeSSHPublicKey: "ssh_public_key",					RequiredClaimName:     "can_access",					RequiredClaimValue:    "yes",					GroupClaimName:        "groups",					AdminGroup:            "admin",					GroupTeamMap:          `{"org_a_team_1": {"organization-a": ["Team 1"]}, "org_a_all_teams": {"organization-a": ["Team 1", "Team 2", "Team 3"]}}`,					GroupTeamMapRemoval:   true,					RestrictedGroup:       "restricted",					SkipLocalTwoFA:        true,					AllowUsernameChange:   true,				},			},		},		// case 1		{			args: []string{				"oauth-test",				"--name", "oauth2 (via openidConnect) source min",				"--provider", "openidConnect",				"--auto-discover-url", "https://example.com/.well-known/openid-configuration",			},			source: &auth.Source{				Type:     auth.OAuth2,				Name:     "oauth2 (via openidConnect) source min",				IsActive: true,				Cfg: &oauth2.Source{					Provider:                      "openidConnect",					OpenIDConnectAutoDiscoveryURL: "https://example.com/.well-known/openid-configuration",					Scopes:                        []string{},				},			},		},		// case 2		{			args: []string{				"oauth-test",				"--name", "oauth2 (via openidConnect) source `--use-custom-urls` required for `--custom-*` flags",				"--custom-tenant-id", "tenant id",				"--custom-auth-url", "https://example.com/auth",				"--custom-token-url", "https://example.com/token",				"--custom-profile-url", "https://example.com/profile",				"--custom-email-url", "https://example.com/email",			},			source: &auth.Source{				Type:     auth.OAuth2,				Name:     "oauth2 (via openidConnect) source `--use-custom-urls` required for `--custom-*` flags",				IsActive: true,				Cfg: &oauth2.Source{					Scopes: []string{},				},			},		},		// case 3		{			args: []string{				"oauth-test",				"--name", "oauth2 (via openidConnect) source `--scopes` aggregates multiple uses",				"--provider", "openidConnect",				"--auto-discover-url", "https://example.com/.well-known/openid-configuration",				"--scopes", "address",				"--scopes", "email",				"--scopes", "phone",				"--scopes", "profile",			},			source: &auth.Source{				Type:     auth.OAuth2,				Name:     "oauth2 (via openidConnect) source `--scopes` aggregates multiple uses",				IsActive: true,				Cfg: &oauth2.Source{					Provider:                      "openidConnect",					OpenIDConnectAutoDiscoveryURL: "https://example.com/.well-known/openid-configuration",					Scopes:                        []string{"address", "email", "phone", "profile"},				},			},		},		// case 4		{			args: []string{				"oauth-test",				"--name", "oauth2 (via openidConnect) source `--scopes` supports commas as separators",				"--provider", "openidConnect",				"--auto-discover-url", "https://example.com/.well-known/openid-configuration",				"--scopes", "address,email,phone,profile",			},			source: &auth.Source{				Type:     auth.OAuth2,				Name:     "oauth2 (via openidConnect) source `--scopes` supports commas as separators",				IsActive: true,				Cfg: &oauth2.Source{					Provider:                      "openidConnect",					OpenIDConnectAutoDiscoveryURL: "https://example.com/.well-known/openid-configuration",					Scopes:                        []string{"address", "email", "phone", "profile"},				},			},		},		// case 5		{			args: []string{				"oauth-test",				"--name", "oauth2 (via openidConnect) source",				"--provider", "openidConnect",			},			errMsg: "invalid Auto Discovery URL:  (this must be a valid URL starting with http:// or https://)",		},		// case 6		{			args: []string{				"oauth-test",				"--name", "oauth2 (via openidConnect) source",				"--provider", "openidConnect",				"--auto-discover-url", "example.com",			},			errMsg: "invalid Auto Discovery URL: example.com (this must be a valid URL starting with http:// or https://)",		},	} 	for n, c := range cases {		// Mock functions.		var createdAuthSource *auth.Source		service := &authService{			initDB: func(context.Context) error {				return nil			},			createAuthSource: func(ctx context.Context, authSource *auth.Source) error {				createdAuthSource = authSource				return nil			},			updateAuthSource: func(ctx context.Context, authSource *auth.Source) error {				assert.FailNow(t, "should not call updateAuthSource", "case: %d", n)				return nil			},			getAuthSourceByID: func(ctx context.Context, id int64) (*auth.Source, error) {				assert.FailNow(t, "should not call getAuthSourceByID", "case: %d", n)				return nil, nil			},		} 		// Create a copy of command to test		app := cli.Command{}		app.Flags = microcmdAuthAddOauth().Flags		app.Action = service.addOauth 		// Run it		err := app.Run(t.Context(), c.args)		if c.errMsg != "" {			assert.EqualError(t, err, c.errMsg, "case %d: error should match", n)		} else {			require.NoError(t, err, "case %d: should have no errors", n)			assert.Equal(t, c.source, createdAuthSource, "case %d: wrong authSource", n)		}	}} func TestUpdateOauth(t *testing.T) {	// Mock cli functions to do not exit on error	defer test.MockVariableValue(&cli.OsExiter, func(code int) {})() 	// Test cases	cases := []struct {		args               []string		id                 int64		existingAuthSource *auth.Source		authSource         *auth.Source		errMsg             string	}{		// case 0		{			args: []string{				"oauth-test",				"--id", "23",				"--name", "oauth2 (via openidConnect) source full",				"--provider", "openidConnect",				"--key", "client id",				"--secret", "client secret",				"--auto-discover-url", "https://example.com/.well-known/openid-configuration",				"--use-custom-urls", "",				"--custom-tenant-id", "tenant id",				"--custom-auth-url", "https://example.com/auth",				"--custom-token-url", "https://example.com/token",				"--custom-profile-url", "https://example.com/profile",				"--custom-email-url", "https://example.com/email",				"--icon-url", "https://example.com/icon.svg",				"--skip-local-2fa",				"--scopes", "address",				"--scopes", "email",				"--scopes", "phone",				"--scopes", "profile",				"--attribute-ssh-public-key", "ssh_public_key",				"--required-claim-name", "can_access",				"--required-claim-value", "yes",				"--group-claim-name", "groups",				"--admin-group", "admin",				"--restricted-group", "restricted",				"--group-team-map", `{"org_a_team_1": {"organization-a": ["Team 1"]}, "org_a_all_teams": {"organization-a": ["Team 1", "Team 2", "Team 3"]}}`,				"--group-team-map-removal",			},			id: 23,			existingAuthSource: &auth.Source{				Type: auth.OAuth2,				Cfg:  &oauth2.Source{},			},			authSource: &auth.Source{				Type: auth.OAuth2,				Name: "oauth2 (via openidConnect) source full",				Cfg: &oauth2.Source{					Provider:                      "openidConnect",					ClientID:                      "client id",					ClientSecret:                  "client secret",					OpenIDConnectAutoDiscoveryURL: "https://example.com/.well-known/openid-configuration",					CustomURLMapping: &oauth2.CustomURLMapping{						AuthURL:    "https://example.com/auth",						TokenURL:   "https://example.com/token",						ProfileURL: "https://example.com/profile",						EmailURL:   "https://example.com/email",						Tenant:     "tenant id",					},					IconURL:               "https://example.com/icon.svg",					Scopes:                []string{"address", "email", "phone", "profile"},					AttributeSSHPublicKey: "ssh_public_key",					RequiredClaimName:     "can_access",					RequiredClaimValue:    "yes",					GroupClaimName:        "groups",					AdminGroup:            "admin",					GroupTeamMap:          `{"org_a_team_1": {"organization-a": ["Team 1"]}, "org_a_all_teams": {"organization-a": ["Team 1", "Team 2", "Team 3"]}}`,					GroupTeamMapRemoval:   true,					RestrictedGroup:       "restricted",					// `--skip-local-2fa` is currently ignored.					// SkipLocalTwoFA:        true,				},			},		},		// case 1		{			args: []string{				"oauth-test",				"--id", "1",			},			authSource: &auth.Source{				Type: auth.OAuth2,				Cfg: &oauth2.Source{					CustomURLMapping: &oauth2.CustomURLMapping{},				},			},		},		// case 2		{			args: []string{				"oauth-test",				"--id", "1",				"--name", "oauth2 (via openidConnect) source full",			},			authSource: &auth.Source{				Type: auth.OAuth2,				Name: "oauth2 (via openidConnect) source full",				Cfg: &oauth2.Source{					CustomURLMapping: &oauth2.CustomURLMapping{},				},			},		},		// case 3		{			args: []string{				"oauth-test",				"--id", "1",				"--provider", "openidConnect",			},			authSource: &auth.Source{				Type: auth.OAuth2,				Cfg: &oauth2.Source{					Provider:         "openidConnect",					CustomURLMapping: &oauth2.CustomURLMapping{},				},			},		},		// case 4		{			args: []string{				"oauth-test",				"--id", "1",				"--key", "client id",			},			authSource: &auth.Source{				Type: auth.OAuth2,				Cfg: &oauth2.Source{					ClientID:         "client id",					CustomURLMapping: &oauth2.CustomURLMapping{},				},			},		},		// case 5		{			args: []string{				"oauth-test",				"--id", "1",				"--secret", "client secret",			},			authSource: &auth.Source{				Type: auth.OAuth2,				Cfg: &oauth2.Source{					ClientSecret:     "client secret",					CustomURLMapping: &oauth2.CustomURLMapping{},				},			},		},		// case 6		{			args: []string{				"oauth-test",				"--id", "1",				"--auto-discover-url", "https://example.com/.well-known/openid-configuration",			},			authSource: &auth.Source{				Type: auth.OAuth2,				Cfg: &oauth2.Source{					OpenIDConnectAutoDiscoveryURL: "https://example.com/.well-known/openid-configuration",					CustomURLMapping:              &oauth2.CustomURLMapping{},				},			},		},		// case 7		{			args: []string{				"oauth-test",				"--id", "1",				"--use-custom-urls", "",				"--custom-tenant-id", "tenant id",				"--custom-auth-url", "https://example.com/auth",				"--custom-token-url", "https://example.com/token",				"--custom-profile-url", "https://example.com/profile",				"--custom-email-url", "https://example.com/email",			},			authSource: &auth.Source{				Type: auth.OAuth2,				Cfg: &oauth2.Source{					CustomURLMapping: &oauth2.CustomURLMapping{						AuthURL:    "https://example.com/auth",						TokenURL:   "https://example.com/token",						ProfileURL: "https://example.com/profile",						EmailURL:   "https://example.com/email",						Tenant:     "tenant id",					},				},			},		},		// case 8		{			args: []string{				"oauth-test",				"--id", "1",				"--name", "oauth2 (via openidConnect) source `--use-custom-urls` required for `--custom-*` flags",				"--custom-tenant-id", "tenant id",				"--custom-auth-url", "https://example.com/auth",				"--custom-token-url", "https://example.com/token",				"--custom-profile-url", "https://example.com/profile",				"--custom-email-url", "https://example.com/email",			},			authSource: &auth.Source{				Type: auth.OAuth2,				Name: "oauth2 (via openidConnect) source `--use-custom-urls` required for `--custom-*` flags",				Cfg: &oauth2.Source{					CustomURLMapping: &oauth2.CustomURLMapping{},				},			},		},		// case 9		{			args: []string{				"oauth-test",				"--id", "1",				"--icon-url", "https://example.com/icon.svg",			},			authSource: &auth.Source{				Type: auth.OAuth2,				Cfg: &oauth2.Source{					CustomURLMapping: &oauth2.CustomURLMapping{},					IconURL:          "https://example.com/icon.svg",				},			},		},		// case 10		{			args: []string{				"oauth-test",				"--id", "1",				"--name", "oauth2 (via openidConnect) source `--skip-local-2fa` is currently ignored",				"--skip-local-2fa",			},			authSource: &auth.Source{				Type: auth.OAuth2,				Name: "oauth2 (via openidConnect) source `--skip-local-2fa` is currently ignored",				Cfg: &oauth2.Source{					CustomURLMapping: &oauth2.CustomURLMapping{},					// `--skip-local-2fa` is currently ignored.					// SkipLocalTwoFA: true,				},			},		},		// case 11		{			args: []string{				"oauth-test",				"--id", "1",				"--name", "oauth2 (via openidConnect) source `--scopes` aggregates multiple uses",				"--scopes", "address",				"--scopes", "email",				"--scopes", "phone",				"--scopes", "profile",			},			authSource: &auth.Source{				Type: auth.OAuth2,				Name: "oauth2 (via openidConnect) source `--scopes` aggregates multiple uses",				Cfg: &oauth2.Source{					CustomURLMapping: &oauth2.CustomURLMapping{},					Scopes:           []string{"address", "email", "phone", "profile"},				},			},		},		// case 12		{			args: []string{				"oauth-test",				"--id", "1",				"--name", "oauth2 (via openidConnect) source `--scopes` supports commas as separators",

Lines 1–500 of 706.