Add github login functionality to the Auth API
parent
fa68d034e5
commit
4860c7cbb9
|
@ -28,6 +28,7 @@ from rest_framework import serializers
|
||||||
|
|
||||||
from taiga.base.decorators import list_route
|
from taiga.base.decorators import list_route
|
||||||
from taiga.base import exceptions as exc
|
from taiga.base import exceptions as exc
|
||||||
|
from taiga.base.connectors import github
|
||||||
from taiga.users.services import get_and_validate_user
|
from taiga.users.services import get_and_validate_user
|
||||||
|
|
||||||
from .serializers import PublicRegisterSerializer
|
from .serializers import PublicRegisterSerializer
|
||||||
|
@ -37,6 +38,7 @@ from .serializers import PrivateRegisterForNewUserSerializer
|
||||||
from .services import private_register_for_existing_user
|
from .services import private_register_for_existing_user
|
||||||
from .services import private_register_for_new_user
|
from .services import private_register_for_new_user
|
||||||
from .services import public_register
|
from .services import public_register
|
||||||
|
from .services import github_register
|
||||||
from .services import make_auth_response_data
|
from .services import make_auth_response_data
|
||||||
|
|
||||||
|
|
||||||
|
@ -130,11 +132,34 @@ class AuthViewSet(viewsets.ViewSet):
|
||||||
return self._private_register(request)
|
return self._private_register(request)
|
||||||
raise exc.BadRequest(_("invalid register type"))
|
raise exc.BadRequest(_("invalid register type"))
|
||||||
|
|
||||||
# Login view: /api/v1/auth
|
def _login(self, request):
|
||||||
def create(self, request, **kwargs):
|
|
||||||
username = request.DATA.get('username', None)
|
username = request.DATA.get('username', None)
|
||||||
password = request.DATA.get('password', None)
|
password = request.DATA.get('password', None)
|
||||||
|
|
||||||
user = get_and_validate_user(username=username, password=password)
|
user = get_and_validate_user(username=username, password=password)
|
||||||
data = make_auth_response_data(user)
|
data = make_auth_response_data(user)
|
||||||
return Response(data, status=status.HTTP_200_OK)
|
return Response(data, status=status.HTTP_200_OK)
|
||||||
|
|
||||||
|
def _github_login(self, request):
|
||||||
|
code = request.DATA.get('code', None)
|
||||||
|
token = request.DATA.get('token', None)
|
||||||
|
|
||||||
|
email, user_info = github.me(code)
|
||||||
|
|
||||||
|
user = github_register(username=user_info.username,
|
||||||
|
email=email,
|
||||||
|
full_name=user_info.full_name,
|
||||||
|
github_id=user_info.id,
|
||||||
|
bio=user_info.bio,
|
||||||
|
token=token)
|
||||||
|
data = make_auth_response_data(user)
|
||||||
|
return Response(data, status=status.HTTP_200_OK)
|
||||||
|
|
||||||
|
# Login view: /api/v1/auth
|
||||||
|
def create(self, request, **kwargs):
|
||||||
|
type = request.DATA.get("type", None)
|
||||||
|
if type == "normal":
|
||||||
|
return self._login(request)
|
||||||
|
elif type == "github":
|
||||||
|
return self._github_login(request)
|
||||||
|
raise exc.BadRequest(_("invalid login type"))
|
||||||
|
|
|
@ -63,14 +63,18 @@ def send_private_register_email(user, **kwargs) -> bool:
|
||||||
return bool(email.send())
|
return bool(email.send())
|
||||||
|
|
||||||
|
|
||||||
def is_user_already_registred(*, username:str, email:str) -> bool:
|
def is_user_already_registred(*, username:str, email:str, github_id:int=None) -> bool:
|
||||||
"""
|
"""
|
||||||
Checks if a specified user is already registred.
|
Checks if a specified user is already registred.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
user_model = get_model("users", "User")
|
user_model = get_model("users", "User")
|
||||||
qs = user_model.objects.filter(Q(username=username) |
|
|
||||||
Q(email=email))
|
or_expr = Q(username=username) | Q(email=email)
|
||||||
|
if github_id:
|
||||||
|
or_expr = or_expr | Q(email=email)
|
||||||
|
|
||||||
|
qs = user_model.objects.filter(or_expr)
|
||||||
return qs.exists()
|
return qs.exists()
|
||||||
|
|
||||||
|
|
||||||
|
@ -90,7 +94,7 @@ def get_membership_by_token(token:str):
|
||||||
|
|
||||||
|
|
||||||
@tx.atomic
|
@tx.atomic
|
||||||
def public_register(username:str, password:str, email:str, first_name:str, last_name:str):
|
def public_register(username:str, password:str, email:str, full_name:str):
|
||||||
"""
|
"""
|
||||||
Given a parsed parameters, try register a new user
|
Given a parsed parameters, try register a new user
|
||||||
knowing that it follows a public register flow.
|
knowing that it follows a public register flow.
|
||||||
|
@ -107,8 +111,7 @@ def public_register(username:str, password:str, email:str, first_name:str, last_
|
||||||
user_model = get_model("users", "User")
|
user_model = get_model("users", "User")
|
||||||
user = user_model(username=username,
|
user = user_model(username=username,
|
||||||
email=email,
|
email=email,
|
||||||
first_name=first_name,
|
full_name=full_name)
|
||||||
last_name=last_name)
|
|
||||||
user.set_password(password)
|
user.set_password(password)
|
||||||
user.save()
|
user.save()
|
||||||
|
|
||||||
|
@ -138,21 +141,18 @@ def private_register_for_existing_user(token:str, username:str, password:str):
|
||||||
|
|
||||||
@tx.atomic
|
@tx.atomic
|
||||||
def private_register_for_new_user(token:str, username:str, email:str,
|
def private_register_for_new_user(token:str, username:str, email:str,
|
||||||
first_name:str, last_name:str, password:str):
|
full_name:str, password:str):
|
||||||
"""
|
"""
|
||||||
Given a inviation token, try register new user matching
|
Given a inviation token, try register new user matching
|
||||||
the invitation token.
|
the invitation token.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
user_model = get_model("users", "User")
|
|
||||||
|
|
||||||
if is_user_already_registred(username=username, email=email):
|
if is_user_already_registred(username=username, email=email):
|
||||||
raise exc.WrongArguments(_("Username or Email is already in use."))
|
raise exc.WrongArguments(_("Username or Email is already in use."))
|
||||||
|
|
||||||
|
user_model = get_model("users", "User")
|
||||||
user = user_model(username=username,
|
user = user_model(username=username,
|
||||||
email=email,
|
email=email,
|
||||||
first_name=first_name,
|
full_name=full_name)
|
||||||
last_name=last_name)
|
|
||||||
|
|
||||||
user.set_password(password)
|
user.set_password(password)
|
||||||
try:
|
try:
|
||||||
|
@ -167,6 +167,30 @@ def private_register_for_new_user(token:str, username:str, email:str,
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
|
||||||
|
@tx.atomic
|
||||||
|
def github_register(username:str, email:str, full_name:str, github_id:int, bio:str, token:str=None):
|
||||||
|
"""
|
||||||
|
Register a new user from github.
|
||||||
|
|
||||||
|
This can raise `exc.IntegrityError` exceptions in
|
||||||
|
case of conflics found.
|
||||||
|
|
||||||
|
:returns: User
|
||||||
|
"""
|
||||||
|
user_model = get_model("users", "User")
|
||||||
|
user, created = user_model.objects.get_or_create(github_id=github_id,
|
||||||
|
defaults={"username": username,
|
||||||
|
"email": email,
|
||||||
|
"full_name": full_name,
|
||||||
|
"bio": bio})
|
||||||
|
if token:
|
||||||
|
membership = get_membership_by_token(token)
|
||||||
|
membership.user = user
|
||||||
|
membership.save(update_fields=["user"])
|
||||||
|
|
||||||
|
return user
|
||||||
|
|
||||||
|
|
||||||
def make_auth_response_data(user) -> dict:
|
def make_auth_response_data(user) -> dict:
|
||||||
"""
|
"""
|
||||||
Given a domain and user, creates data structure
|
Given a domain and user, creates data structure
|
||||||
|
|
|
@ -79,8 +79,8 @@ def _get(url:str, headers:dict) -> dict:
|
||||||
|
|
||||||
data = response.json()
|
data = response.json()
|
||||||
if response.status_code != 200:
|
if response.status_code != 200:
|
||||||
raise exc.GitHubApiErrorApiError({"status_code": response.status_code,
|
raise exc.GitHubApiError({"status_code": response.status_code,
|
||||||
"error": data.get("error", "")})
|
"error": data.get("error", "")})
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
@ -88,12 +88,12 @@ def _post(url:str, params:dict, headers:dict) -> dict:
|
||||||
"""
|
"""
|
||||||
Make a POST call.
|
Make a POST call.
|
||||||
"""
|
"""
|
||||||
response = requests.post(url,params=params, headers=headers)
|
response = requests.post(url, params=params, headers=headers)
|
||||||
|
|
||||||
data = response.json()
|
data = response.json()
|
||||||
if response.status_code != 200 or "error" in data:
|
if response.status_code != 200 or "error" in data:
|
||||||
raise exc.GitHubApiErrorApiError({"status_code": response.status_code,
|
raise exc.GitHubApiError({"status_code": response.status_code,
|
||||||
"error": data.get("error", "")})
|
"error": data.get("error", "")})
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
@ -136,7 +136,7 @@ def get_user_emails(headers:dict=HEADERS) -> list:
|
||||||
"""
|
"""
|
||||||
url = _build_url("user", "emails")
|
url = _build_url("user", "emails")
|
||||||
data = _get(url, headers=headers)
|
data = _get(url, headers=headers)
|
||||||
return [Email(email=e.get("email", None), is_primary=e.get("primary", None))
|
return [Email(email=e.get("email", None), is_primary=e.get("primary", False))
|
||||||
for e in data]
|
for e in data]
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue