Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions auth_gitlab/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,5 @@ class Config(AppConfig):

def ready(self):
from sentry.auth import register

from .provider import GitLabOAuth2Provider

register('gitlab', GitLabOAuth2Provider)
3 changes: 1 addition & 2 deletions auth_gitlab/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

from .constants import API_BASE_URL


class GitLabApiError(Exception):
def __init__(self, message='', status=None):
super().__init__(message)
Expand Down Expand Up @@ -36,5 +35,5 @@ def _request(self, path):
raise GitLabApiError(req.content, status=req.status_code)
return json.loads(req.content)

def get_user(self, access_token):
def get_user(self):
return self._request('user')
10 changes: 10 additions & 0 deletions auth_gitlab/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,13 @@
ACCESS_TOKEN_URL = '{0}://{1}/oauth/token'.format(SCHEME, BASE_DOMAIN)
AUTHORIZE_URL = '{0}://{1}/oauth/authorize'.format(SCHEME, BASE_DOMAIN)
API_BASE_URL = '{0}://{1}/api/v{2}'.format(SCHEME, BASE_DOMAIN, API_VERSION)

# Just dummies from copied GitHub API so far
ERR_NO_ORG_ACCESS = "You do not have access to the required GitLab organization."
ERR_NO_PRIMARY_EMAIL = "We were unable to find a primary email address associated with your GitLab account."

ERR_NO_SINGLE_PRIMARY_EMAIL = "We were unable to find a single primary email address associated with your GitLab account."

ERR_NO_VERIFIED_PRIMARY_EMAIL = "We were unable to find a verified, primary email address associated with your GitLab account."

ERR_NO_SINGLE_VERIFIED_PRIMARY_EMAIL = "We were unable to find a single verified, primary email address associated with your GitLab account"
14 changes: 4 additions & 10 deletions auth_gitlab/provider.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,9 @@
from sentry.auth.providers.oauth2 import (
OAuth2Callback, OAuth2Provider, OAuth2Login
)

from .constants import (
AUTHORIZE_URL, ACCESS_TOKEN_URL, CLIENT_ID, CLIENT_SECRET, SCOPE
)
from .views import FetchUser

from sentry.auth.providers.oauth2 import OAuth2Callback, OAuth2Provider, OAuth2Login
from .constants import AUTHORIZE_URL, ACCESS_TOKEN_URL, CLIENT_ID, CLIENT_SECRET, SCOPE
from .views import ConfirmEmail, FetchUser, GitLabConfigureView, SelectOrganization

class GitLabOAuth2Provider(OAuth2Provider):
name = 'Gitlab'
name = 'GitLab'
client_id = CLIENT_ID
client_secret = CLIENT_SECRET

Expand Down
129 changes: 125 additions & 4 deletions auth_gitlab/views.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,134 @@
from __future__ import absolute_import
from django import forms

from sentry.auth.view import AuthView
from sentry.auth.view import AuthView, ConfigureView
from sentry.models import AuthIdentity

from .client import GitLabClient
from .constants import (
ERR_NO_ORG_ACCESS,
ERR_NO_PRIMARY_EMAIL,
ERR_NO_SINGLE_PRIMARY_EMAIL,
ERR_NO_SINGLE_VERIFIED_PRIMARY_EMAIL,
ERR_NO_VERIFIED_PRIMARY_EMAIL,
# REQUIRE_VERIFIED_EMAIL,
)


def _get_name_from_email(email):
"""
Given an email return a capitalized name. Ex. [email protected] would return John Smith.
"""
name = email.rsplit("@", 1)[0]
name = " ".join(n_part.capitalize() for n_part in name.split("."))
return name


class FetchUser(AuthView):
def __init__(self, org=None, *args, **kwargs):
self.org = org
super().__init__(*args, **kwargs)

def handle(self, request, helper):
with GitHubClient(helper.fetch_state("data")["access_token"]) as client:
with GitLabClient(helper.fetch_state("data")["access_token"]) as client:
if self.org is not None:
if not client.is_org_member(self.org["id"]):
return helper.error(ERR_NO_ORG_ACCESS)

user = client.get_user()
helper.bind_state('user', user)

#if not user.get("email"):
# emails = client.get_user_emails()
# email = [
# e["email"]
# for e in emails
# if ((not REQUIRE_VERIFIED_EMAIL) or e["verified"]) and e["primary"]
# ]
# if len(email) == 0:
# if REQUIRE_VERIFIED_EMAIL:
# msg = ERR_NO_VERIFIED_PRIMARY_EMAIL
# else:
# msg = ERR_NO_PRIMARY_EMAIL
# return helper.error(msg)
# elif len(email) > 1:
# if REQUIRE_VERIFIED_EMAIL:
# msg = ERR_NO_SINGLE_VERIFIED_PRIMARY_EMAIL
# else:
# msg = ERR_NO_SINGLE_PRIMARY_EMAIL
# return helper.error(msg)
# else:
# user["email"] = email[0]

## A user hasn't set their name in their Github profile so it isn't
## populated in the response
#if not user.get("name"):
# user["name"] = _get_name_from_email(user["email"])

helper.bind_state("user", user)

return helper.next_step()


class ConfirmEmailForm(forms.Form):
email = forms.EmailField(label="Email")


class ConfirmEmail(AuthView):
def handle(self, request, helper):
user = helper.fetch_state("user")

# TODO(dcramer): this isn't ideal, but our current flow doesnt really
# support this behavior;
try:
auth_identity = AuthIdentity.objects.select_related("user").get(
auth_provider=helper.provider_model, ident=user["id"]
)
except AuthIdentity.DoesNotExist:
pass
else:
user["email"] = auth_identity.user.email

if user.get("email"):
return helper.next_step()

form = ConfirmEmailForm(request.POST or None)
if form.is_valid():
user["email"] = form.cleaned_data["email"]
helper.bind_state("user", user)
return helper.next_step()

return self.respond("sentry_auth_github/enter-email.html", {"form": form})


class SelectOrganizationForm(forms.Form):
org = forms.ChoiceField(label="Organization")

def __init__(self, org_list, *args, **kwargs):
super().__init__(*args, **kwargs)

self.fields["org"].choices = [(o["id"], o["login"]) for o in org_list]
self.fields["org"].widget.choices = self.fields["org"].choices


class SelectOrganization(AuthView):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

def handle(self, request, helper):
with GitLabClient(helper.fetch_state("data")["access_token"]) as client:
org_list = client.get_org_list()

form = SelectOrganizationForm(org_list, request.POST or None)
if form.is_valid():
org_id = form.cleaned_data["org"]
org = [o for o in org_list if org_id == str(o["id"])][0]
helper.bind_state("org", org)
return helper.next_step()

return self.respond(
"sentry_auth_github/select-organization.html", {"form": form, "org_list": org_list}
)


class GitLabConfigureView(ConfigureView):
def dispatch(self, request, organization, auth_provider):
return self.render("sentry_auth_github/configure.html")