mirror of
https://github.com/oauth2-proxy/oauth2-proxy.git
synced 2025-03-19 21:27:58 +02:00
Merge branch 'master' into pgroudas/add-samesite-cookie-options
This commit is contained in:
commit
17d9961cb9
4
.github/CODEOWNERS
vendored
4
.github/CODEOWNERS
vendored
@ -18,3 +18,7 @@ providers/bitbucket_test.go @aledeganopix4d
|
||||
# Nextcloud provider
|
||||
providers/nextcloud.go @Ramblurr
|
||||
providers/nextcloud_test.go @Ramblurr
|
||||
|
||||
# DigitalOcean provider
|
||||
providers/digitalocean.go @kamaln7
|
||||
providers/digitalocean_test.go @kamaln7
|
||||
|
@ -5,9 +5,13 @@
|
||||
## Breaking Changes
|
||||
|
||||
## Changes since v4.1.0
|
||||
|
||||
- [#339](https://github.com/pusher/oauth2_proxy/pull/339) Add configuration for cookie 'SameSite' value. (@pgroudas)
|
||||
- [#347](https://github.com/pusher/oauth2_proxy/pull/347) Update keycloak provider configuration documentation
|
||||
- [#325](https://github.com/pusher/oauth2_proxy/pull/325) dist.sh: use sha256sum (@syscll)
|
||||
- [#179](https://github.com/pusher/oauth2_proxy/pull/179) Add Nextcloud provider (@Ramblurr)
|
||||
- [#280](https://github.com/pusher/oauth2_proxy/pull/280) whitelisted redirect domains: add support for whitelisting specific ports or allowing wildcard ports (@kamaln7)
|
||||
- [#351](https://github.com/pusher/oauth2_proxy/pull/351) Add DigitalOcean Auth provider (@kamaln7)
|
||||
|
||||
# v4.1.0
|
||||
|
||||
|
@ -17,7 +17,7 @@ _oauth2_proxy() {
|
||||
return 0
|
||||
;;
|
||||
-provider)
|
||||
COMPREPLY=( $(compgen -W "google azure facebook github keycloak gitlab linkedin login.gov" -- ${cur}) )
|
||||
COMPREPLY=( $(compgen -W "google azure facebook github keycloak gitlab linkedin login.gov digitalocean" -- ${cur}) )
|
||||
return 0
|
||||
;;
|
||||
-@(http-address|https-address|redirect-url|upstream|basic-auth-password|skip-auth-regex|flush-interval|extra-jwt-issuers|email-domain|whitelist-domain|keycloak-group|azure-tenant|bitbucket-team|bitbucket-repository|github-org|github-team|gitlab-group|google-group|google-admin-email|google-service-account-json|client-id|client_secret|banner|footer|proxy-prefix|ping-path|cookie-name|cookie-secret|cookie-domain|cookie-path|cookie-expire|cookie-refresh|cookie-samesite|redist-sentinel-master-name|redist-sentinel-connection-urls|logging-max-size|logging-max-age|logging-max-backups|standard-logging-format|request-logging-format|exclude-logging-paths|auth-logging-format|oidc-issuer-url|oidc-jwks-url|login-url|redeem-url|profile-url|resource|validate-url|scope|approval-prompt|signature-key|acr-values|jwt-key|pubjwk-url))
|
||||
|
@ -20,6 +20,7 @@ Valid providers are :
|
||||
- [LinkedIn](#linkedin-auth-provider)
|
||||
- [login.gov](#logingov-provider)
|
||||
- [Nextcloud](#nextcloud-provider)
|
||||
- [DigitalOcean](#digitalocean-auth-provider)
|
||||
|
||||
The provider can be selected using the `provider` configuration value.
|
||||
|
||||
@ -107,8 +108,9 @@ If you are using GitHub enterprise, make sure you set the following to the appro
|
||||
|
||||
### Keycloak Auth Provider
|
||||
|
||||
1. Create new client in your Keycloak with **Access Type** 'confidental'.
|
||||
2. Create a mapper with **Mapper Type** 'Group Membership'.
|
||||
1. Create new client in your Keycloak with **Access Type** 'confidental' and **Valid Redirect URIs** 'https://internal.yourcompany.com/oauth2/callback'
|
||||
2. Take note of the Secret in the credential tab of the client
|
||||
3. Create a mapper with **Mapper Type** 'Group Membership' and **Token Claim Name** 'groups'.
|
||||
|
||||
Make sure you set the following to the appropriate url:
|
||||
|
||||
@ -116,8 +118,11 @@ Make sure you set the following to the appropriate url:
|
||||
-client-id=<client you have created>
|
||||
-client-secret=<your client's secret>
|
||||
-login-url="http(s)://<keycloak host>/realms/<your realm>/protocol/openid-connect/auth"
|
||||
-redeem-url="http(s)://<keycloak host>/realms/master/<your realm>/openid-connect/auth/token"
|
||||
-validate-url="http(s)://<keycloak host>/realms/master/<your realm>/openid-connect/userinfo"
|
||||
-redeem-url="http(s)://<keycloak host>/realms/<your realm>/protocol/openid-connect/token"
|
||||
-validate-url="http(s)://<keycloak host>/realms/<your realm>/protocol/openid-connect/userinfo"
|
||||
-keycloak-group=<user_group>
|
||||
|
||||
The group management in keycloak is using a tree. If you create a group named admin in keycloak you should define the 'keycloak-group' value to /admin.
|
||||
|
||||
### GitLab Auth Provider
|
||||
|
||||
@ -316,6 +321,23 @@ to setup the client id and client secret. Your "Redirection URI" will be
|
||||
|
||||
Note: in *all* cases the validate-url will *not* have the `index.php`.
|
||||
|
||||
### DigitalOcean Auth Provider
|
||||
|
||||
1. [Create a new OAuth application](https://cloud.digitalocean.com/account/api/applications)
|
||||
* You can fill in the name, homepage, and description however you wish.
|
||||
* In the "Application callback URL" field, enter: `https://oauth-proxy/oauth2/callback`, substituting `oauth2-proxy` with the actual hostname that oauth2_proxy is running on. The URL must match oauth2_proxy's configured redirect URL.
|
||||
2. Note the Client ID and Client Secret.
|
||||
|
||||
To use the provider, pass the following options:
|
||||
|
||||
```
|
||||
--provider=digitalocean
|
||||
--client-id=<Client ID>
|
||||
--client-secret=<Client Secret>
|
||||
```
|
||||
|
||||
Alternatively, set the equivalent options in the config file. The redirect URL defaults to `https://<requested host header>/oauth2/callback`. If you need to change it, you can use the `--redirect-url` command-line option.
|
||||
|
||||
## Email Authentication
|
||||
|
||||
To authorize by email domain use `--email-domain=yourcompany.com`. To authorize individual email addresses use `--authenticated-emails-file=/path/to/file` with one email per line. To authorize all email addresses use `--email-domain=*`.
|
||||
|
@ -113,7 +113,7 @@ An example [oauth2_proxy.cfg]({{ site.gitweb }}/contrib/oauth2_proxy.cfg.example
|
||||
| `-version` | n/a | print version string | |
|
||||
| `-whitelist-domain` | string \| list | allowed domains for redirection after authentication. Prefix domain with a `.` to allow subdomains (eg `.example.com`) | |
|
||||
|
||||
Note, when using the `whitelist-domain` option, any domain prefixed with a `.` will allow any subdomain of the specified domain as a valid redirect URL.
|
||||
Note: when using the `whitelist-domain` option, any domain prefixed with a `.` will allow any subdomain of the specified domain as a valid redirect URL. By default, only empty ports are allowed. This translates to allowing the default port of the URL's protocol (80 for HTTP, 443 for HTTPS, etc.) since browsers omit them. To allow only a specific port, add it to the whitelisted domain: `example.com:8080`. To allow any port, use `*`: `example.com:*`.
|
||||
|
||||
See below for provider specific options
|
||||
|
||||
|
@ -518,6 +518,43 @@ func (p *OAuthProxy) GetRedirect(req *http.Request) (redirect string, err error)
|
||||
return
|
||||
}
|
||||
|
||||
// splitHostPort separates host and port. If the port is not valid, it returns
|
||||
// the entire input as host, and it doesn't check the validity of the host.
|
||||
// Unlike net.SplitHostPort, but per RFC 3986, it requires ports to be numeric.
|
||||
// *** taken from net/url, modified validOptionalPort() to accept ":*"
|
||||
func splitHostPort(hostport string) (host, port string) {
|
||||
host = hostport
|
||||
|
||||
colon := strings.LastIndexByte(host, ':')
|
||||
if colon != -1 && validOptionalPort(host[colon:]) {
|
||||
host, port = host[:colon], host[colon+1:]
|
||||
}
|
||||
|
||||
if strings.HasPrefix(host, "[") && strings.HasSuffix(host, "]") {
|
||||
host = host[1 : len(host)-1]
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// validOptionalPort reports whether port is either an empty string
|
||||
// or matches /^:\d*$/
|
||||
// *** taken from net/url, modified to accept ":*"
|
||||
func validOptionalPort(port string) bool {
|
||||
if port == "" || port == ":*" {
|
||||
return true
|
||||
}
|
||||
if port[0] != ':' {
|
||||
return false
|
||||
}
|
||||
for _, b := range port[1:] {
|
||||
if b < '0' || b > '9' {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// IsValidRedirect checks whether the redirect URL is whitelisted
|
||||
func (p *OAuthProxy) IsValidRedirect(redirect string) bool {
|
||||
switch {
|
||||
@ -528,11 +565,28 @@ func (p *OAuthProxy) IsValidRedirect(redirect string) bool {
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
redirectHostname := redirectURL.Hostname()
|
||||
|
||||
for _, domain := range p.whitelistDomains {
|
||||
if (redirectURL.Host == domain) || (strings.HasPrefix(domain, ".") && strings.HasSuffix(redirectURL.Host, domain)) {
|
||||
return true
|
||||
domainHostname, domainPort := splitHostPort(strings.TrimLeft(domain, "."))
|
||||
if domainHostname == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
if (redirectHostname == domainHostname) || (strings.HasPrefix(domain, ".") && strings.HasSuffix(redirectHostname, domainHostname)) {
|
||||
// the domain names match, now validate the ports
|
||||
// if the whitelisted domain's port is '*', allow all ports
|
||||
// if the whitelisted domain contains a specific port, only allow that port
|
||||
// if the whitelisted domain doesn't contain a port at all, only allow empty redirect ports ie http and https
|
||||
redirectPort := redirectURL.Port()
|
||||
if (domainPort == "*") ||
|
||||
(domainPort == redirectPort) ||
|
||||
(domainPort == "" && redirectPort == "") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
default:
|
||||
return false
|
||||
|
@ -182,49 +182,158 @@ func TestIsValidRedirect(t *testing.T) {
|
||||
opts.ClientSecret = "fgkdsgj"
|
||||
opts.CookieSecret = "ljgiogbj"
|
||||
// Should match domains that are exactly foo.bar and any subdomain of bar.foo
|
||||
opts.WhitelistDomains = []string{"foo.bar", ".bar.foo"}
|
||||
opts.WhitelistDomains = []string{
|
||||
"foo.bar",
|
||||
".bar.foo",
|
||||
"port.bar:8080",
|
||||
".sub.port.bar:8080",
|
||||
"anyport.bar:*",
|
||||
".sub.anyport.bar:*",
|
||||
}
|
||||
opts.Validate()
|
||||
|
||||
proxy := NewOAuthProxy(opts, func(string) bool { return true })
|
||||
|
||||
noRD := proxy.IsValidRedirect("")
|
||||
assert.Equal(t, false, noRD)
|
||||
testCases := []struct {
|
||||
Desc, Redirect string
|
||||
ExpectedResult bool
|
||||
}{
|
||||
{
|
||||
Desc: "noRD",
|
||||
Redirect: "",
|
||||
ExpectedResult: false,
|
||||
},
|
||||
{
|
||||
Desc: "singleSlash",
|
||||
Redirect: "/redirect",
|
||||
ExpectedResult: true,
|
||||
},
|
||||
{
|
||||
Desc: "doubleSlash",
|
||||
Redirect: "//redirect",
|
||||
ExpectedResult: false,
|
||||
},
|
||||
{
|
||||
Desc: "validHTTP",
|
||||
Redirect: "http://foo.bar/redirect",
|
||||
ExpectedResult: true,
|
||||
},
|
||||
{
|
||||
Desc: "validHTTPS",
|
||||
Redirect: "https://foo.bar/redirect",
|
||||
ExpectedResult: true,
|
||||
},
|
||||
{
|
||||
Desc: "invalidHTTPSubdomain",
|
||||
Redirect: "http://baz.foo.bar/redirect",
|
||||
ExpectedResult: false,
|
||||
},
|
||||
{
|
||||
Desc: "invalidHTTPSSubdomain",
|
||||
Redirect: "https://baz.foo.bar/redirect",
|
||||
ExpectedResult: false,
|
||||
},
|
||||
{
|
||||
Desc: "validHTTPSubdomain",
|
||||
Redirect: "http://baz.bar.foo/redirect",
|
||||
ExpectedResult: true,
|
||||
},
|
||||
{
|
||||
Desc: "validHTTPSSubdomain",
|
||||
Redirect: "https://baz.bar.foo/redirect",
|
||||
ExpectedResult: true,
|
||||
},
|
||||
{
|
||||
Desc: "validHTTPDomain",
|
||||
Redirect: "http://bar.foo/redirect",
|
||||
ExpectedResult: true,
|
||||
},
|
||||
{
|
||||
Desc: "invalidHTTP1",
|
||||
Redirect: "http://foo.bar.evil.corp/redirect",
|
||||
ExpectedResult: false,
|
||||
},
|
||||
{
|
||||
Desc: "invalidHTTPS1",
|
||||
Redirect: "https://foo.bar.evil.corp/redirect",
|
||||
ExpectedResult: false,
|
||||
},
|
||||
{
|
||||
Desc: "invalidHTTP2",
|
||||
Redirect: "http://evil.corp/redirect?rd=foo.bar",
|
||||
ExpectedResult: false,
|
||||
},
|
||||
{
|
||||
Desc: "invalidHTTPS2",
|
||||
Redirect: "https://evil.corp/redirect?rd=foo.bar",
|
||||
ExpectedResult: false,
|
||||
},
|
||||
{
|
||||
Desc: "invalidPort",
|
||||
Redirect: "https://evil.corp:3838/redirect",
|
||||
ExpectedResult: false,
|
||||
},
|
||||
{
|
||||
Desc: "invalidEmptyPort",
|
||||
Redirect: "http://foo.bar:3838/redirect",
|
||||
ExpectedResult: false,
|
||||
},
|
||||
{
|
||||
Desc: "invalidEmptyPortSubdomain",
|
||||
Redirect: "http://baz.bar.foo:3838/redirect",
|
||||
ExpectedResult: false,
|
||||
},
|
||||
{
|
||||
Desc: "validSpecificPort",
|
||||
Redirect: "http://port.bar:8080/redirect",
|
||||
ExpectedResult: true,
|
||||
},
|
||||
{
|
||||
Desc: "invalidSpecificPort",
|
||||
Redirect: "http://port.bar:3838/redirect",
|
||||
ExpectedResult: false,
|
||||
},
|
||||
{
|
||||
Desc: "validSpecificPortSubdomain",
|
||||
Redirect: "http://foo.sub.port.bar:8080/redirect",
|
||||
ExpectedResult: true,
|
||||
},
|
||||
{
|
||||
Desc: "invalidSpecificPortSubdomain",
|
||||
Redirect: "http://foo.sub.port.bar:3838/redirect",
|
||||
ExpectedResult: false,
|
||||
},
|
||||
{
|
||||
Desc: "validAnyPort1",
|
||||
Redirect: "http://anyport.bar:8080/redirect",
|
||||
ExpectedResult: true,
|
||||
},
|
||||
{
|
||||
Desc: "validAnyPort2",
|
||||
Redirect: "http://anyport.bar:8081/redirect",
|
||||
ExpectedResult: true,
|
||||
},
|
||||
{
|
||||
Desc: "validAnyPortSubdomain1",
|
||||
Redirect: "http://a.sub.anyport.bar:8080/redirect",
|
||||
ExpectedResult: true,
|
||||
},
|
||||
{
|
||||
Desc: "validAnyPortSubdomain2",
|
||||
Redirect: "http://a.sub.anyport.bar:8081/redirect",
|
||||
ExpectedResult: true,
|
||||
},
|
||||
}
|
||||
|
||||
singleSlash := proxy.IsValidRedirect("/redirect")
|
||||
assert.Equal(t, true, singleSlash)
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.Desc, func(t *testing.T) {
|
||||
result := proxy.IsValidRedirect(tc.Redirect)
|
||||
|
||||
doubleSlash := proxy.IsValidRedirect("//redirect")
|
||||
assert.Equal(t, false, doubleSlash)
|
||||
|
||||
validHTTP := proxy.IsValidRedirect("http://foo.bar/redirect")
|
||||
assert.Equal(t, true, validHTTP)
|
||||
|
||||
validHTTPS := proxy.IsValidRedirect("https://foo.bar/redirect")
|
||||
assert.Equal(t, true, validHTTPS)
|
||||
|
||||
invalidHTTPSubdomain := proxy.IsValidRedirect("http://baz.foo.bar/redirect")
|
||||
assert.Equal(t, false, invalidHTTPSubdomain)
|
||||
|
||||
invalidHTTPSSubdomain := proxy.IsValidRedirect("https://baz.foo.bar/redirect")
|
||||
assert.Equal(t, false, invalidHTTPSSubdomain)
|
||||
|
||||
validHTTPSubdomain := proxy.IsValidRedirect("http://baz.bar.foo/redirect")
|
||||
assert.Equal(t, true, validHTTPSubdomain)
|
||||
|
||||
validHTTPSSubdomain := proxy.IsValidRedirect("https://baz.bar.foo/redirect")
|
||||
assert.Equal(t, true, validHTTPSSubdomain)
|
||||
|
||||
invalidHTTP1 := proxy.IsValidRedirect("http://foo.bar.evil.corp/redirect")
|
||||
assert.Equal(t, false, invalidHTTP1)
|
||||
|
||||
invalidHTTPS1 := proxy.IsValidRedirect("https://foo.bar.evil.corp/redirect")
|
||||
assert.Equal(t, false, invalidHTTPS1)
|
||||
|
||||
invalidHTTP2 := proxy.IsValidRedirect("http://evil.corp/redirect?rd=foo.bar")
|
||||
assert.Equal(t, false, invalidHTTP2)
|
||||
|
||||
invalidHTTPS2 := proxy.IsValidRedirect("https://evil.corp/redirect?rd=foo.bar")
|
||||
assert.Equal(t, false, invalidHTTPS2)
|
||||
if result != tc.ExpectedResult {
|
||||
t.Errorf("expected %t got %t", tc.ExpectedResult, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type TestProvider struct {
|
||||
|
81
providers/digitalocean.go
Normal file
81
providers/digitalocean.go
Normal file
@ -0,0 +1,81 @@
|
||||
package providers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/pusher/oauth2_proxy/pkg/apis/sessions"
|
||||
"github.com/pusher/oauth2_proxy/pkg/requests"
|
||||
)
|
||||
|
||||
// DigitalOceanProvider represents a DigitalOcean based Identity Provider
|
||||
type DigitalOceanProvider struct {
|
||||
*ProviderData
|
||||
}
|
||||
|
||||
// NewDigitalOceanProvider initiates a new DigitalOceanProvider
|
||||
func NewDigitalOceanProvider(p *ProviderData) *DigitalOceanProvider {
|
||||
p.ProviderName = "DigitalOcean"
|
||||
if p.LoginURL.String() == "" {
|
||||
p.LoginURL = &url.URL{Scheme: "https",
|
||||
Host: "cloud.digitalocean.com",
|
||||
Path: "/v1/oauth/authorize",
|
||||
}
|
||||
}
|
||||
if p.RedeemURL.String() == "" {
|
||||
p.RedeemURL = &url.URL{Scheme: "https",
|
||||
Host: "cloud.digitalocean.com",
|
||||
Path: "/v1/oauth/token",
|
||||
}
|
||||
}
|
||||
if p.ProfileURL.String() == "" {
|
||||
p.ProfileURL = &url.URL{Scheme: "https",
|
||||
Host: "api.digitalocean.com",
|
||||
Path: "/v2/account",
|
||||
}
|
||||
}
|
||||
if p.ValidateURL.String() == "" {
|
||||
p.ValidateURL = p.ProfileURL
|
||||
}
|
||||
if p.Scope == "" {
|
||||
p.Scope = "read"
|
||||
}
|
||||
return &DigitalOceanProvider{ProviderData: p}
|
||||
}
|
||||
|
||||
func getDigitalOceanHeader(accessToken string) http.Header {
|
||||
header := make(http.Header)
|
||||
header.Set("Content-Type", "application/json")
|
||||
header.Set("Authorization", fmt.Sprintf("Bearer %s", accessToken))
|
||||
return header
|
||||
}
|
||||
|
||||
// GetEmailAddress returns the Account email address
|
||||
func (p *DigitalOceanProvider) GetEmailAddress(s *sessions.SessionState) (string, error) {
|
||||
if s.AccessToken == "" {
|
||||
return "", errors.New("missing access token")
|
||||
}
|
||||
req, err := http.NewRequest("GET", p.ProfileURL.String(), nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
req.Header = getDigitalOceanHeader(s.AccessToken)
|
||||
|
||||
json, err := requests.Request(req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
email, err := json.GetPath("account", "email").String()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return email, nil
|
||||
}
|
||||
|
||||
// ValidateSessionState validates the AccessToken
|
||||
func (p *DigitalOceanProvider) ValidateSessionState(s *sessions.SessionState) bool {
|
||||
return validateToken(p, s.AccessToken, getDigitalOceanHeader(s.AccessToken))
|
||||
}
|
134
providers/digitalocean_test.go
Normal file
134
providers/digitalocean_test.go
Normal file
@ -0,0 +1,134 @@
|
||||
package providers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/pusher/oauth2_proxy/pkg/apis/sessions"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func testDigitalOceanProvider(hostname string) *DigitalOceanProvider {
|
||||
p := NewDigitalOceanProvider(
|
||||
&ProviderData{
|
||||
ProviderName: "",
|
||||
LoginURL: &url.URL{},
|
||||
RedeemURL: &url.URL{},
|
||||
ProfileURL: &url.URL{},
|
||||
ValidateURL: &url.URL{},
|
||||
Scope: ""})
|
||||
if hostname != "" {
|
||||
updateURL(p.Data().LoginURL, hostname)
|
||||
updateURL(p.Data().RedeemURL, hostname)
|
||||
updateURL(p.Data().ProfileURL, hostname)
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func testDigitalOceanBackend(payload string) *httptest.Server {
|
||||
path := "/v2/account"
|
||||
|
||||
return httptest.NewServer(http.HandlerFunc(
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path != path {
|
||||
w.WriteHeader(404)
|
||||
} else if r.Header.Get("Authorization") != "Bearer imaginary_access_token" {
|
||||
w.WriteHeader(403)
|
||||
} else {
|
||||
w.WriteHeader(200)
|
||||
w.Write([]byte(payload))
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
func TestDigitalOceanProviderDefaults(t *testing.T) {
|
||||
p := testDigitalOceanProvider("")
|
||||
assert.NotEqual(t, nil, p)
|
||||
assert.Equal(t, "DigitalOcean", p.Data().ProviderName)
|
||||
assert.Equal(t, "https://cloud.digitalocean.com/v1/oauth/authorize",
|
||||
p.Data().LoginURL.String())
|
||||
assert.Equal(t, "https://cloud.digitalocean.com/v1/oauth/token",
|
||||
p.Data().RedeemURL.String())
|
||||
assert.Equal(t, "https://api.digitalocean.com/v2/account",
|
||||
p.Data().ProfileURL.String())
|
||||
assert.Equal(t, "https://api.digitalocean.com/v2/account",
|
||||
p.Data().ValidateURL.String())
|
||||
assert.Equal(t, "read", p.Data().Scope)
|
||||
}
|
||||
|
||||
func TestDigitalOceanProviderOverrides(t *testing.T) {
|
||||
p := NewDigitalOceanProvider(
|
||||
&ProviderData{
|
||||
LoginURL: &url.URL{
|
||||
Scheme: "https",
|
||||
Host: "example.com",
|
||||
Path: "/oauth/auth"},
|
||||
RedeemURL: &url.URL{
|
||||
Scheme: "https",
|
||||
Host: "example.com",
|
||||
Path: "/oauth/token"},
|
||||
ProfileURL: &url.URL{
|
||||
Scheme: "https",
|
||||
Host: "example.com",
|
||||
Path: "/oauth/profile"},
|
||||
ValidateURL: &url.URL{
|
||||
Scheme: "https",
|
||||
Host: "example.com",
|
||||
Path: "/oauth/tokeninfo"},
|
||||
Scope: "profile"})
|
||||
assert.NotEqual(t, nil, p)
|
||||
assert.Equal(t, "DigitalOcean", p.Data().ProviderName)
|
||||
assert.Equal(t, "https://example.com/oauth/auth",
|
||||
p.Data().LoginURL.String())
|
||||
assert.Equal(t, "https://example.com/oauth/token",
|
||||
p.Data().RedeemURL.String())
|
||||
assert.Equal(t, "https://example.com/oauth/profile",
|
||||
p.Data().ProfileURL.String())
|
||||
assert.Equal(t, "https://example.com/oauth/tokeninfo",
|
||||
p.Data().ValidateURL.String())
|
||||
assert.Equal(t, "profile", p.Data().Scope)
|
||||
}
|
||||
|
||||
func TestDigitalOceanProviderGetEmailAddress(t *testing.T) {
|
||||
b := testDigitalOceanBackend(`{"account": {"email": "user@example.com"}}`)
|
||||
defer b.Close()
|
||||
|
||||
bURL, _ := url.Parse(b.URL)
|
||||
p := testDigitalOceanProvider(bURL.Host)
|
||||
|
||||
session := &sessions.SessionState{AccessToken: "imaginary_access_token"}
|
||||
email, err := p.GetEmailAddress(session)
|
||||
assert.Equal(t, nil, err)
|
||||
assert.Equal(t, "user@example.com", email)
|
||||
}
|
||||
|
||||
func TestDigitalOceanProviderGetEmailAddressFailedRequest(t *testing.T) {
|
||||
b := testDigitalOceanBackend("unused payload")
|
||||
defer b.Close()
|
||||
|
||||
bURL, _ := url.Parse(b.URL)
|
||||
p := testDigitalOceanProvider(bURL.Host)
|
||||
|
||||
// We'll trigger a request failure by using an unexpected access
|
||||
// token. Alternatively, we could allow the parsing of the payload as
|
||||
// JSON to fail.
|
||||
session := &sessions.SessionState{AccessToken: "unexpected_access_token"}
|
||||
email, err := p.GetEmailAddress(session)
|
||||
assert.NotEqual(t, nil, err)
|
||||
assert.Equal(t, "", email)
|
||||
}
|
||||
|
||||
func TestDigitalOceanProviderGetEmailAddressEmailNotPresentInPayload(t *testing.T) {
|
||||
b := testDigitalOceanBackend("{\"foo\": \"bar\"}")
|
||||
defer b.Close()
|
||||
|
||||
bURL, _ := url.Parse(b.URL)
|
||||
p := testDigitalOceanProvider(bURL.Host)
|
||||
|
||||
session := &sessions.SessionState{AccessToken: "imaginary_access_token"}
|
||||
email, err := p.GetEmailAddress(session)
|
||||
assert.NotEqual(t, nil, err)
|
||||
assert.Equal(t, "", email)
|
||||
}
|
@ -42,6 +42,8 @@ func New(provider string, p *ProviderData) Provider {
|
||||
return NewBitbucketProvider(p)
|
||||
case "nextcloud":
|
||||
return NewNextcloudProvider(p)
|
||||
case "digitalocean":
|
||||
return NewDigitalOceanProvider(p)
|
||||
default:
|
||||
return NewGoogleProvider(p)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user