mirror of
https://github.com/oauth2-proxy/oauth2-proxy.git
synced 2025-01-10 04:18:14 +02:00
25371ea4af
* implementation draft * add cfg options skip-au-when-missing && client-id-verification-claim; enhance the provider data verification logic for sake of the added options * refactor configs, added logging and add additional claim verification * simplify logic by just having one configuration similar to oidc-email-claim * added internal oidc token verifier, so that aud check behavior can be managed with oauth2-proxy and is compatible with extra-jwt-issuers * refactored verification to reduce complexity * refactored verification to reduce complexity * added docs * adjust tests to support new OIDCAudienceClaim and OIDCExtraAudiences options * extend unit tests and ensure that audience is set with the value of aud claim configuration * revert filemodes and update docs * update docs * remove unneccesary logging, refactor audience existence check and added additional unit tests * fix linting issues after rebase on origin/main * cleanup: use new imports for migrated libraries after rebase on origin/main * adapt mock in keycloak_oidc_test.go * allow specifying multiple audience claims, fixed bug where jwt issuers client id was not the being considered and fixed bug where aud claims with multiple audiences has broken the whole validation * fixed formatting issue * do not pass the whole options struct to minimize complexity and dependency to the configuration structure * added changelog entry * update docs Co-authored-by: Sofia Weiler <sofia.weiler@aoe.com> Co-authored-by: Christian Zenker <christian.zenker@aoe.com>
253 lines
7.1 KiB
Go
253 lines
7.1 KiB
Go
package main
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
|
|
. "github.com/onsi/ginkgo"
|
|
. "github.com/onsi/ginkgo/extensions/table"
|
|
. "github.com/onsi/gomega"
|
|
"github.com/spf13/pflag"
|
|
)
|
|
|
|
var _ = Describe("Configuration Loading Suite", func() {
|
|
const testLegacyConfig = `
|
|
http_address="127.0.0.1:4180"
|
|
upstreams="http://httpbin"
|
|
set_basic_auth="true"
|
|
basic_auth_password="super-secret-password"
|
|
client_id="oauth2-proxy"
|
|
client_secret="b2F1dGgyLXByb3h5LWNsaWVudC1zZWNyZXQK"
|
|
`
|
|
|
|
const testAlphaConfig = `
|
|
upstreamConfig:
|
|
proxyrawpath: false
|
|
upstreams:
|
|
- id: /
|
|
path: /
|
|
uri: http://httpbin
|
|
flushInterval: 1s
|
|
passHostHeader: true
|
|
proxyWebSockets: true
|
|
injectRequestHeaders:
|
|
- name: Authorization
|
|
values:
|
|
- claim: user
|
|
prefix: "Basic "
|
|
basicAuthPassword:
|
|
value: c3VwZXItc2VjcmV0LXBhc3N3b3Jk
|
|
- name: X-Forwarded-Groups
|
|
values:
|
|
- claim: groups
|
|
- name: X-Forwarded-User
|
|
values:
|
|
- claim: user
|
|
- name: X-Forwarded-Email
|
|
values:
|
|
- claim: email
|
|
- name: X-Forwarded-Preferred-Username
|
|
values:
|
|
- claim: preferred_username
|
|
injectResponseHeaders:
|
|
- name: Authorization
|
|
values:
|
|
- claim: user
|
|
prefix: "Basic "
|
|
basicAuthPassword:
|
|
value: c3VwZXItc2VjcmV0LXBhc3N3b3Jk
|
|
server:
|
|
bindAddress: "127.0.0.1:4180"
|
|
providers:
|
|
- provider: google
|
|
ID: google=oauth2-proxy
|
|
clientSecret: b2F1dGgyLXByb3h5LWNsaWVudC1zZWNyZXQK
|
|
clientID: oauth2-proxy
|
|
approvalPrompt: force
|
|
azureConfig:
|
|
tenant: common
|
|
oidcConfig:
|
|
groupsClaim: groups
|
|
emailClaim: email
|
|
userIDClaim: email
|
|
insecureSkipNonce: true
|
|
audienceClaims: [aud]
|
|
extraAudiences: []
|
|
`
|
|
|
|
const testCoreConfig = `
|
|
cookie_secret="OQINaROshtE9TcZkNAm-5Zs2Pv3xaWytBmc5W7sPX7w="
|
|
email_domains="example.com"
|
|
cookie_secure="false"
|
|
|
|
redirect_url="http://localhost:4180/oauth2/callback"
|
|
`
|
|
|
|
boolPtr := func(b bool) *bool {
|
|
return &b
|
|
}
|
|
|
|
durationPtr := func(d time.Duration) *options.Duration {
|
|
du := options.Duration(d)
|
|
return &du
|
|
}
|
|
|
|
testExpectedOptions := func() *options.Options {
|
|
opts, err := options.NewLegacyOptions().ToOptions()
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
opts.Cookie.Secret = "OQINaROshtE9TcZkNAm-5Zs2Pv3xaWytBmc5W7sPX7w="
|
|
opts.EmailDomains = []string{"example.com"}
|
|
opts.Cookie.Secure = false
|
|
opts.RawRedirectURL = "http://localhost:4180/oauth2/callback"
|
|
|
|
opts.UpstreamServers = options.UpstreamConfig{
|
|
Upstreams: []options.Upstream{
|
|
{
|
|
ID: "/",
|
|
Path: "/",
|
|
URI: "http://httpbin",
|
|
FlushInterval: durationPtr(options.DefaultUpstreamFlushInterval),
|
|
PassHostHeader: boolPtr(true),
|
|
ProxyWebSockets: boolPtr(true),
|
|
},
|
|
},
|
|
}
|
|
|
|
authHeader := options.Header{
|
|
Name: "Authorization",
|
|
Values: []options.HeaderValue{
|
|
{
|
|
ClaimSource: &options.ClaimSource{
|
|
Claim: "user",
|
|
Prefix: "Basic ",
|
|
BasicAuthPassword: &options.SecretSource{
|
|
Value: []byte("super-secret-password"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
opts.InjectRequestHeaders = append([]options.Header{authHeader}, opts.InjectRequestHeaders...)
|
|
opts.InjectResponseHeaders = append(opts.InjectResponseHeaders, authHeader)
|
|
|
|
opts.Providers = options.Providers{
|
|
options.Provider{
|
|
ID: "google=oauth2-proxy",
|
|
Type: "google",
|
|
ClientSecret: "b2F1dGgyLXByb3h5LWNsaWVudC1zZWNyZXQK",
|
|
ClientID: "oauth2-proxy",
|
|
AzureConfig: options.AzureOptions{
|
|
Tenant: "common",
|
|
},
|
|
OIDCConfig: options.OIDCOptions{
|
|
GroupsClaim: "groups",
|
|
EmailClaim: "email",
|
|
UserIDClaim: "email",
|
|
AudienceClaims: []string{"aud"},
|
|
ExtraAudiences: []string{},
|
|
InsecureSkipNonce: true,
|
|
},
|
|
ApprovalPrompt: "force",
|
|
},
|
|
}
|
|
return opts
|
|
}
|
|
|
|
type loadConfigurationTableInput struct {
|
|
configContent string
|
|
alphaConfigContent string
|
|
args []string
|
|
extraFlags func() *pflag.FlagSet
|
|
expectedOptions func() *options.Options
|
|
expectedErr error
|
|
}
|
|
|
|
DescribeTable("LoadConfiguration",
|
|
func(in loadConfigurationTableInput) {
|
|
var configFileName, alphaConfigFileName string
|
|
|
|
defer func() {
|
|
if configFileName != "" {
|
|
Expect(os.Remove(configFileName)).To(Succeed())
|
|
}
|
|
if alphaConfigFileName != "" {
|
|
Expect(os.Remove(alphaConfigFileName)).To(Succeed())
|
|
}
|
|
}()
|
|
|
|
if in.configContent != "" {
|
|
By("Writing the config to a temporary file", func() {
|
|
file, err := ioutil.TempFile("", "oauth2-proxy-test-config-XXXX.cfg")
|
|
Expect(err).ToNot(HaveOccurred())
|
|
defer file.Close()
|
|
|
|
configFileName = file.Name()
|
|
|
|
_, err = file.WriteString(in.configContent)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
})
|
|
}
|
|
|
|
if in.alphaConfigContent != "" {
|
|
By("Writing the config to a temporary file", func() {
|
|
file, err := ioutil.TempFile("", "oauth2-proxy-test-alpha-config-XXXX.yaml")
|
|
Expect(err).ToNot(HaveOccurred())
|
|
defer file.Close()
|
|
|
|
alphaConfigFileName = file.Name()
|
|
|
|
_, err = file.WriteString(in.alphaConfigContent)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
})
|
|
}
|
|
|
|
extraFlags := pflag.NewFlagSet("test-flagset", pflag.ExitOnError)
|
|
if in.extraFlags != nil {
|
|
extraFlags = in.extraFlags()
|
|
}
|
|
|
|
opts, err := loadConfiguration(configFileName, alphaConfigFileName, extraFlags, in.args)
|
|
if in.expectedErr != nil {
|
|
Expect(err).To(MatchError(in.expectedErr.Error()))
|
|
} else {
|
|
Expect(err).ToNot(HaveOccurred())
|
|
}
|
|
Expect(in.expectedOptions).ToNot(BeNil())
|
|
Expect(opts).To(Equal(in.expectedOptions()))
|
|
},
|
|
Entry("with legacy configuration", loadConfigurationTableInput{
|
|
configContent: testCoreConfig + testLegacyConfig,
|
|
expectedOptions: testExpectedOptions,
|
|
}),
|
|
Entry("with alpha configuration", loadConfigurationTableInput{
|
|
configContent: testCoreConfig,
|
|
alphaConfigContent: testAlphaConfig,
|
|
expectedOptions: testExpectedOptions,
|
|
}),
|
|
Entry("with bad legacy configuration", loadConfigurationTableInput{
|
|
configContent: testCoreConfig + "unknown_field=\"something\"",
|
|
expectedOptions: func() *options.Options { return nil },
|
|
expectedErr: errors.New("failed to load config: error unmarshalling config: 1 error(s) decoding:\n\n* '' has invalid keys: unknown_field"),
|
|
}),
|
|
Entry("with bad alpha configuration", loadConfigurationTableInput{
|
|
configContent: testCoreConfig,
|
|
alphaConfigContent: testAlphaConfig + ":",
|
|
expectedOptions: func() *options.Options { return nil },
|
|
expectedErr: fmt.Errorf("failed to load alpha options: error unmarshalling config: error converting YAML to JSON: yaml: line %d: did not find expected key", strings.Count(testAlphaConfig, "\n")),
|
|
}),
|
|
Entry("with alpha configuration and bad core configuration", loadConfigurationTableInput{
|
|
configContent: testCoreConfig + "unknown_field=\"something\"",
|
|
alphaConfigContent: testAlphaConfig,
|
|
expectedOptions: func() *options.Options { return nil },
|
|
expectedErr: errors.New("failed to load core options: failed to load config: error unmarshalling config: 1 error(s) decoding:\n\n* '' has invalid keys: unknown_field"),
|
|
}),
|
|
)
|
|
})
|