Merge pull request #211 from pmosbach/gitlab-provider
Add GitLab provider
This commit is contained in:
		
						commit
						bfb8dc13bf
					
				
							
								
								
									
										12
									
								
								README.md
								
								
								
								
							
							
						
						
									
										12
									
								
								README.md
								
								
								
								
							|  | @ -31,6 +31,7 @@ Valid providers are : | ||||||
| * [Google](#google-auth-provider) *default* | * [Google](#google-auth-provider) *default* | ||||||
| * [Azure](#azure-auth-provider) | * [Azure](#azure-auth-provider) | ||||||
| * [GitHub](#github-auth-provider) | * [GitHub](#github-auth-provider) | ||||||
|  | * [GitLab](#gitlab-auth-provider) | ||||||
| * [LinkedIn](#linkedin-auth-provider) | * [LinkedIn](#linkedin-auth-provider) | ||||||
| * [MyUSA](#myusa-auth-provider) | * [MyUSA](#myusa-auth-provider) | ||||||
| 
 | 
 | ||||||
|  | @ -103,6 +104,17 @@ If you are using github enterprise, make sure you set the following to the appro | ||||||
|     -validate-url="<enterprise github api url>/user/emails" |     -validate-url="<enterprise github api url>/user/emails" | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | ### GitLab Auth Provider | ||||||
|  | 
 | ||||||
|  | Whether you are using GitLab.com or self-hosting GitLab, follow [these steps to add an application](http://doc.gitlab.com/ce/integration/oauth_provider.html) | ||||||
|  | 
 | ||||||
|  | If you are using self-hosted GitLab, make sure you set the following to the appropriate URL: | ||||||
|  | 
 | ||||||
|  |     -login-url="<your gitlab url>/oauth/authorize" | ||||||
|  |     -redeem-url="<your gitlab url>/oauth/token" | ||||||
|  |     -validate-url="<your gitlab url>/api/v3/user" | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| ### LinkedIn Auth Provider | ### LinkedIn Auth Provider | ||||||
| 
 | 
 | ||||||
| For LinkedIn, the registration steps are: | For LinkedIn, the registration steps are: | ||||||
|  |  | ||||||
|  | @ -0,0 +1,58 @@ | ||||||
|  | package providers | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"log" | ||||||
|  | 	"net/http" | ||||||
|  | 	"net/url" | ||||||
|  | 
 | ||||||
|  | 	"github.com/bitly/oauth2_proxy/api" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type GitLabProvider struct { | ||||||
|  | 	*ProviderData | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func NewGitLabProvider(p *ProviderData) *GitLabProvider { | ||||||
|  | 	p.ProviderName = "GitLab" | ||||||
|  | 	if p.LoginURL == nil || p.LoginURL.String() == "" { | ||||||
|  | 		p.LoginURL = &url.URL{ | ||||||
|  | 			Scheme: "https", | ||||||
|  | 			Host:   "gitlab.com", | ||||||
|  | 			Path:   "/oauth/authorize", | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if p.RedeemURL == nil || p.RedeemURL.String() == "" { | ||||||
|  | 		p.RedeemURL = &url.URL{ | ||||||
|  | 			Scheme: "https", | ||||||
|  | 			Host:   "gitlab.com", | ||||||
|  | 			Path:   "/oauth/token", | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if p.ValidateURL == nil || p.ValidateURL.String() == "" { | ||||||
|  | 		p.ValidateURL = &url.URL{ | ||||||
|  | 			Scheme: "https", | ||||||
|  | 			Host:   "gitlab.com", | ||||||
|  | 			Path:   "/api/v3/user", | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if p.Scope == "" { | ||||||
|  | 		p.Scope = "api" | ||||||
|  | 	} | ||||||
|  | 	return &GitLabProvider{ProviderData: p} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (p *GitLabProvider) GetEmailAddress(s *SessionState) (string, error) { | ||||||
|  | 
 | ||||||
|  | 	req, err := http.NewRequest("GET", | ||||||
|  | 		p.ValidateURL.String()+"?access_token="+s.AccessToken, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Printf("failed building request %s", err) | ||||||
|  | 		return "", err | ||||||
|  | 	} | ||||||
|  | 	json, err := api.Request(req) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Printf("failed making request %s", err) | ||||||
|  | 		return "", err | ||||||
|  | 	} | ||||||
|  | 	return json.Get("email").String() | ||||||
|  | } | ||||||
|  | @ -0,0 +1,128 @@ | ||||||
|  | package providers | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"net/http" | ||||||
|  | 	"net/http/httptest" | ||||||
|  | 	"net/url" | ||||||
|  | 	"testing" | ||||||
|  | 
 | ||||||
|  | 	"github.com/bmizerany/assert" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func testGitLabProvider(hostname string) *GitLabProvider { | ||||||
|  | 	p := NewGitLabProvider( | ||||||
|  | 		&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) | ||||||
|  | 		updateURL(p.Data().ValidateURL, hostname) | ||||||
|  | 	} | ||||||
|  | 	return p | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func testGitLabBackend(payload string) *httptest.Server { | ||||||
|  | 	path := "/api/v3/user" | ||||||
|  | 	query := "access_token=imaginary_access_token" | ||||||
|  | 
 | ||||||
|  | 	return httptest.NewServer(http.HandlerFunc( | ||||||
|  | 		func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 			url := r.URL | ||||||
|  | 			if url.Path != path || url.RawQuery != query { | ||||||
|  | 				w.WriteHeader(404) | ||||||
|  | 			} else { | ||||||
|  | 				w.WriteHeader(200) | ||||||
|  | 				w.Write([]byte(payload)) | ||||||
|  | 			} | ||||||
|  | 		})) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestGitLabProviderDefaults(t *testing.T) { | ||||||
|  | 	p := testGitLabProvider("") | ||||||
|  | 	assert.NotEqual(t, nil, p) | ||||||
|  | 	assert.Equal(t, "GitLab", p.Data().ProviderName) | ||||||
|  | 	assert.Equal(t, "https://gitlab.com/oauth/authorize", | ||||||
|  | 		p.Data().LoginURL.String()) | ||||||
|  | 	assert.Equal(t, "https://gitlab.com/oauth/token", | ||||||
|  | 		p.Data().RedeemURL.String()) | ||||||
|  | 	assert.Equal(t, "https://gitlab.com/api/v3/user", | ||||||
|  | 		p.Data().ValidateURL.String()) | ||||||
|  | 	assert.Equal(t, "api", p.Data().Scope) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestGitLabProviderOverrides(t *testing.T) { | ||||||
|  | 	p := NewGitLabProvider( | ||||||
|  | 		&ProviderData{ | ||||||
|  | 			LoginURL: &url.URL{ | ||||||
|  | 				Scheme: "https", | ||||||
|  | 				Host:   "example.com", | ||||||
|  | 				Path:   "/oauth/auth"}, | ||||||
|  | 			RedeemURL: &url.URL{ | ||||||
|  | 				Scheme: "https", | ||||||
|  | 				Host:   "example.com", | ||||||
|  | 				Path:   "/oauth/token"}, | ||||||
|  | 			ValidateURL: &url.URL{ | ||||||
|  | 				Scheme: "https", | ||||||
|  | 				Host:   "example.com", | ||||||
|  | 				Path:   "/api/v3/user"}, | ||||||
|  | 			Scope: "profile"}) | ||||||
|  | 	assert.NotEqual(t, nil, p) | ||||||
|  | 	assert.Equal(t, "GitLab", 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/api/v3/user", | ||||||
|  | 		p.Data().ValidateURL.String()) | ||||||
|  | 	assert.Equal(t, "profile", p.Data().Scope) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestGitLabProviderGetEmailAddress(t *testing.T) { | ||||||
|  | 	b := testGitLabBackend("{\"email\": \"michael.bland@gsa.gov\"}") | ||||||
|  | 	defer b.Close() | ||||||
|  | 
 | ||||||
|  | 	b_url, _ := url.Parse(b.URL) | ||||||
|  | 	p := testGitLabProvider(b_url.Host) | ||||||
|  | 
 | ||||||
|  | 	session := &SessionState{AccessToken: "imaginary_access_token"} | ||||||
|  | 	email, err := p.GetEmailAddress(session) | ||||||
|  | 	assert.Equal(t, nil, err) | ||||||
|  | 	assert.Equal(t, "michael.bland@gsa.gov", email) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Note that trying to trigger the "failed building request" case is not
 | ||||||
|  | // practical, since the only way it can fail is if the URL fails to parse.
 | ||||||
|  | func TestGitLabProviderGetEmailAddressFailedRequest(t *testing.T) { | ||||||
|  | 	b := testGitLabBackend("unused payload") | ||||||
|  | 	defer b.Close() | ||||||
|  | 
 | ||||||
|  | 	b_url, _ := url.Parse(b.URL) | ||||||
|  | 	p := testGitLabProvider(b_url.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 := &SessionState{AccessToken: "unexpected_access_token"} | ||||||
|  | 	email, err := p.GetEmailAddress(session) | ||||||
|  | 	assert.NotEqual(t, nil, err) | ||||||
|  | 	assert.Equal(t, "", email) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestGitLabProviderGetEmailAddressEmailNotPresentInPayload(t *testing.T) { | ||||||
|  | 	b := testGitLabBackend("{\"foo\": \"bar\"}") | ||||||
|  | 	defer b.Close() | ||||||
|  | 
 | ||||||
|  | 	b_url, _ := url.Parse(b.URL) | ||||||
|  | 	p := testGitLabProvider(b_url.Host) | ||||||
|  | 
 | ||||||
|  | 	session := &SessionState{AccessToken: "imaginary_access_token"} | ||||||
|  | 	email, err := p.GetEmailAddress(session) | ||||||
|  | 	assert.NotEqual(t, nil, err) | ||||||
|  | 	assert.Equal(t, "", email) | ||||||
|  | } | ||||||
|  | @ -26,6 +26,8 @@ func New(provider string, p *ProviderData) Provider { | ||||||
| 		return NewGitHubProvider(p) | 		return NewGitHubProvider(p) | ||||||
| 	case "azure": | 	case "azure": | ||||||
| 		return NewAzureProvider(p) | 		return NewAzureProvider(p) | ||||||
|  | 	case "gitlab": | ||||||
|  | 		return NewGitLabProvider(p) | ||||||
| 	default: | 	default: | ||||||
| 		return NewGoogleProvider(p) | 		return NewGoogleProvider(p) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue