Move provider initialisation into providers package
This commit is contained in:
parent
95dd2745c7
commit
d162b018a8
@ -305,7 +305,7 @@ Provider holds all configuration for a single provider
|
||||
| `oidcConfig` | _[OIDCOptions](#oidcoptions)_ | OIDCConfig holds all configurations for OIDC provider<br/>or providers utilize OIDC configurations. |
|
||||
| `loginGovConfig` | _[LoginGovOptions](#logingovoptions)_ | LoginGovConfig holds all configurations for LoginGov provider. |
|
||||
| `id` | _string_ | ID should be a unique identifier for the provider.<br/>This value is required for all providers. |
|
||||
| `provider` | _string_ | Type is the OAuth provider<br/>must be set from the supported providers group,<br/>otherwise 'Google' is set as default |
|
||||
| `provider` | _[ProviderType](#providertype)_ | Type is the OAuth provider<br/>must be set from the supported providers group,<br/>otherwise 'Google' is set as default |
|
||||
| `name` | _string_ | Name is the providers display name<br/>if set, it will be shown to the users in the login page. |
|
||||
| `caFiles` | _[]string_ | CAFiles is a list of paths to CA certificates that should be used when connecting to the provider.<br/>If not specified, the default Go trust sources are used instead |
|
||||
| `loginURL` | _string_ | LoginURL is the authentication endpoint |
|
||||
@ -319,6 +319,17 @@ Provider holds all configuration for a single provider
|
||||
| `allowedGroups` | _[]string_ | AllowedGroups is a list of restrict logins to members of this group |
|
||||
| `acrValues` | _string_ | AcrValues is a string of acr values |
|
||||
|
||||
### ProviderType
|
||||
#### (`string` alias)
|
||||
|
||||
(**Appears on:** [Provider](#provider))
|
||||
|
||||
ProviderType is used to enumerate the different provider type options
|
||||
Valid options are: adfs, azure, bitbucket, digitalocean facebook, github,
|
||||
gitlab, google, keycloak, keycloak-oidc, linkedin, login.gov, nextcloud
|
||||
and oidc.
|
||||
|
||||
|
||||
### Providers
|
||||
|
||||
#### ([[]Provider](#provider) alias)
|
||||
|
@ -624,7 +624,7 @@ func (l *LegacyProvider) convert() (Providers, error) {
|
||||
ClientID: l.ClientID,
|
||||
ClientSecret: l.ClientSecret,
|
||||
ClientSecretFile: l.ClientSecretFile,
|
||||
Type: l.ProviderType,
|
||||
Type: ProviderType(l.ProviderType),
|
||||
CAFiles: l.ProviderCAFiles,
|
||||
LoginURL: l.LoginURL,
|
||||
RedeemURL: l.RedeemURL,
|
||||
|
@ -52,7 +52,7 @@ type Provider struct {
|
||||
// Type is the OAuth provider
|
||||
// must be set from the supported providers group,
|
||||
// otherwise 'Google' is set as default
|
||||
Type string `json:"provider,omitempty"`
|
||||
Type ProviderType `json:"provider,omitempty"`
|
||||
// Name is the providers display name
|
||||
// if set, it will be shown to the users in the login page.
|
||||
Name string `json:"name,omitempty"`
|
||||
@ -84,6 +84,56 @@ type Provider struct {
|
||||
AcrValues string `json:"acrValues,omitempty"`
|
||||
}
|
||||
|
||||
// ProviderType is used to enumerate the different provider type options
|
||||
// Valid options are: adfs, azure, bitbucket, digitalocean facebook, github,
|
||||
// gitlab, google, keycloak, keycloak-oidc, linkedin, login.gov, nextcloud
|
||||
// and oidc.
|
||||
type ProviderType string
|
||||
|
||||
const (
|
||||
// ADFSProvider is the provider type for ADFS
|
||||
ADFSProvider ProviderType = "adfs"
|
||||
|
||||
// AzureProvider is the provider type for Azure
|
||||
AzureProvider ProviderType = "azure"
|
||||
|
||||
// BitbucketProvider is the provider type for Bitbucket
|
||||
BitbucketProvider ProviderType = "bitbucket"
|
||||
|
||||
// DigitalOceanProvider is the provider type for DigitalOcean
|
||||
DigitalOceanProvider ProviderType = "digitalocean"
|
||||
|
||||
// FacebookProvider is the provider type for Facebook
|
||||
FacebookProvider ProviderType = "facebook"
|
||||
|
||||
// GitHubProvider is the provider type for GitHub
|
||||
GitHubProvider ProviderType = "github"
|
||||
|
||||
// GitLabProvider is the provider type for GitLab
|
||||
GitLabProvider ProviderType = "gitlab"
|
||||
|
||||
// GoogleProvider is the provider type for GoogleProvider
|
||||
GoogleProvider ProviderType = "google"
|
||||
|
||||
// KeycloakProvider is the provider type for Keycloak
|
||||
KeycloakProvider ProviderType = "keycloak"
|
||||
|
||||
// KeycloakOIDCProvider is the provider type for Keycloak OIDC
|
||||
KeycloakOIDCProvider ProviderType = "keycloak-oidc"
|
||||
|
||||
// LinkedInProvider is the provider type for LinkedIn
|
||||
LinkedInProvider ProviderType = "linkedin"
|
||||
|
||||
// LoginGovProvider is the provider type for LoginGov
|
||||
LoginGovProvider ProviderType = "login.gov"
|
||||
|
||||
// NextCloudProvider is the provider type for NextCloud
|
||||
NextCloudProvider ProviderType = "nextcloud"
|
||||
|
||||
// OIDCProvider is the provider type for OIDC
|
||||
OIDCProvider ProviderType = "oidc"
|
||||
)
|
||||
|
||||
type KeycloakOptions struct {
|
||||
// Group enables to restrict login to members of indicated group
|
||||
Groups []string `json:"groups,omitempty"`
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
|
||||
)
|
||||
|
||||
@ -24,12 +25,11 @@ var _ Provider = (*ADFSProvider)(nil)
|
||||
const (
|
||||
adfsProviderName = "ADFS"
|
||||
adfsDefaultScope = "openid email profile"
|
||||
adfsSkipScope = false
|
||||
adfsUPNClaim = "upn"
|
||||
)
|
||||
|
||||
// NewADFSProvider initiates a new ADFSProvider
|
||||
func NewADFSProvider(p *ProviderData) *ADFSProvider {
|
||||
func NewADFSProvider(p *ProviderData, opts options.ADFSOptions) *ADFSProvider {
|
||||
p.setProviderDefaults(providerDefaults{
|
||||
name: adfsProviderName,
|
||||
scope: adfsDefaultScope,
|
||||
@ -53,17 +53,12 @@ func NewADFSProvider(p *ProviderData) *ADFSProvider {
|
||||
|
||||
return &ADFSProvider{
|
||||
OIDCProvider: oidcProvider,
|
||||
skipScope: adfsSkipScope,
|
||||
skipScope: opts.SkipScope,
|
||||
oidcEnrichFunc: oidcProvider.EnrichSession,
|
||||
oidcRefreshFunc: oidcProvider.RefreshSession,
|
||||
}
|
||||
}
|
||||
|
||||
// Configure defaults the ADFSProvider configuration options
|
||||
func (p *ADFSProvider) Configure(skipScope bool) {
|
||||
p.skipScope = skipScope
|
||||
}
|
||||
|
||||
// GetLoginURL Override to double encode the state parameter. If not query params are lost
|
||||
// More info here: https://docs.microsoft.com/en-us/powerapps/maker/portals/configure/configure-saml2-settings
|
||||
func (p *ADFSProvider) GetLoginURL(redirectURI, state, nonce string) string {
|
||||
|
@ -13,6 +13,7 @@ import (
|
||||
|
||||
"github.com/coreos/go-oidc/v3/oidc"
|
||||
"github.com/golang-jwt/jwt"
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
|
||||
internaloidc "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/oidc"
|
||||
. "github.com/onsi/ginkgo"
|
||||
@ -61,8 +62,8 @@ func testADFSProvider(hostname string) *ADFSProvider {
|
||||
ValidateURL: &url.URL{},
|
||||
Scope: "",
|
||||
Verifier: o,
|
||||
EmailClaim: OIDCEmailClaim,
|
||||
})
|
||||
EmailClaim: options.OIDCEmailClaim,
|
||||
}, options.ADFSOptions{})
|
||||
|
||||
if hostname != "" {
|
||||
updateURL(p.Data().LoginURL, hostname)
|
||||
@ -133,7 +134,7 @@ var _ = Describe("ADFS Provider Tests", func() {
|
||||
|
||||
Context("New Provider Init", func() {
|
||||
It("uses defaults", func() {
|
||||
providerData := NewADFSProvider(&ProviderData{}).Data()
|
||||
providerData := NewADFSProvider(&ProviderData{}, options.ADFSOptions{}).Data()
|
||||
Expect(providerData.ProviderName).To(Equal("ADFS"))
|
||||
Expect(providerData.Scope).To(Equal("openid email profile"))
|
||||
})
|
||||
@ -165,8 +166,7 @@ var _ = Describe("ADFS Provider Tests", func() {
|
||||
p := NewADFSProvider(&ProviderData{
|
||||
ProtectedResource: resource,
|
||||
Scope: "",
|
||||
})
|
||||
p.skipScope = true
|
||||
}, options.ADFSOptions{SkipScope: true})
|
||||
|
||||
result := p.GetLoginURL("https://example.com/adfs/oauth2/", "", "")
|
||||
Expect(result).NotTo(ContainSubstring("scope="))
|
||||
@ -186,7 +186,7 @@ var _ = Describe("ADFS Provider Tests", func() {
|
||||
p := NewADFSProvider(&ProviderData{
|
||||
ProtectedResource: resource,
|
||||
Scope: in.scope,
|
||||
})
|
||||
}, options.ADFSOptions{})
|
||||
|
||||
Expect(p.Data().Scope).To(Equal(in.expectedScope))
|
||||
result := p.GetLoginURL("https://example.com/adfs/oauth2/", "", "")
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/bitly/go-simplejson"
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger"
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/requests"
|
||||
@ -62,7 +63,7 @@ var (
|
||||
)
|
||||
|
||||
// NewAzureProvider initiates a new AzureProvider
|
||||
func NewAzureProvider(p *ProviderData) *AzureProvider {
|
||||
func NewAzureProvider(p *ProviderData, opts options.AzureOptions) *AzureProvider {
|
||||
p.setProviderDefaults(providerDefaults{
|
||||
name: azureProviderName,
|
||||
loginURL: azureDefaultLoginURL,
|
||||
@ -80,25 +81,19 @@ func NewAzureProvider(p *ProviderData) *AzureProvider {
|
||||
}
|
||||
p.getAuthorizationHeaderFunc = makeAzureHeader
|
||||
|
||||
tenant := "common"
|
||||
if opts.Tenant != "" {
|
||||
tenant = opts.Tenant
|
||||
overrideTenantURL(p.LoginURL, azureDefaultLoginURL, tenant, "authorize")
|
||||
overrideTenantURL(p.RedeemURL, azureDefaultRedeemURL, tenant, "token")
|
||||
}
|
||||
|
||||
return &AzureProvider{
|
||||
ProviderData: p,
|
||||
Tenant: "common",
|
||||
Tenant: tenant,
|
||||
}
|
||||
}
|
||||
|
||||
// Configure defaults the AzureProvider configuration options
|
||||
func (p *AzureProvider) Configure(tenant string) {
|
||||
if tenant == "" || tenant == "common" {
|
||||
// tenant is empty or default, remain on the default "common" tenant
|
||||
return
|
||||
}
|
||||
|
||||
// Specific tennant specified, override the Login and RedeemURLs
|
||||
p.Tenant = tenant
|
||||
overrideTenantURL(p.LoginURL, azureDefaultLoginURL, tenant, "authorize")
|
||||
overrideTenantURL(p.RedeemURL, azureDefaultRedeemURL, tenant, "token")
|
||||
}
|
||||
|
||||
func overrideTenantURL(current, defaultURL *url.URL, tenant, path string) {
|
||||
if current == nil || current.String() == "" || current.String() == defaultURL.String() {
|
||||
*current = url.URL{
|
||||
|
@ -15,6 +15,7 @@ import (
|
||||
|
||||
"github.com/coreos/go-oidc/v3/oidc"
|
||||
"github.com/golang-jwt/jwt"
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
|
||||
internaloidc "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/oidc"
|
||||
|
||||
@ -39,7 +40,7 @@ type azureOAuthPayload struct {
|
||||
IDToken string `json:"id_token,omitempty"`
|
||||
}
|
||||
|
||||
func testAzureProvider(hostname string) *AzureProvider {
|
||||
func testAzureProvider(hostname string, opts options.AzureOptions) *AzureProvider {
|
||||
verificationOptions := &internaloidc.IDTokenVerificationOptions{
|
||||
AudienceClaims: []string{"aud"},
|
||||
ClientID: "cd6d4fae-f6a6-4a34-8454-2c6b598e9532",
|
||||
@ -64,7 +65,7 @@ func testAzureProvider(hostname string) *AzureProvider {
|
||||
SkipExpiryCheck: true,
|
||||
},
|
||||
), verificationOptions),
|
||||
})
|
||||
}, opts)
|
||||
|
||||
if hostname != "" {
|
||||
updateURL(p.Data().LoginURL, hostname)
|
||||
@ -80,7 +81,7 @@ func TestNewAzureProvider(t *testing.T) {
|
||||
g := NewWithT(t)
|
||||
|
||||
// Test that defaults are set when calling for a new provider with nothing set
|
||||
providerData := NewAzureProvider(&ProviderData{}).Data()
|
||||
providerData := NewAzureProvider(&ProviderData{}, options.AzureOptions{}).Data()
|
||||
g.Expect(providerData.ProviderName).To(Equal("Azure"))
|
||||
g.Expect(providerData.LoginURL.String()).To(Equal("https://login.microsoftonline.com/common/oauth2/authorize"))
|
||||
g.Expect(providerData.RedeemURL.String()).To(Equal("https://login.microsoftonline.com/common/oauth2/token"))
|
||||
@ -111,7 +112,8 @@ func TestAzureProviderOverrides(t *testing.T) {
|
||||
ProtectedResource: &url.URL{
|
||||
Scheme: "https",
|
||||
Host: "example.com"},
|
||||
Scope: "profile"})
|
||||
Scope: "profile"},
|
||||
options.AzureOptions{})
|
||||
assert.NotEqual(t, nil, p)
|
||||
assert.Equal(t, "Azure", p.Data().ProviderName)
|
||||
assert.Equal(t, "https://example.com/oauth/auth",
|
||||
@ -128,8 +130,7 @@ func TestAzureProviderOverrides(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAzureSetTenant(t *testing.T) {
|
||||
p := testAzureProvider("")
|
||||
p.Configure("example")
|
||||
p := testAzureProvider("", options.AzureOptions{Tenant: "example"})
|
||||
assert.Equal(t, "Azure", p.Data().ProviderName)
|
||||
assert.Equal(t, "example", p.Tenant)
|
||||
assert.Equal(t, "https://login.microsoftonline.com/example/oauth2/authorize",
|
||||
@ -230,7 +231,7 @@ func TestAzureProviderEnrichSession(t *testing.T) {
|
||||
bURL, _ := url.Parse(b.URL)
|
||||
host = bURL.Host
|
||||
}
|
||||
p := testAzureProvider(host)
|
||||
p := testAzureProvider(host, options.AzureOptions{})
|
||||
session := CreateAuthorizedSession()
|
||||
session.Email = testCase.Email
|
||||
err := p.EnrichSession(context.Background(), session)
|
||||
@ -323,7 +324,7 @@ func TestAzureProviderRedeem(t *testing.T) {
|
||||
defer b.Close()
|
||||
|
||||
bURL, _ := url.Parse(b.URL)
|
||||
p := testAzureProvider(bURL.Host)
|
||||
p := testAzureProvider(bURL.Host, options.AzureOptions{})
|
||||
p.Data().RedeemURL.Path = "/common/oauth2/token"
|
||||
s, err := p.Redeem(context.Background(), "https://localhost", "1234")
|
||||
if testCase.InjectRedeemURLError {
|
||||
@ -345,7 +346,7 @@ func TestAzureProviderRedeem(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAzureProviderProtectedResourceConfigured(t *testing.T) {
|
||||
p := testAzureProvider("")
|
||||
p := testAzureProvider("", options.AzureOptions{})
|
||||
p.ProtectedResource, _ = url.Parse("http://my.resource.test")
|
||||
result := p.GetLoginURL("https://my.test.app/oauth", "", "")
|
||||
assert.Contains(t, result, "resource="+url.QueryEscape("http://my.resource.test"))
|
||||
@ -381,7 +382,7 @@ func TestAzureProviderRefresh(t *testing.T) {
|
||||
b := testAzureBackend(string(payloadBytes), newAccessToken, refreshToken)
|
||||
defer b.Close()
|
||||
bURL, _ := url.Parse(b.URL)
|
||||
p := testAzureProvider(bURL.Host)
|
||||
p := testAzureProvider(bURL.Host, options.AzureOptions{})
|
||||
|
||||
expires := time.Now().Add(time.Duration(-1) * time.Hour)
|
||||
session := &sessions.SessionState{AccessToken: "some_access_token", RefreshToken: refreshToken, IDToken: "some_id_token", ExpiresOn: &expires}
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger"
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/requests"
|
||||
@ -53,7 +54,7 @@ var (
|
||||
)
|
||||
|
||||
// NewBitbucketProvider initiates a new BitbucketProvider
|
||||
func NewBitbucketProvider(p *ProviderData) *BitbucketProvider {
|
||||
func NewBitbucketProvider(p *ProviderData, opts options.BitbucketOptions) *BitbucketProvider {
|
||||
p.setProviderDefaults(providerDefaults{
|
||||
name: bitbucketProviderName,
|
||||
loginURL: bitbucketDefaultLoginURL,
|
||||
@ -62,19 +63,28 @@ func NewBitbucketProvider(p *ProviderData) *BitbucketProvider {
|
||||
validateURL: bitbucketDefaultValidateURL,
|
||||
scope: bitbucketDefaultScope,
|
||||
})
|
||||
return &BitbucketProvider{ProviderData: p}
|
||||
|
||||
provider := &BitbucketProvider{ProviderData: p}
|
||||
|
||||
if opts.Team != "" {
|
||||
provider.setTeam(opts.Team)
|
||||
}
|
||||
if opts.Repository != "" {
|
||||
provider.setRepository(opts.Repository)
|
||||
}
|
||||
return provider
|
||||
}
|
||||
|
||||
// SetTeam defines the Bitbucket team the user must be part of
|
||||
func (p *BitbucketProvider) SetTeam(team string) {
|
||||
// setTeam defines the Bitbucket team the user must be part of
|
||||
func (p *BitbucketProvider) setTeam(team string) {
|
||||
p.Team = team
|
||||
if !strings.Contains(p.Scope, "team") {
|
||||
p.Scope += " team"
|
||||
}
|
||||
}
|
||||
|
||||
// SetRepository defines the repository the user must have access to
|
||||
func (p *BitbucketProvider) SetRepository(repository string) {
|
||||
// setRepository defines the repository the user must have access to
|
||||
func (p *BitbucketProvider) setRepository(repository string) {
|
||||
p.Repository = repository
|
||||
if !strings.Contains(p.Scope, "repository") {
|
||||
p.Scope += " repository"
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/stretchr/testify/assert"
|
||||
@ -21,15 +22,12 @@ func testBitbucketProvider(hostname, team string, repository string) *BitbucketP
|
||||
RedeemURL: &url.URL{},
|
||||
ProfileURL: &url.URL{},
|
||||
ValidateURL: &url.URL{},
|
||||
Scope: ""})
|
||||
|
||||
if team != "" {
|
||||
p.SetTeam(team)
|
||||
}
|
||||
|
||||
if repository != "" {
|
||||
p.SetRepository(repository)
|
||||
}
|
||||
Scope: ""},
|
||||
options.BitbucketOptions{
|
||||
Team: team,
|
||||
Repository: repository,
|
||||
},
|
||||
)
|
||||
|
||||
if hostname != "" {
|
||||
updateURL(p.Data().LoginURL, hostname)
|
||||
@ -65,7 +63,7 @@ func TestNewBitbucketProvider(t *testing.T) {
|
||||
g := NewWithT(t)
|
||||
|
||||
// Test that defaults are set when calling for a new provider with nothing set
|
||||
providerData := NewBitbucketProvider(&ProviderData{}).Data()
|
||||
providerData := NewBitbucketProvider(&ProviderData{}, options.BitbucketOptions{}).Data()
|
||||
g.Expect(providerData.ProviderName).To(Equal("Bitbucket"))
|
||||
g.Expect(providerData.LoginURL.String()).To(Equal("https://bitbucket.org/site/oauth2/authorize"))
|
||||
g.Expect(providerData.RedeemURL.String()).To(Equal("https://bitbucket.org/site/oauth2/access_token"))
|
||||
@ -101,7 +99,8 @@ func TestBitbucketProviderOverrides(t *testing.T) {
|
||||
Scheme: "https",
|
||||
Host: "example.com",
|
||||
Path: "/api/v3/user"},
|
||||
Scope: "profile"})
|
||||
Scope: "profile"},
|
||||
options.BitbucketOptions{})
|
||||
assert.NotEqual(t, nil, p)
|
||||
assert.Equal(t, "Bitbucket", p.Data().ProviderName)
|
||||
assert.Equal(t, "https://example.com/oauth/auth",
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger"
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/requests"
|
||||
@ -62,7 +63,7 @@ var (
|
||||
)
|
||||
|
||||
// NewGitHubProvider initiates a new GitHubProvider
|
||||
func NewGitHubProvider(p *ProviderData) *GitHubProvider {
|
||||
func NewGitHubProvider(p *ProviderData, opts options.GitHubOptions) *GitHubProvider {
|
||||
p.setProviderDefaults(providerDefaults{
|
||||
name: githubProviderName,
|
||||
loginURL: githubDefaultLoginURL,
|
||||
@ -71,7 +72,13 @@ func NewGitHubProvider(p *ProviderData) *GitHubProvider {
|
||||
validateURL: githubDefaultValidateURL,
|
||||
scope: githubDefaultScope,
|
||||
})
|
||||
return &GitHubProvider{ProviderData: p}
|
||||
|
||||
provider := &GitHubProvider{ProviderData: p}
|
||||
|
||||
provider.setOrgTeam(opts.Org, opts.Team)
|
||||
provider.setRepo(opts.Repo, opts.Token)
|
||||
provider.setUsers(opts.Users)
|
||||
return provider
|
||||
}
|
||||
|
||||
func makeGitHubHeader(accessToken string) http.Header {
|
||||
@ -82,8 +89,8 @@ func makeGitHubHeader(accessToken string) http.Header {
|
||||
return makeAuthorizationHeader(tokenTypeToken, accessToken, extraHeaders)
|
||||
}
|
||||
|
||||
// SetOrgTeam adds GitHub org reading parameters to the OAuth2 scope
|
||||
func (p *GitHubProvider) SetOrgTeam(org, team string) {
|
||||
// setOrgTeam adds GitHub org reading parameters to the OAuth2 scope
|
||||
func (p *GitHubProvider) setOrgTeam(org, team string) {
|
||||
p.Org = org
|
||||
p.Team = team
|
||||
if org != "" || team != "" {
|
||||
@ -91,14 +98,14 @@ func (p *GitHubProvider) SetOrgTeam(org, team string) {
|
||||
}
|
||||
}
|
||||
|
||||
// SetRepo configures the target repository and optional token to use
|
||||
func (p *GitHubProvider) SetRepo(repo, token string) {
|
||||
// setRepo configures the target repository and optional token to use
|
||||
func (p *GitHubProvider) setRepo(repo, token string) {
|
||||
p.Repo = repo
|
||||
p.Token = token
|
||||
}
|
||||
|
||||
// SetUsers configures allowed usernames
|
||||
func (p *GitHubProvider) SetUsers(users []string) {
|
||||
// setUsers configures allowed usernames
|
||||
func (p *GitHubProvider) setUsers(users []string) {
|
||||
p.Users = users
|
||||
}
|
||||
|
||||
|
@ -7,12 +7,13 @@ import (
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func testGitHubProvider(hostname string) *GitHubProvider {
|
||||
func testGitHubProvider(hostname string, opts options.GitHubOptions) *GitHubProvider {
|
||||
p := NewGitHubProvider(
|
||||
&ProviderData{
|
||||
ProviderName: "",
|
||||
@ -20,7 +21,8 @@ func testGitHubProvider(hostname string) *GitHubProvider {
|
||||
RedeemURL: &url.URL{},
|
||||
ProfileURL: &url.URL{},
|
||||
ValidateURL: &url.URL{},
|
||||
Scope: ""})
|
||||
Scope: ""},
|
||||
opts)
|
||||
if hostname != "" {
|
||||
updateURL(p.Data().LoginURL, hostname)
|
||||
updateURL(p.Data().RedeemURL, hostname)
|
||||
@ -71,7 +73,7 @@ func TestNewGitHubProvider(t *testing.T) {
|
||||
g := NewWithT(t)
|
||||
|
||||
// Test that defaults are set when calling for a new provider with nothing set
|
||||
providerData := NewGitHubProvider(&ProviderData{}).Data()
|
||||
providerData := NewGitHubProvider(&ProviderData{}, options.GitHubOptions{}).Data()
|
||||
g.Expect(providerData.ProviderName).To(Equal("GitHub"))
|
||||
g.Expect(providerData.LoginURL.String()).To(Equal("https://github.com/login/oauth/authorize"))
|
||||
g.Expect(providerData.RedeemURL.String()).To(Equal("https://github.com/login/oauth/access_token"))
|
||||
@ -95,7 +97,8 @@ func TestGitHubProviderOverrides(t *testing.T) {
|
||||
Scheme: "https",
|
||||
Host: "api.example.com",
|
||||
Path: "/"},
|
||||
Scope: "profile"})
|
||||
Scope: "profile"},
|
||||
options.GitHubOptions{})
|
||||
assert.NotEqual(t, nil, p)
|
||||
assert.Equal(t, "GitHub", p.Data().ProviderName)
|
||||
assert.Equal(t, "https://example.com/login/oauth/authorize",
|
||||
@ -114,7 +117,7 @@ func TestGitHubProvider_getEmail(t *testing.T) {
|
||||
defer b.Close()
|
||||
|
||||
bURL, _ := url.Parse(b.URL)
|
||||
p := testGitHubProvider(bURL.Host)
|
||||
p := testGitHubProvider(bURL.Host, options.GitHubOptions{})
|
||||
|
||||
session := CreateAuthorizedSession()
|
||||
err := p.getEmail(context.Background(), session)
|
||||
@ -129,7 +132,7 @@ func TestGitHubProvider_getEmailNotVerified(t *testing.T) {
|
||||
defer b.Close()
|
||||
|
||||
bURL, _ := url.Parse(b.URL)
|
||||
p := testGitHubProvider(bURL.Host)
|
||||
p := testGitHubProvider(bURL.Host, options.GitHubOptions{})
|
||||
|
||||
session := CreateAuthorizedSession()
|
||||
err := p.getEmail(context.Background(), session)
|
||||
@ -149,8 +152,11 @@ func TestGitHubProvider_getEmailWithOrg(t *testing.T) {
|
||||
defer b.Close()
|
||||
|
||||
bURL, _ := url.Parse(b.URL)
|
||||
p := testGitHubProvider(bURL.Host)
|
||||
p.Org = "testorg1"
|
||||
p := testGitHubProvider(bURL.Host,
|
||||
options.GitHubOptions{
|
||||
Org: "testorg1",
|
||||
},
|
||||
)
|
||||
|
||||
session := CreateAuthorizedSession()
|
||||
err := p.getEmail(context.Background(), session)
|
||||
@ -166,8 +172,12 @@ func TestGitHubProvider_getEmailWithWriteAccessToPublicRepo(t *testing.T) {
|
||||
defer b.Close()
|
||||
|
||||
bURL, _ := url.Parse(b.URL)
|
||||
p := testGitHubProvider(bURL.Host)
|
||||
p.SetRepo("oauth2-proxy/oauth2-proxy", "")
|
||||
p := testGitHubProvider(bURL.Host,
|
||||
options.GitHubOptions{
|
||||
Repo: "oauth2-proxy/oauth2-proxy",
|
||||
Token: "token",
|
||||
},
|
||||
)
|
||||
|
||||
session := CreateAuthorizedSession()
|
||||
err := p.getEmail(context.Background(), session)
|
||||
@ -183,8 +193,12 @@ func TestGitHubProvider_getEmailWithReadOnlyAccessToPrivateRepo(t *testing.T) {
|
||||
defer b.Close()
|
||||
|
||||
bURL, _ := url.Parse(b.URL)
|
||||
p := testGitHubProvider(bURL.Host)
|
||||
p.SetRepo("oauth2-proxy/oauth2-proxy", "")
|
||||
p := testGitHubProvider(bURL.Host,
|
||||
options.GitHubOptions{
|
||||
Repo: "oauth2-proxy/oauth2-proxy",
|
||||
Token: "token",
|
||||
},
|
||||
)
|
||||
|
||||
session := CreateAuthorizedSession()
|
||||
err := p.getEmail(context.Background(), session)
|
||||
@ -200,8 +214,12 @@ func TestGitHubProvider_getEmailWithWriteAccessToPrivateRepo(t *testing.T) {
|
||||
defer b.Close()
|
||||
|
||||
bURL, _ := url.Parse(b.URL)
|
||||
p := testGitHubProvider(bURL.Host)
|
||||
p.SetRepo("oauth2-proxy/oauth2-proxy", "")
|
||||
p := testGitHubProvider(bURL.Host,
|
||||
options.GitHubOptions{
|
||||
Repo: "oauth2-proxy/oauth2-proxy",
|
||||
Token: "token",
|
||||
},
|
||||
)
|
||||
|
||||
session := CreateAuthorizedSession()
|
||||
err := p.getEmail(context.Background(), session)
|
||||
@ -216,8 +234,11 @@ func TestGitHubProvider_getEmailWithNoAccessToPrivateRepo(t *testing.T) {
|
||||
defer b.Close()
|
||||
|
||||
bURL, _ := url.Parse(b.URL)
|
||||
p := testGitHubProvider(bURL.Host)
|
||||
p.SetRepo("oauth2-proxy/oauth2-proxy", "")
|
||||
p := testGitHubProvider(bURL.Host,
|
||||
options.GitHubOptions{
|
||||
Repo: "oauth2-proxy/oauth2-proxy",
|
||||
},
|
||||
)
|
||||
|
||||
session := CreateAuthorizedSession()
|
||||
err := p.getEmail(context.Background(), session)
|
||||
@ -232,8 +253,12 @@ func TestGitHubProvider_getEmailWithToken(t *testing.T) {
|
||||
defer b.Close()
|
||||
|
||||
bURL, _ := url.Parse(b.URL)
|
||||
p := testGitHubProvider(bURL.Host)
|
||||
p.SetRepo("oauth2-proxy/oauth2-proxy", "token")
|
||||
p := testGitHubProvider(bURL.Host,
|
||||
options.GitHubOptions{
|
||||
Repo: "oauth2-proxy/oauth2-proxy",
|
||||
Token: "token",
|
||||
},
|
||||
)
|
||||
|
||||
session := CreateAuthorizedSession()
|
||||
err := p.getEmail(context.Background(), session)
|
||||
@ -248,7 +273,7 @@ func TestGitHubProvider_getEmailFailedRequest(t *testing.T) {
|
||||
defer b.Close()
|
||||
|
||||
bURL, _ := url.Parse(b.URL)
|
||||
p := testGitHubProvider(bURL.Host)
|
||||
p := testGitHubProvider(bURL.Host, options.GitHubOptions{})
|
||||
|
||||
// We'll trigger a request failure by using an unexpected access
|
||||
// token. Alternatively, we could allow the parsing of the payload as
|
||||
@ -266,7 +291,7 @@ func TestGitHubProvider_getEmailNotPresentInPayload(t *testing.T) {
|
||||
defer b.Close()
|
||||
|
||||
bURL, _ := url.Parse(b.URL)
|
||||
p := testGitHubProvider(bURL.Host)
|
||||
p := testGitHubProvider(bURL.Host, options.GitHubOptions{})
|
||||
|
||||
session := CreateAuthorizedSession()
|
||||
err := p.getEmail(context.Background(), session)
|
||||
@ -281,7 +306,7 @@ func TestGitHubProvider_getUser(t *testing.T) {
|
||||
defer b.Close()
|
||||
|
||||
bURL, _ := url.Parse(b.URL)
|
||||
p := testGitHubProvider(bURL.Host)
|
||||
p := testGitHubProvider(bURL.Host, options.GitHubOptions{})
|
||||
|
||||
session := CreateAuthorizedSession()
|
||||
err := p.getUser(context.Background(), session)
|
||||
@ -297,8 +322,12 @@ func TestGitHubProvider_getUserWithRepoAndToken(t *testing.T) {
|
||||
defer b.Close()
|
||||
|
||||
bURL, _ := url.Parse(b.URL)
|
||||
p := testGitHubProvider(bURL.Host)
|
||||
p.SetRepo("oauth2-proxy/oauth2-proxy", "token")
|
||||
p := testGitHubProvider(bURL.Host,
|
||||
options.GitHubOptions{
|
||||
Repo: "oauth2-proxy/oauth2-proxy",
|
||||
Token: "token",
|
||||
},
|
||||
)
|
||||
|
||||
session := CreateAuthorizedSession()
|
||||
err := p.getUser(context.Background(), session)
|
||||
@ -311,8 +340,12 @@ func TestGitHubProvider_getUserWithRepoAndTokenWithoutPushAccess(t *testing.T) {
|
||||
defer b.Close()
|
||||
|
||||
bURL, _ := url.Parse(b.URL)
|
||||
p := testGitHubProvider(bURL.Host)
|
||||
p.SetRepo("oauth2-proxy/oauth2-proxy", "token")
|
||||
p := testGitHubProvider(bURL.Host,
|
||||
options.GitHubOptions{
|
||||
Repo: "oauth2-proxy/oauth2-proxy",
|
||||
Token: "token",
|
||||
},
|
||||
)
|
||||
|
||||
session := CreateAuthorizedSession()
|
||||
err := p.getUser(context.Background(), session)
|
||||
@ -328,8 +361,11 @@ func TestGitHubProvider_getEmailWithUsername(t *testing.T) {
|
||||
defer b.Close()
|
||||
|
||||
bURL, _ := url.Parse(b.URL)
|
||||
p := testGitHubProvider(bURL.Host)
|
||||
p.SetUsers([]string{"mbland", "octocat"})
|
||||
p := testGitHubProvider(bURL.Host,
|
||||
options.GitHubOptions{
|
||||
Users: []string{"mbland", "octocat"},
|
||||
},
|
||||
)
|
||||
|
||||
session := CreateAuthorizedSession()
|
||||
err := p.getEmail(context.Background(), session)
|
||||
@ -345,8 +381,11 @@ func TestGitHubProvider_getEmailWithNotAllowedUsername(t *testing.T) {
|
||||
defer b.Close()
|
||||
|
||||
bURL, _ := url.Parse(b.URL)
|
||||
p := testGitHubProvider(bURL.Host)
|
||||
p.SetUsers([]string{"octocat"})
|
||||
p := testGitHubProvider(bURL.Host,
|
||||
options.GitHubOptions{
|
||||
Users: []string{"octocat"},
|
||||
},
|
||||
)
|
||||
|
||||
session := CreateAuthorizedSession()
|
||||
err := p.getEmail(context.Background(), session)
|
||||
@ -366,9 +405,12 @@ func TestGitHubProvider_getEmailWithUsernameAndNotBelongToOrg(t *testing.T) {
|
||||
defer b.Close()
|
||||
|
||||
bURL, _ := url.Parse(b.URL)
|
||||
p := testGitHubProvider(bURL.Host)
|
||||
p.SetOrgTeam("not_belong_to", "")
|
||||
p.SetUsers([]string{"mbland"})
|
||||
p := testGitHubProvider(bURL.Host,
|
||||
options.GitHubOptions{
|
||||
Org: "not_belog_to",
|
||||
Users: []string{"mbland"},
|
||||
},
|
||||
)
|
||||
|
||||
session := CreateAuthorizedSession()
|
||||
err := p.getEmail(context.Background(), session)
|
||||
@ -385,9 +427,13 @@ func TestGitHubProvider_getEmailWithUsernameAndNoAccessToPrivateRepo(t *testing.
|
||||
defer b.Close()
|
||||
|
||||
bURL, _ := url.Parse(b.URL)
|
||||
p := testGitHubProvider(bURL.Host)
|
||||
p.SetRepo("oauth2-proxy/oauth2-proxy", "")
|
||||
p.SetUsers([]string{"mbland"})
|
||||
p := testGitHubProvider(bURL.Host,
|
||||
options.GitHubOptions{
|
||||
Repo: "oauth2-proxy/oauth2-proxy",
|
||||
Token: "token",
|
||||
Users: []string{"mbland"},
|
||||
},
|
||||
)
|
||||
|
||||
session := CreateAuthorizedSession()
|
||||
err := p.getEmail(context.Background(), session)
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger"
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/requests"
|
||||
@ -30,7 +31,7 @@ type GitLabProvider struct {
|
||||
var _ Provider = (*GitLabProvider)(nil)
|
||||
|
||||
// NewGitLabProvider initiates a new GitLabProvider
|
||||
func NewGitLabProvider(p *ProviderData) *GitLabProvider {
|
||||
func NewGitLabProvider(p *ProviderData, opts options.GitLabOptions) (*GitLabProvider, error) {
|
||||
p.ProviderName = gitlabProviderName
|
||||
if p.Scope == "" {
|
||||
p.Scope = gitlabDefaultScope
|
||||
@ -41,15 +42,22 @@ func NewGitLabProvider(p *ProviderData) *GitLabProvider {
|
||||
SkipNonce: false,
|
||||
}
|
||||
|
||||
return &GitLabProvider{
|
||||
provider := &GitLabProvider{
|
||||
OIDCProvider: oidcProvider,
|
||||
oidcRefreshFunc: oidcProvider.RefreshSession,
|
||||
}
|
||||
provider.setAllowedGroups(opts.Group)
|
||||
|
||||
if err := provider.setAllowedProjects(opts.Projects); err != nil {
|
||||
return nil, fmt.Errorf("could not configure allowed projects: %v", err)
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
}
|
||||
|
||||
// SetAllowedProjects adds Gitlab projects to the AllowedGroups list
|
||||
// setAllowedProjects adds Gitlab projects to the AllowedGroups list
|
||||
// and tracks them to do a project API lookup during `EnrichSession`.
|
||||
func (p *GitLabProvider) SetAllowedProjects(projects []string) error {
|
||||
func (p *GitLabProvider) setAllowedProjects(projects []string) error {
|
||||
for _, project := range projects {
|
||||
gp, err := newGitlabProject(project)
|
||||
if err != nil {
|
||||
|
@ -7,21 +7,26 @@ import (
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/ginkgo/extensions/table"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func testGitLabProvider(hostname string) *GitLabProvider {
|
||||
p := NewGitLabProvider(
|
||||
func testGitLabProvider(hostname, scope string, opts options.GitLabOptions) (*GitLabProvider, error) {
|
||||
p, err := NewGitLabProvider(
|
||||
&ProviderData{
|
||||
ProviderName: "",
|
||||
LoginURL: &url.URL{},
|
||||
RedeemURL: &url.URL{},
|
||||
ProfileURL: &url.URL{},
|
||||
ValidateURL: &url.URL{},
|
||||
Scope: ""})
|
||||
Scope: scope},
|
||||
opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if hostname != "" {
|
||||
updateURL(p.Data().LoginURL, hostname)
|
||||
updateURL(p.Data().RedeemURL, hostname)
|
||||
@ -29,7 +34,7 @@ func testGitLabProvider(hostname string) *GitLabProvider {
|
||||
updateURL(p.Data().ValidateURL, hostname)
|
||||
}
|
||||
|
||||
return p
|
||||
return p, err
|
||||
}
|
||||
|
||||
func testGitLabBackend() *httptest.Server {
|
||||
@ -157,7 +162,8 @@ var _ = Describe("Gitlab Provider Tests", func() {
|
||||
bURL, err := url.Parse(b.URL)
|
||||
Expect(err).To(BeNil())
|
||||
|
||||
p = testGitLabProvider(bURL.Host)
|
||||
p, err = testGitLabProvider(bURL.Host, "", options.GitLabOptions{})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
@ -219,21 +225,23 @@ var _ = Describe("Gitlab Provider Tests", func() {
|
||||
|
||||
DescribeTable("should return expected results",
|
||||
func(in entitiesTableInput) {
|
||||
p.AllowUnverifiedEmail = true
|
||||
if in.scope != "" {
|
||||
p.Scope = in.scope
|
||||
}
|
||||
session := &sessions.SessionState{AccessToken: "gitlab_access_token"}
|
||||
bURL, err := url.Parse(b.URL)
|
||||
Expect(err).To(BeNil())
|
||||
|
||||
p.SetAllowedGroups(in.allowedGroups)
|
||||
|
||||
err := p.SetAllowedProjects(in.allowedProjects)
|
||||
p, err := testGitLabProvider(bURL.Host, in.scope, options.GitLabOptions{
|
||||
Group: in.allowedGroups,
|
||||
Projects: in.allowedProjects,
|
||||
})
|
||||
if in.expectedError == nil {
|
||||
Expect(err).To(BeNil())
|
||||
} else {
|
||||
Expect(err).To(MatchError(in.expectedError))
|
||||
return
|
||||
}
|
||||
|
||||
p.AllowUnverifiedEmail = true
|
||||
session := &sessions.SessionState{AccessToken: "gitlab_access_token"}
|
||||
|
||||
Expect(p.Scope).To(Equal(in.expectedScope))
|
||||
|
||||
err = p.EnrichSession(context.Background(), session)
|
||||
@ -302,7 +310,7 @@ var _ = Describe("Gitlab Provider Tests", func() {
|
||||
}),
|
||||
Entry("invalid project format", entitiesTableInput{
|
||||
allowedProjects: []string{"my_group/my_invalid_project=123"},
|
||||
expectedError: errors.New("invalid gitlab project access level specified (my_group/my_invalid_project)"),
|
||||
expectedError: errors.New("could not configure allowed projects: invalid gitlab project access level specified (my_group/my_invalid_project)"),
|
||||
expectedScope: "openid email read_api",
|
||||
}),
|
||||
)
|
||||
|
@ -10,9 +10,11 @@ import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger"
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/requests"
|
||||
@ -79,7 +81,7 @@ var (
|
||||
)
|
||||
|
||||
// NewGoogleProvider initiates a new GoogleProvider
|
||||
func NewGoogleProvider(p *ProviderData) *GoogleProvider {
|
||||
func NewGoogleProvider(p *ProviderData, opts options.GoogleOptions) (*GoogleProvider, error) {
|
||||
p.setProviderDefaults(providerDefaults{
|
||||
name: googleProviderName,
|
||||
loginURL: googleDefaultLoginURL,
|
||||
@ -88,7 +90,7 @@ func NewGoogleProvider(p *ProviderData) *GoogleProvider {
|
||||
validateURL: googleDefaultValidateURL,
|
||||
scope: googleDefaultScope,
|
||||
})
|
||||
return &GoogleProvider{
|
||||
provider := &GoogleProvider{
|
||||
ProviderData: p,
|
||||
// Set a default groupValidator to just always return valid (true), it will
|
||||
// be overwritten if we configured a Google group restriction.
|
||||
@ -96,6 +98,21 @@ func NewGoogleProvider(p *ProviderData) *GoogleProvider {
|
||||
return true
|
||||
},
|
||||
}
|
||||
|
||||
if opts.ServiceAccountJSON != "" {
|
||||
file, err := os.Open(opts.ServiceAccountJSON)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid Google credentials file: %s", opts.ServiceAccountJSON)
|
||||
}
|
||||
|
||||
// Backwards compatibility with `--google-group` option
|
||||
if len(opts.Groups) > 0 {
|
||||
provider.setAllowedGroups(opts.Groups)
|
||||
}
|
||||
provider.setGroupRestriction(opts.Groups, opts.AdminEmail, file)
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
}
|
||||
|
||||
func claimsFromIDToken(idToken string) (*claims, error) {
|
||||
@ -195,7 +212,7 @@ func (p *GoogleProvider) EnrichSession(_ context.Context, s *sessions.SessionSta
|
||||
// account credentials.
|
||||
//
|
||||
// TODO (@NickMeves) - Unit Test this OR refactor away from groupValidator func
|
||||
func (p *GoogleProvider) SetGroupRestriction(groups []string, adminEmail string, credentialsReader io.Reader) {
|
||||
func (p *GoogleProvider) setGroupRestriction(groups []string, adminEmail string, credentialsReader io.Reader) {
|
||||
adminService := getAdminService(adminEmail, credentialsReader)
|
||||
p.groupValidator = func(s *sessions.SessionState) bool {
|
||||
// Reset our saved Groups in case membership changed
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/stretchr/testify/assert"
|
||||
@ -25,22 +26,29 @@ func newRedeemServer(body []byte) (*url.URL, *httptest.Server) {
|
||||
return u, s
|
||||
}
|
||||
|
||||
func newGoogleProvider() *GoogleProvider {
|
||||
return NewGoogleProvider(
|
||||
func newGoogleProvider(t *testing.T) *GoogleProvider {
|
||||
g := NewWithT(t)
|
||||
p, err := NewGoogleProvider(
|
||||
&ProviderData{
|
||||
ProviderName: "",
|
||||
LoginURL: &url.URL{},
|
||||
RedeemURL: &url.URL{},
|
||||
ProfileURL: &url.URL{},
|
||||
ValidateURL: &url.URL{},
|
||||
Scope: ""})
|
||||
Scope: ""},
|
||||
options.GoogleOptions{})
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
return p
|
||||
}
|
||||
|
||||
func TestNewGoogleProvider(t *testing.T) {
|
||||
g := NewWithT(t)
|
||||
|
||||
// Test that defaults are set when calling for a new provider with nothing set
|
||||
providerData := NewGoogleProvider(&ProviderData{}).Data()
|
||||
provider, err := NewGoogleProvider(&ProviderData{}, options.GoogleOptions{})
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
providerData := provider.Data()
|
||||
|
||||
g.Expect(providerData.ProviderName).To(Equal("Google"))
|
||||
g.Expect(providerData.LoginURL.String()).To(Equal("https://accounts.google.com/o/oauth2/auth?access_type=offline"))
|
||||
g.Expect(providerData.RedeemURL.String()).To(Equal("https://www.googleapis.com/oauth2/v3/token"))
|
||||
@ -50,7 +58,7 @@ func TestNewGoogleProvider(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGoogleProviderOverrides(t *testing.T) {
|
||||
p := NewGoogleProvider(
|
||||
p, err := NewGoogleProvider(
|
||||
&ProviderData{
|
||||
LoginURL: &url.URL{
|
||||
Scheme: "https",
|
||||
@ -68,7 +76,9 @@ func TestGoogleProviderOverrides(t *testing.T) {
|
||||
Scheme: "https",
|
||||
Host: "example.com",
|
||||
Path: "/oauth/tokeninfo"},
|
||||
Scope: "profile"})
|
||||
Scope: "profile"},
|
||||
options.GoogleOptions{})
|
||||
assert.NoError(t, err)
|
||||
assert.NotEqual(t, nil, p)
|
||||
assert.Equal(t, "Google", p.Data().ProviderName)
|
||||
assert.Equal(t, "https://example.com/oauth/auth",
|
||||
@ -90,7 +100,7 @@ type redeemResponse struct {
|
||||
}
|
||||
|
||||
func TestGoogleProviderGetEmailAddress(t *testing.T) {
|
||||
p := newGoogleProvider()
|
||||
p := newGoogleProvider(t)
|
||||
body, err := json.Marshal(redeemResponse{
|
||||
AccessToken: "a1234",
|
||||
ExpiresIn: 10,
|
||||
@ -147,7 +157,7 @@ func TestGoogleProviderGroupValidator(t *testing.T) {
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
g := NewWithT(t)
|
||||
p := newGoogleProvider()
|
||||
p := newGoogleProvider(t)
|
||||
if tc.validatorFunc != nil {
|
||||
p.groupValidator = tc.validatorFunc
|
||||
}
|
||||
@ -158,7 +168,7 @@ func TestGoogleProviderGroupValidator(t *testing.T) {
|
||||
|
||||
//
|
||||
func TestGoogleProviderGetEmailAddressInvalidEncoding(t *testing.T) {
|
||||
p := newGoogleProvider()
|
||||
p := newGoogleProvider(t)
|
||||
body, err := json.Marshal(redeemResponse{
|
||||
AccessToken: "a1234",
|
||||
IDToken: "ignored prefix." + `{"email": "michael.bland@gsa.gov"}`,
|
||||
@ -176,7 +186,7 @@ func TestGoogleProviderGetEmailAddressInvalidEncoding(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGoogleProviderRedeemFailsNoCLientSecret(t *testing.T) {
|
||||
p := newGoogleProvider()
|
||||
p := newGoogleProvider(t)
|
||||
p.ProviderData.ClientSecretFile = "srvnoerre"
|
||||
|
||||
session, err := p.Redeem(context.Background(), "http://redirect/", "code1234")
|
||||
@ -188,7 +198,7 @@ func TestGoogleProviderRedeemFailsNoCLientSecret(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGoogleProviderGetEmailAddressInvalidJson(t *testing.T) {
|
||||
p := newGoogleProvider()
|
||||
p := newGoogleProvider(t)
|
||||
|
||||
body, err := json.Marshal(redeemResponse{
|
||||
AccessToken: "a1234",
|
||||
@ -208,7 +218,7 @@ func TestGoogleProviderGetEmailAddressInvalidJson(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGoogleProviderGetEmailAddressEmailMissing(t *testing.T) {
|
||||
p := newGoogleProvider()
|
||||
p := newGoogleProvider(t)
|
||||
body, err := json.Marshal(redeemResponse{
|
||||
AccessToken: "a1234",
|
||||
IDToken: "ignored prefix." + base64.URLEncoding.EncodeToString([]byte(`{"not_email": "missing"}`)),
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger"
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/requests"
|
||||
@ -48,7 +49,7 @@ var (
|
||||
)
|
||||
|
||||
// NewKeycloakProvider creates a KeyCloakProvider using the passed ProviderData
|
||||
func NewKeycloakProvider(p *ProviderData) *KeycloakProvider {
|
||||
func NewKeycloakProvider(p *ProviderData, opts options.KeycloakOptions) *KeycloakProvider {
|
||||
p.setProviderDefaults(providerDefaults{
|
||||
name: keycloakProviderName,
|
||||
loginURL: keycloakDefaultLoginURL,
|
||||
@ -57,7 +58,10 @@ func NewKeycloakProvider(p *ProviderData) *KeycloakProvider {
|
||||
validateURL: keycloakDefaultValidateURL,
|
||||
scope: keycloakDefaultScope,
|
||||
})
|
||||
return &KeycloakProvider{ProviderData: p}
|
||||
|
||||
provider := &KeycloakProvider{ProviderData: p}
|
||||
provider.setAllowedGroups(opts.Groups)
|
||||
return provider
|
||||
}
|
||||
|
||||
// EnrichSession uses the Keycloak userinfo endpoint to populate the session's
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
|
||||
)
|
||||
|
||||
@ -15,21 +16,25 @@ type KeycloakOIDCProvider struct {
|
||||
}
|
||||
|
||||
// NewKeycloakOIDCProvider makes a KeycloakOIDCProvider using the ProviderData
|
||||
func NewKeycloakOIDCProvider(p *ProviderData) *KeycloakOIDCProvider {
|
||||
func NewKeycloakOIDCProvider(p *ProviderData, opts options.KeycloakOptions) *KeycloakOIDCProvider {
|
||||
p.ProviderName = keycloakOIDCProviderName
|
||||
return &KeycloakOIDCProvider{
|
||||
|
||||
provider := &KeycloakOIDCProvider{
|
||||
OIDCProvider: &OIDCProvider{
|
||||
ProviderData: p,
|
||||
},
|
||||
}
|
||||
|
||||
provider.addAllowedRoles(opts.Roles)
|
||||
return provider
|
||||
}
|
||||
|
||||
var _ Provider = (*KeycloakOIDCProvider)(nil)
|
||||
|
||||
// AddAllowedRoles sets Keycloak roles that are authorized.
|
||||
// addAllowedRoles sets Keycloak roles that are authorized.
|
||||
// Assumes `SetAllowedGroups` is already called on groups and appends to that
|
||||
// with `role:` prefixed roles.
|
||||
func (p *KeycloakOIDCProvider) AddAllowedRoles(roles []string) {
|
||||
func (p *KeycloakOIDCProvider) addAllowedRoles(roles []string) {
|
||||
if p.AllowedGroups == nil {
|
||||
p.AllowedGroups = make(map[string]struct{})
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
|
||||
"github.com/coreos/go-oidc/v3/oidc"
|
||||
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
@ -39,11 +40,11 @@ func getAccessToken() string {
|
||||
|
||||
func newTestKeycloakOIDCSetup() (*httptest.Server, *KeycloakOIDCProvider) {
|
||||
redeemURL, server := newOIDCServer([]byte(fmt.Sprintf(`{"email": "new@thing.com", "expires_in": 300, "access_token": "%v"}`, getAccessToken())))
|
||||
provider := newKeycloakOIDCProvider(redeemURL)
|
||||
provider := newKeycloakOIDCProvider(redeemURL, options.KeycloakOptions{})
|
||||
return server, provider
|
||||
}
|
||||
|
||||
func newKeycloakOIDCProvider(serverURL *url.URL) *KeycloakOIDCProvider {
|
||||
func newKeycloakOIDCProvider(serverURL *url.URL, opts options.KeycloakOptions) *KeycloakOIDCProvider {
|
||||
verificationOptions := &internaloidc.IDTokenVerificationOptions{
|
||||
AudienceClaims: []string{defaultAudienceClaim},
|
||||
ClientID: mockClientID,
|
||||
@ -66,7 +67,8 @@ func newKeycloakOIDCProvider(serverURL *url.URL) *KeycloakOIDCProvider {
|
||||
Scheme: "https",
|
||||
Host: "keycloak-oidc.com",
|
||||
Path: "/api/v3/user"},
|
||||
Scope: "openid email profile"})
|
||||
Scope: "openid email profile"},
|
||||
opts)
|
||||
|
||||
if serverURL != nil {
|
||||
p.RedeemURL.Scheme = serverURL.Scheme
|
||||
@ -88,7 +90,7 @@ func newKeycloakOIDCProvider(serverURL *url.URL) *KeycloakOIDCProvider {
|
||||
var _ = Describe("Keycloak OIDC Provider Tests", func() {
|
||||
Context("New Provider Init", func() {
|
||||
It("creates new keycloak oidc provider with expected defaults", func() {
|
||||
p := newKeycloakOIDCProvider(nil)
|
||||
p := newKeycloakOIDCProvider(nil, options.KeycloakOptions{})
|
||||
providerData := p.Data()
|
||||
Expect(providerData.ProviderName).To(Equal(keycloakOIDCProviderName))
|
||||
Expect(providerData.LoginURL.String()).To(Equal("https://keycloak-oidc.com/oauth/auth"))
|
||||
@ -101,8 +103,9 @@ var _ = Describe("Keycloak OIDC Provider Tests", func() {
|
||||
|
||||
Context("Allowed Roles", func() {
|
||||
It("should prefix allowed roles and add them to groups", func() {
|
||||
p := newKeycloakOIDCProvider(nil)
|
||||
p.AddAllowedRoles([]string{"admin", "editor"})
|
||||
p := newKeycloakOIDCProvider(nil, options.KeycloakOptions{
|
||||
Roles: []string{"admin", "editor"},
|
||||
})
|
||||
Expect(p.AllowedGroups).To(HaveKey("role:admin"))
|
||||
Expect(p.AllowedGroups).To(HaveKey("role:editor"))
|
||||
})
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/ginkgo/extensions/table"
|
||||
@ -27,7 +28,8 @@ func testKeycloakProvider(backend *httptest.Server) (*KeycloakProvider, error) {
|
||||
RedeemURL: &url.URL{},
|
||||
ProfileURL: &url.URL{},
|
||||
ValidateURL: &url.URL{},
|
||||
Scope: ""})
|
||||
Scope: ""},
|
||||
options.KeycloakOptions{})
|
||||
|
||||
if backend != nil {
|
||||
bURL, err := url.Parse(backend.URL)
|
||||
@ -48,7 +50,7 @@ func testKeycloakProvider(backend *httptest.Server) (*KeycloakProvider, error) {
|
||||
var _ = Describe("Keycloak Provider Tests", func() {
|
||||
Context("New Provider Init", func() {
|
||||
It("uses defaults", func() {
|
||||
providerData := NewKeycloakProvider(&ProviderData{}).Data()
|
||||
providerData := NewKeycloakProvider(&ProviderData{}, options.KeycloakOptions{}).Data()
|
||||
Expect(providerData.ProviderName).To(Equal("Keycloak"))
|
||||
Expect(providerData.LoginURL.String()).To(Equal("https://keycloak.org/oauth/authorize"))
|
||||
Expect(providerData.RedeemURL.String()).To(Equal("https://keycloak.org/oauth/token"))
|
||||
@ -76,7 +78,8 @@ var _ = Describe("Keycloak Provider Tests", func() {
|
||||
Scheme: "https",
|
||||
Host: "example.com",
|
||||
Path: "/api/v3/user"},
|
||||
Scope: "profile"})
|
||||
Scope: "profile"},
|
||||
options.KeycloakOptions{})
|
||||
providerData := p.Data()
|
||||
|
||||
Expect(providerData.ProviderName).To(Equal("Keycloak"))
|
||||
|
@ -5,12 +5,15 @@ import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt"
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/requests"
|
||||
"gopkg.in/square/go-jose.v2"
|
||||
@ -78,7 +81,7 @@ var (
|
||||
)
|
||||
|
||||
// NewLoginGovProvider initiates a new LoginGovProvider
|
||||
func NewLoginGovProvider(p *ProviderData) *LoginGovProvider {
|
||||
func NewLoginGovProvider(p *ProviderData, opts options.LoginGovOptions) (*LoginGovProvider, error) {
|
||||
p.setProviderDefaults(providerDefaults{
|
||||
name: loginGovProviderName,
|
||||
loginURL: loginGovDefaultLoginURL,
|
||||
@ -87,10 +90,50 @@ func NewLoginGovProvider(p *ProviderData) *LoginGovProvider {
|
||||
validateURL: loginGovDefaultProfileURL,
|
||||
scope: loginGovDefaultScope,
|
||||
})
|
||||
return &LoginGovProvider{
|
||||
provider := &LoginGovProvider{
|
||||
ProviderData: p,
|
||||
Nonce: randSeq(32),
|
||||
}
|
||||
|
||||
if err := provider.configure(opts); err != nil {
|
||||
return nil, fmt.Errorf("could not configure login.gov provider: %v", err)
|
||||
}
|
||||
return provider, nil
|
||||
}
|
||||
|
||||
func (p *LoginGovProvider) configure(opts options.LoginGovOptions) error {
|
||||
pubJWKURL, err := url.Parse(opts.PubJWKURL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not parse Public JWK URL: %v", err)
|
||||
}
|
||||
p.PubJWKURL = pubJWKURL
|
||||
|
||||
// JWT key can be supplied via env variable or file in the filesystem, but not both.
|
||||
switch {
|
||||
case opts.JWTKey != "" && opts.JWTKeyFile != "":
|
||||
return errors.New("cannot set both jwt-key and jwt-key-file options")
|
||||
case opts.JWTKey == "" && opts.JWTKeyFile == "":
|
||||
return errors.New("login.gov provider requires a private key for signing JWTs")
|
||||
case opts.JWTKey != "":
|
||||
// The JWT Key is in the commandline argument
|
||||
signKey, err := jwt.ParseRSAPrivateKeyFromPEM([]byte(opts.JWTKey))
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not parse RSA Private Key PEM: %v", err)
|
||||
}
|
||||
p.JWTKey = signKey
|
||||
case opts.JWTKeyFile != "":
|
||||
// The JWT key is in the filesystem
|
||||
keyData, err := ioutil.ReadFile(opts.JWTKeyFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not read key file: %v", opts.JWTKeyFile)
|
||||
}
|
||||
signKey, err := jwt.ParseRSAPrivateKeyFromPEM(keyData)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not parse private key from PEM file: %v", opts.JWTKeyFile)
|
||||
}
|
||||
p.JWTKey = signKey
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type loginGovCustomClaims struct {
|
||||
|
@ -1,11 +1,14 @@
|
||||
package providers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
@ -13,6 +16,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt"
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/square/go-jose.v2"
|
||||
@ -32,12 +36,35 @@ func newLoginGovServer(body []byte) (*url.URL, *httptest.Server) {
|
||||
return u, s
|
||||
}
|
||||
|
||||
func newLoginGovProvider() (l *LoginGovProvider, serverKey *MyKeyData, err error) {
|
||||
func newPrivateKeyBytes() ([]byte, error) {
|
||||
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
keyBytes, err := x509.MarshalPKCS8PrivateKey(privateKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
privateKeyBlock := &pem.Block{
|
||||
Type: "RSA PRIVATE KEY",
|
||||
Bytes: keyBytes,
|
||||
}
|
||||
b := &bytes.Buffer{}
|
||||
if err := pem.Encode(b, privateKeyBlock); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return b.Bytes(), nil
|
||||
}
|
||||
|
||||
func newLoginGovProvider() (*LoginGovProvider, *MyKeyData, error) {
|
||||
key, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
return
|
||||
return nil, nil, err
|
||||
}
|
||||
serverKey = &MyKeyData{
|
||||
serverKey := &MyKeyData{
|
||||
PubKey: key.Public(),
|
||||
PrivKey: key,
|
||||
PubJWK: jose.JSONWebKey{
|
||||
@ -48,29 +75,40 @@ func newLoginGovProvider() (l *LoginGovProvider, serverKey *MyKeyData, err error
|
||||
},
|
||||
}
|
||||
|
||||
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
privKey, err := newPrivateKeyBytes()
|
||||
if err != nil {
|
||||
return
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
l = NewLoginGovProvider(
|
||||
l, err := NewLoginGovProvider(
|
||||
&ProviderData{
|
||||
ProviderName: "",
|
||||
LoginURL: &url.URL{},
|
||||
RedeemURL: &url.URL{},
|
||||
ProfileURL: &url.URL{},
|
||||
ValidateURL: &url.URL{},
|
||||
Scope: ""})
|
||||
l.JWTKey = privateKey
|
||||
Scope: ""},
|
||||
options.LoginGovOptions{
|
||||
JWTKey: string(privKey),
|
||||
},
|
||||
)
|
||||
l.Nonce = "fakenonce"
|
||||
return
|
||||
return l, serverKey, err
|
||||
}
|
||||
|
||||
func TestNewLoginGovProvider(t *testing.T) {
|
||||
g := NewWithT(t)
|
||||
|
||||
privKey, err := newPrivateKeyBytes()
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
// Test that defaults are set when calling for a new provider with nothing set
|
||||
providerData := NewLoginGovProvider(&ProviderData{}).Data()
|
||||
provider, err := NewLoginGovProvider(&ProviderData{}, options.LoginGovOptions{
|
||||
JWTKey: string(privKey),
|
||||
})
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
providerData := provider.Data()
|
||||
g.Expect(providerData.ProviderName).To(Equal("login.gov"))
|
||||
g.Expect(providerData.LoginURL.String()).To(Equal("https://secure.login.gov/openid_connect/authorize"))
|
||||
g.Expect(providerData.RedeemURL.String()).To(Equal("https://secure.login.gov/api/openid_connect/token"))
|
||||
@ -80,7 +118,10 @@ func TestNewLoginGovProvider(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLoginGovProviderOverrides(t *testing.T) {
|
||||
p := NewLoginGovProvider(
|
||||
privKey, err := newPrivateKeyBytes()
|
||||
assert.NoError(t, err)
|
||||
|
||||
p, err := NewLoginGovProvider(
|
||||
&ProviderData{
|
||||
LoginURL: &url.URL{
|
||||
Scheme: "https",
|
||||
@ -94,7 +135,11 @@ func TestLoginGovProviderOverrides(t *testing.T) {
|
||||
Scheme: "https",
|
||||
Host: "example.com",
|
||||
Path: "/oauth/profile"},
|
||||
Scope: "profile"})
|
||||
Scope: "profile"},
|
||||
options.LoginGovOptions{
|
||||
JWTKey: string(privKey),
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.NotEqual(t, nil, p)
|
||||
assert.Equal(t, "login.gov", p.Data().ProviderName)
|
||||
assert.Equal(t, "https://example.com/oauth/auth",
|
||||
|
@ -1,5 +1,7 @@
|
||||
package providers
|
||||
|
||||
import "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
|
||||
|
||||
// NextcloudProvider represents an Nextcloud based Identity Provider
|
||||
type NextcloudProvider struct {
|
||||
*ProviderData
|
||||
@ -13,7 +15,7 @@ const nextCloudProviderName = "Nextcloud"
|
||||
func NewNextcloudProvider(p *ProviderData) *NextcloudProvider {
|
||||
p.ProviderName = nextCloudProviderName
|
||||
p.getAuthorizationHeaderFunc = makeOIDCHeader
|
||||
if p.EmailClaim == OIDCEmailClaim {
|
||||
if p.EmailClaim == options.OIDCEmailClaim {
|
||||
// This implies the email claim has not been overridden, we should set a default
|
||||
// for this provider
|
||||
p.EmailClaim = "ocs.data.email"
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger"
|
||||
"golang.org/x/oauth2"
|
||||
@ -20,13 +21,13 @@ type OIDCProvider struct {
|
||||
}
|
||||
|
||||
// NewOIDCProvider initiates a new OIDCProvider
|
||||
func NewOIDCProvider(p *ProviderData) *OIDCProvider {
|
||||
func NewOIDCProvider(p *ProviderData, opts options.OIDCOptions) *OIDCProvider {
|
||||
p.ProviderName = "OpenID Connect"
|
||||
p.getAuthorizationHeaderFunc = makeOIDCHeader
|
||||
|
||||
return &OIDCProvider{
|
||||
ProviderData: p,
|
||||
SkipNonce: true,
|
||||
SkipNonce: opts.InsecureSkipNonce,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/coreos/go-oidc/v3/oidc"
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/encryption"
|
||||
internaloidc "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/oidc"
|
||||
@ -25,7 +26,7 @@ type redeemTokenResponse struct {
|
||||
IDToken string `json:"id_token,omitempty"`
|
||||
}
|
||||
|
||||
func newOIDCProvider(serverURL *url.URL) *OIDCProvider {
|
||||
func newOIDCProvider(serverURL *url.URL, skipNonce bool) *OIDCProvider {
|
||||
verificationOptions := &internaloidc.IDTokenVerificationOptions{
|
||||
AudienceClaims: []string{"aud"},
|
||||
ClientID: "https://test.myapp.com",
|
||||
@ -61,7 +62,9 @@ func newOIDCProvider(serverURL *url.URL) *OIDCProvider {
|
||||
), verificationOptions),
|
||||
}
|
||||
|
||||
p := NewOIDCProvider(providerData)
|
||||
p := NewOIDCProvider(providerData, options.OIDCOptions{
|
||||
InsecureSkipNonce: skipNonce,
|
||||
})
|
||||
|
||||
return p
|
||||
}
|
||||
@ -77,7 +80,7 @@ func newOIDCServer(body []byte) (*url.URL, *httptest.Server) {
|
||||
|
||||
func newTestOIDCSetup(body []byte) (*httptest.Server, *OIDCProvider) {
|
||||
redeemURL, server := newOIDCServer(body)
|
||||
provider := newOIDCProvider(redeemURL)
|
||||
provider := newOIDCProvider(redeemURL, false)
|
||||
return server, provider
|
||||
}
|
||||
|
||||
@ -86,7 +89,7 @@ func TestOIDCProviderGetLoginURL(t *testing.T) {
|
||||
Scheme: "https",
|
||||
Host: "oauth2proxy.oidctest",
|
||||
}
|
||||
provider := newOIDCProvider(serverURL)
|
||||
provider := newOIDCProvider(serverURL, true)
|
||||
|
||||
n, err := encryption.Nonce()
|
||||
assert.NoError(t, err)
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/coreos/go-oidc/v3/oidc"
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger"
|
||||
internaloidc "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/oidc"
|
||||
@ -18,14 +19,10 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
OIDCEmailClaim = "email"
|
||||
OIDCGroupsClaim = "groups"
|
||||
// This is not exported as it's not currently user configurable
|
||||
oidcUserClaim = "sub"
|
||||
)
|
||||
|
||||
var OIDCAudienceClaims = []string{"aud"}
|
||||
|
||||
// ProviderData contains information required to configure all implementations
|
||||
// of OAuth2 providers
|
||||
type ProviderData struct {
|
||||
@ -76,9 +73,9 @@ func (p *ProviderData) GetClientSecret() (clientSecret string, err error) {
|
||||
return string(fileClientSecret), nil
|
||||
}
|
||||
|
||||
// SetAllowedGroups organizes a group list into the AllowedGroups map
|
||||
// setAllowedGroups organizes a group list into the AllowedGroups map
|
||||
// to be consumed by Authorize implementations
|
||||
func (p *ProviderData) SetAllowedGroups(groups []string) {
|
||||
func (p *ProviderData) setAllowedGroups(groups []string) {
|
||||
p.AllowedGroups = make(map[string]struct{}, len(groups))
|
||||
for _, group := range groups {
|
||||
p.AllowedGroups[group] = struct{}{}
|
||||
@ -172,7 +169,7 @@ func (p *ProviderData) buildSessionFromClaims(rawIDToken, accessToken string) (*
|
||||
|
||||
// `email_verified` must be present and explicitly set to `false` to be
|
||||
// considered unverified.
|
||||
verifyEmail := (p.EmailClaim == OIDCEmailClaim) && !p.AllowUnverifiedEmail
|
||||
verifyEmail := (p.EmailClaim == options.OIDCEmailClaim) && !p.AllowUnverifiedEmail
|
||||
|
||||
var verified bool
|
||||
exists, err := extractor.GetClaimInto("email_verified", &verified)
|
||||
|
@ -107,7 +107,7 @@ func TestProviderDataAuthorize(t *testing.T) {
|
||||
Groups: tc.groups,
|
||||
}
|
||||
p := &ProviderData{}
|
||||
p.SetAllowedGroups(tc.allowedGroups)
|
||||
p.setAllowedGroups(tc.allowedGroups)
|
||||
|
||||
authorized, err := p.Authorize(context.Background(), session)
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
|
@ -2,8 +2,18 @@ package providers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/coreos/go-oidc/v3/oidc"
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger"
|
||||
internaloidc "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/oidc"
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/requests"
|
||||
k8serrors "k8s.io/apimachinery/pkg/util/errors"
|
||||
)
|
||||
|
||||
// Provider represents an upstream identity provider implementation
|
||||
@ -20,38 +30,247 @@ type Provider interface {
|
||||
CreateSessionFromToken(ctx context.Context, token string) (*sessions.SessionState, error)
|
||||
}
|
||||
|
||||
// New provides a new Provider based on the configured provider string
|
||||
func New(provider string, p *ProviderData) Provider {
|
||||
switch provider {
|
||||
case "linkedin":
|
||||
return NewLinkedInProvider(p)
|
||||
case "facebook":
|
||||
return NewFacebookProvider(p)
|
||||
case "github":
|
||||
return NewGitHubProvider(p)
|
||||
case "keycloak":
|
||||
return NewKeycloakProvider(p)
|
||||
case "keycloak-oidc":
|
||||
return NewKeycloakOIDCProvider(p)
|
||||
case "azure":
|
||||
return NewAzureProvider(p)
|
||||
case "adfs":
|
||||
return NewADFSProvider(p)
|
||||
case "gitlab":
|
||||
return NewGitLabProvider(p)
|
||||
case "oidc":
|
||||
return NewOIDCProvider(p)
|
||||
case "login.gov":
|
||||
return NewLoginGovProvider(p)
|
||||
case "bitbucket":
|
||||
return NewBitbucketProvider(p)
|
||||
case "nextcloud":
|
||||
return NewNextcloudProvider(p)
|
||||
case "digitalocean":
|
||||
return NewDigitalOceanProvider(p)
|
||||
case "google":
|
||||
return NewGoogleProvider(p)
|
||||
func NewProvider(providerConfig options.Provider) (Provider, error) {
|
||||
providerData, err := newProviderDataFromConfig(providerConfig)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not create provider data: %v", err)
|
||||
}
|
||||
switch providerConfig.Type {
|
||||
case options.ADFSProvider:
|
||||
return NewADFSProvider(providerData, providerConfig.ADFSConfig), nil
|
||||
case options.AzureProvider:
|
||||
return NewAzureProvider(providerData, providerConfig.AzureConfig), nil
|
||||
case options.BitbucketProvider:
|
||||
return NewBitbucketProvider(providerData, providerConfig.BitbucketConfig), nil
|
||||
case options.DigitalOceanProvider:
|
||||
return NewDigitalOceanProvider(providerData), nil
|
||||
case options.FacebookProvider:
|
||||
return NewFacebookProvider(providerData), nil
|
||||
case options.GitHubProvider:
|
||||
return NewGitHubProvider(providerData, providerConfig.GitHubConfig), nil
|
||||
case options.GitLabProvider:
|
||||
return NewGitLabProvider(providerData, providerConfig.GitLabConfig)
|
||||
case options.GoogleProvider:
|
||||
return NewGoogleProvider(providerData, providerConfig.GoogleConfig)
|
||||
case options.KeycloakProvider:
|
||||
return NewKeycloakProvider(providerData, providerConfig.KeycloakConfig), nil
|
||||
case options.KeycloakOIDCProvider:
|
||||
return NewKeycloakOIDCProvider(providerData, providerConfig.KeycloakConfig), nil
|
||||
case options.LinkedInProvider:
|
||||
return NewLinkedInProvider(providerData), nil
|
||||
case options.LoginGovProvider:
|
||||
return NewLoginGovProvider(providerData, providerConfig.LoginGovConfig)
|
||||
case options.NextCloudProvider:
|
||||
return NewNextcloudProvider(providerData), nil
|
||||
case options.OIDCProvider:
|
||||
return NewOIDCProvider(providerData, providerConfig.OIDCConfig), nil
|
||||
default:
|
||||
return nil
|
||||
return nil, fmt.Errorf("unknown provider type %q", providerConfig.Type)
|
||||
}
|
||||
}
|
||||
|
||||
func newProviderDataFromConfig(providerConfig options.Provider) (*ProviderData, error) {
|
||||
p := &ProviderData{
|
||||
Scope: providerConfig.Scope,
|
||||
ClientID: providerConfig.ClientID,
|
||||
ClientSecret: providerConfig.ClientSecret,
|
||||
ClientSecretFile: providerConfig.ClientSecretFile,
|
||||
Prompt: providerConfig.Prompt,
|
||||
ApprovalPrompt: providerConfig.ApprovalPrompt,
|
||||
AcrValues: providerConfig.AcrValues,
|
||||
}
|
||||
|
||||
needsVerifier, err := providerRequiresOIDCProviderVerifier(providerConfig.Type)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if needsVerifier {
|
||||
oidcProvider, verifier, err := newOIDCProviderVerifier(providerConfig)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error setting OIDC configuration: %v", err)
|
||||
}
|
||||
|
||||
p.Verifier = verifier
|
||||
if oidcProvider != nil {
|
||||
// Use the discovered values rather than any specified values
|
||||
providerConfig.LoginURL = oidcProvider.Endpoint().AuthURL
|
||||
providerConfig.RedeemURL = oidcProvider.Endpoint().TokenURL
|
||||
}
|
||||
}
|
||||
|
||||
errs := []error{}
|
||||
for name, u := range map[string]struct {
|
||||
dst *url.URL
|
||||
raw string
|
||||
}{
|
||||
"login": {dst: p.LoginURL, raw: providerConfig.LoginURL},
|
||||
"redeem": {dst: p.RedeemURL, raw: providerConfig.RedeemURL},
|
||||
"profile": {dst: p.ProfileURL, raw: providerConfig.ProfileURL},
|
||||
"validate": {dst: p.ValidateURL, raw: providerConfig.ValidateURL},
|
||||
"resource": {dst: p.ProtectedResource, raw: providerConfig.ProtectedResource},
|
||||
} {
|
||||
var err error
|
||||
u.dst, err = url.Parse(u.raw)
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf("could not parse %s URL: %v", name, err))
|
||||
}
|
||||
}
|
||||
if len(errs) > 0 {
|
||||
return nil, k8serrors.NewAggregate(errs)
|
||||
}
|
||||
|
||||
// Make the OIDC options available to all providers that support it
|
||||
p.AllowUnverifiedEmail = providerConfig.OIDCConfig.InsecureAllowUnverifiedEmail
|
||||
p.EmailClaim = providerConfig.OIDCConfig.EmailClaim
|
||||
p.GroupsClaim = providerConfig.OIDCConfig.GroupsClaim
|
||||
|
||||
// TODO (@NickMeves) - Remove This
|
||||
// Backwards Compatibility for Deprecated UserIDClaim option
|
||||
if providerConfig.OIDCConfig.EmailClaim == options.OIDCEmailClaim &&
|
||||
providerConfig.OIDCConfig.UserIDClaim != options.OIDCEmailClaim {
|
||||
p.EmailClaim = providerConfig.OIDCConfig.UserIDClaim
|
||||
}
|
||||
|
||||
if providerConfig.Scope == "" {
|
||||
providerConfig.Scope = "openid email profile"
|
||||
|
||||
if len(providerConfig.AllowedGroups) > 0 {
|
||||
providerConfig.Scope += " groups"
|
||||
}
|
||||
}
|
||||
if providerConfig.OIDCConfig.UserIDClaim == "" {
|
||||
providerConfig.OIDCConfig.UserIDClaim = "email"
|
||||
}
|
||||
|
||||
p.setAllowedGroups(providerConfig.AllowedGroups)
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func providerRequiresOIDCProviderVerifier(providerType options.ProviderType) (bool, error) {
|
||||
switch providerType {
|
||||
case options.BitbucketProvider, options.DigitalOceanProvider, options.FacebookProvider, options.GitHubProvider,
|
||||
options.GoogleProvider, options.KeycloakProvider, options.LinkedInProvider, options.LoginGovProvider, options.NextCloudProvider:
|
||||
return false, nil
|
||||
case options.ADFSProvider, options.AzureProvider, options.GitLabProvider, options.KeycloakOIDCProvider, options.OIDCProvider:
|
||||
return true, nil
|
||||
default:
|
||||
return false, fmt.Errorf("unknown provider type: %s", providerType)
|
||||
}
|
||||
}
|
||||
|
||||
func newOIDCProviderVerifier(providerConfig options.Provider) (*oidc.Provider, *internaloidc.IDTokenVerifier, error) {
|
||||
// If the issuer isn't set, default it for platforms where it makes sense
|
||||
if providerConfig.OIDCConfig.IssuerURL == "" {
|
||||
switch providerConfig.Type {
|
||||
case "gitlab":
|
||||
providerConfig.OIDCConfig.IssuerURL = "https://gitlab.com"
|
||||
case "oidc":
|
||||
return nil, nil, errors.New("missing required setting: OIDC Issuer URL cannot be empty")
|
||||
}
|
||||
}
|
||||
|
||||
switch {
|
||||
case providerConfig.OIDCConfig.InsecureSkipIssuerVerification && !providerConfig.OIDCConfig.SkipDiscovery:
|
||||
verifier, err := newInsecureSkipIssuerVerificationOIDCVerifier(providerConfig)
|
||||
return nil, verifier, err
|
||||
case providerConfig.OIDCConfig.SkipDiscovery:
|
||||
verifier, err := newSkipDiscoveryOIDCVerifier(providerConfig)
|
||||
return nil, verifier, err
|
||||
default:
|
||||
return newDiscoveryOIDCProviderVerifier(providerConfig)
|
||||
}
|
||||
}
|
||||
|
||||
func newDiscoveryOIDCProviderVerifier(providerConfig options.Provider) (*oidc.Provider, *internaloidc.IDTokenVerifier, error) {
|
||||
// Configure discoverable provider data.
|
||||
provider, err := oidc.NewProvider(context.TODO(), providerConfig.OIDCConfig.IssuerURL)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
verificationOptions := &internaloidc.IDTokenVerificationOptions{
|
||||
AudienceClaims: providerConfig.OIDCConfig.AudienceClaims,
|
||||
ClientID: providerConfig.ClientID,
|
||||
ExtraAudiences: providerConfig.OIDCConfig.ExtraAudiences,
|
||||
}
|
||||
verifier := internaloidc.NewVerifier(provider.Verifier(&oidc.Config{
|
||||
ClientID: providerConfig.ClientID,
|
||||
SkipIssuerCheck: providerConfig.OIDCConfig.InsecureSkipIssuerVerification,
|
||||
SkipClientIDCheck: true, // client id check is done within oauth2-proxy: IDTokenVerifier.Verify
|
||||
}), verificationOptions)
|
||||
|
||||
return provider, verifier, nil
|
||||
}
|
||||
|
||||
func newInsecureSkipIssuerVerificationOIDCVerifier(providerConfig options.Provider) (*internaloidc.IDTokenVerifier, error) {
|
||||
// go-oidc doesn't let us pass bypass the issuer check this in the oidc.NewProvider call
|
||||
// (which uses discovery to get the URLs), so we'll do a quick check ourselves and if
|
||||
// we get the URLs, we'll just use the non-discovery path.
|
||||
|
||||
logger.Printf("Performing OIDC Discovery...")
|
||||
|
||||
requestURL := strings.TrimSuffix(providerConfig.OIDCConfig.IssuerURL, "/") + "/.well-known/openid-configuration"
|
||||
body, err := requests.New(requestURL).
|
||||
Do().
|
||||
UnmarshalJSON()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to discover OIDC configuration: %v", err)
|
||||
}
|
||||
|
||||
// Prefer manually configured URLs. It's a bit unclear
|
||||
// why you'd be doing discovery and also providing the URLs
|
||||
// explicitly though...
|
||||
if providerConfig.LoginURL == "" {
|
||||
providerConfig.LoginURL = body.Get("authorization_endpoint").MustString()
|
||||
}
|
||||
|
||||
if providerConfig.RedeemURL == "" {
|
||||
providerConfig.RedeemURL = body.Get("token_endpoint").MustString()
|
||||
}
|
||||
|
||||
if providerConfig.OIDCConfig.JwksURL == "" {
|
||||
providerConfig.OIDCConfig.JwksURL = body.Get("jwks_uri").MustString()
|
||||
}
|
||||
|
||||
if providerConfig.ProfileURL == "" {
|
||||
providerConfig.ProfileURL = body.Get("userinfo_endpoint").MustString()
|
||||
}
|
||||
|
||||
// Now we have performed the discovery, construct the verifier manually
|
||||
return newSkipDiscoveryOIDCVerifier(providerConfig)
|
||||
}
|
||||
|
||||
func newSkipDiscoveryOIDCVerifier(providerConfig options.Provider) (*internaloidc.IDTokenVerifier, error) {
|
||||
var errs []error
|
||||
|
||||
// Construct a manual IDTokenVerifier from issuer URL & JWKS URI
|
||||
// instead of metadata discovery if we enable -skip-oidc-discovery.
|
||||
// In this case we need to make sure the required endpoints for
|
||||
// the provider are configured.
|
||||
if providerConfig.LoginURL == "" {
|
||||
errs = append(errs, errors.New("missing required setting: login-url"))
|
||||
}
|
||||
if providerConfig.RedeemURL == "" {
|
||||
errs = append(errs, errors.New("missing required setting: redeem-url"))
|
||||
}
|
||||
if providerConfig.OIDCConfig.JwksURL == "" {
|
||||
errs = append(errs, errors.New("missing required setting: oidc-jwks-url"))
|
||||
}
|
||||
if len(errs) > 0 {
|
||||
return nil, k8serrors.NewAggregate(errs)
|
||||
}
|
||||
|
||||
keySet := oidc.NewRemoteKeySet(context.TODO(), providerConfig.OIDCConfig.JwksURL)
|
||||
verificationOptions := &internaloidc.IDTokenVerificationOptions{
|
||||
AudienceClaims: providerConfig.OIDCConfig.AudienceClaims,
|
||||
ClientID: providerConfig.ClientID,
|
||||
ExtraAudiences: providerConfig.OIDCConfig.ExtraAudiences,
|
||||
}
|
||||
verifier := internaloidc.NewVerifier(oidc.NewVerifier(providerConfig.OIDCConfig.IssuerURL, keySet, &oidc.Config{
|
||||
ClientID: providerConfig.ClientID,
|
||||
SkipIssuerCheck: providerConfig.OIDCConfig.InsecureSkipIssuerVerification,
|
||||
SkipClientIDCheck: true, // client id check is done within oauth2-proxy: IDTokenVerifier.Verify
|
||||
}), verificationOptions)
|
||||
return verifier, nil
|
||||
}
|
||||
|
93
providers/providers_test.go
Normal file
93
providers/providers_test.go
Normal file
@ -0,0 +1,93 @@
|
||||
package providers
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
const (
|
||||
clientID = "bazquux"
|
||||
clientSecret = "xyzzyplugh"
|
||||
providerID = "providerID"
|
||||
)
|
||||
|
||||
func TestClientSecretFileOptionFails(t *testing.T) {
|
||||
g := NewWithT(t)
|
||||
|
||||
providerConfig := options.Provider{
|
||||
ID: providerID,
|
||||
Type: "google",
|
||||
ClientID: clientID,
|
||||
ClientSecretFile: clientSecret,
|
||||
}
|
||||
|
||||
p, err := newProviderDataFromConfig(providerConfig)
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
g.Expect(p.ClientSecretFile).To(Equal(clientSecret))
|
||||
g.Expect(p.ClientSecret).To(BeEmpty())
|
||||
|
||||
s, err := p.GetClientSecret()
|
||||
g.Expect(err).To(HaveOccurred())
|
||||
g.Expect(s).To(BeEmpty())
|
||||
}
|
||||
|
||||
func TestClientSecretFileOption(t *testing.T) {
|
||||
g := NewWithT(t)
|
||||
|
||||
f, err := ioutil.TempFile("", "client_secret_temp_file_")
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
clientSecretFileName := f.Name()
|
||||
|
||||
defer func() {
|
||||
g.Expect(f.Close()).To(Succeed())
|
||||
g.Expect(os.Remove(clientSecretFileName)).To(Succeed())
|
||||
}()
|
||||
|
||||
_, err = f.WriteString("testcase")
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
providerConfig := options.Provider{
|
||||
ID: providerID,
|
||||
Type: "google",
|
||||
ClientID: clientID,
|
||||
ClientSecretFile: clientSecretFileName,
|
||||
}
|
||||
|
||||
p, err := newProviderDataFromConfig(providerConfig)
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
g.Expect(p.ClientSecretFile).To(Equal(clientSecretFileName))
|
||||
g.Expect(p.ClientSecret).To(BeEmpty())
|
||||
|
||||
s, err := p.GetClientSecret()
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
g.Expect(s).To(Equal("testcase"))
|
||||
}
|
||||
|
||||
func TestSkipOIDCDiscovery(t *testing.T) {
|
||||
g := NewWithT(t)
|
||||
providerConfig := options.Provider{
|
||||
ID: providerID,
|
||||
Type: "oidc",
|
||||
ClientID: clientID,
|
||||
ClientSecretFile: clientSecret,
|
||||
OIDCConfig: options.OIDCOptions{
|
||||
IssuerURL: "https://login.microsoftonline.com/fabrikamb2c.onmicrosoft.com/v2.0/",
|
||||
SkipDiscovery: true,
|
||||
},
|
||||
}
|
||||
|
||||
_, err := newProviderDataFromConfig(providerConfig)
|
||||
g.Expect(err).To(MatchError("error setting OIDC configuration: [missing required setting: login-url, missing required setting: redeem-url, missing required setting: oidc-jwks-url]"))
|
||||
|
||||
providerConfig.LoginURL = "https://login.microsoftonline.com/fabrikamb2c.onmicrosoft.com/oauth2/v2.0/authorize?p=b2c_1_sign_in"
|
||||
providerConfig.RedeemURL = "https://login.microsoftonline.com/fabrikamb2c.onmicrosoft.com/oauth2/v2.0/token?p=b2c_1_sign_in"
|
||||
providerConfig.OIDCConfig.JwksURL = "https://login.microsoftonline.com/fabrikamb2c.onmicrosoft.com/discovery/v2.0/keys"
|
||||
|
||||
_, err = newProviderDataFromConfig(providerConfig)
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user