forked from oauth2-proxy/oauth2-proxy
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' into kamal/whitelist-redirects-with-ports
- Loading branch information
Showing
7 changed files
with
241 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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": "[email protected]"}}`) | ||
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, "[email protected]", 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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters