From ebaa05ab2b53634a7a3e738618a031fd1518d796 Mon Sep 17 00:00:00 2001 From: maxbr Date: Fri, 19 Aug 2016 17:15:28 +0200 Subject: Use Jira Oauth for user authentication JIRA: RELENG-12 Users can use their jira accounts for the dashboard. This also allows the dasboard to open jira tickets for bookings. Signed-off-by: maxbr --- tools/pharos-dashboard/account/views.py | 153 +++++++++++++++++++------------- 1 file changed, 93 insertions(+), 60 deletions(-) (limited to 'tools/pharos-dashboard/account/views.py') diff --git a/tools/pharos-dashboard/account/views.py b/tools/pharos-dashboard/account/views.py index 34328674..7d2c9bd0 100644 --- a/tools/pharos-dashboard/account/views.py +++ b/tools/pharos-dashboard/account/views.py @@ -1,78 +1,111 @@ +import os +import urllib + +import oauth2 as oauth from django.contrib import messages +from django.contrib.auth import logout, authenticate, login from django.contrib.auth.decorators import login_required -from django.core.exceptions import ObjectDoesNotExist +from django.contrib.auth.mixins import LoginRequiredMixin +from django.contrib.auth.models import User from django.urls import reverse from django.utils.decorators import method_decorator -from django.views.generic import FormView -from registration.backends.simple.views import RegistrationView as BaseRegistrationView +from django.views.generic import RedirectView +from django.views.generic import UpdateView +from jira import JIRA from account.forms import AccountSettingsForm +from account.jira_util import SignatureMethod_RSA_SHA1 from account.models import UserProfile +from pharos_dashboard import settings + +consumer = oauth.Consumer(settings.OAUTH_CONSUMER_KEY, settings.OAUTH_CONSUMER_SECRET) + + +@method_decorator(login_required, name='dispatch') +class AccountSettingsView(UpdateView): + model = UserProfile + form_class = AccountSettingsForm + template_name_suffix = '_update_form' + def get_success_url(self): + messages.add_message(self.request, messages.INFO, + 'Settings saved') + return '/' -class RegistrationView(BaseRegistrationView): - template_name = 'registration/registration_form.html' + def get_object(self, queryset=None): + return self.request.user.userprofile - def get_context_data(self, **kwargs): - context = super(RegistrationView, self).get_context_data(**kwargs) - context.update({'title': "Registration"}) - return context - def register(self, form): - new_user = super(RegistrationView, self).register(form) - UserProfile.objects.create(user=new_user) - messages.add_message(self.request, messages.INFO, 'Please complete your user profile.') - return new_user +class JiraLoginView(RedirectView): + def get_redirect_url(self, *args, **kwargs): + client = oauth.Client(consumer) + client.set_signature_method(SignatureMethod_RSA_SHA1()) - def get_success_url(self, user): - return reverse('account:settings') + # Step 1. Get a request token from Jira. + resp, content = client.request(settings.OAUTH_REQUEST_TOKEN_URL, "POST") + if resp['status'] != '200': + raise Exception("Invalid response %s: %s" % (resp['status'], content)) + # Step 2. Store the request token in a session for later use. + self.request.session['request_token'] = dict(urllib.parse.parse_qsl(content.decode())) + # Step 3. Redirect the user to the authentication URL. + url = settings.OAUTH_AUTHORIZE_URL + '?oauth_token=' + \ + self.request.session['request_token']['oauth_token'] + return url -@method_decorator(login_required, name='dispatch') -class AccountSettingsView(FormView): - form_class = AccountSettingsForm - template_name = 'registration/registration_form.html' - success_url = '/' - def dispatch(self, request, *args, **kwargs): +class JiraLogoutView(LoginRequiredMixin, RedirectView): + def get_redirect_url(self, *args, **kwargs): + logout(self.request) + return '/' + + +class JiraAuthenticatedView(RedirectView): + def get_redirect_url(self, *args, **kwargs): + # Step 1. Use the request token in the session to build a new client. + token = oauth.Token(self.request.session['request_token']['oauth_token'], + self.request.session['request_token']['oauth_token_secret']) + client = oauth.Client(consumer, token) + client.set_signature_method(SignatureMethod_RSA_SHA1()) + + # Step 2. Request the authorized access token from Jira. + resp, content = client.request(settings.OAUTH_ACCESS_TOKEN_URL, "POST") + if resp['status'] != '200': + return '/' + + access_token = dict(urllib.parse.parse_qsl(content.decode())) + + module_dir = os.path.dirname(__file__) # get current directory + with open(module_dir + '/rsa.pem', 'r') as f: + key_cert = f.read() + + oauth_dict = { + 'access_token': access_token['oauth_token'], + 'access_token_secret': access_token['oauth_token_secret'], + 'consumer_key': settings.OAUTH_CONSUMER_KEY, + 'key_cert': key_cert + } + + jira = JIRA(server=settings.JIRA_URL, oauth=oauth_dict) + username = jira.current_user() + url = '/' + # Step 3. Lookup the user or create them if they don't exist. try: - request.user.userprofile - except ObjectDoesNotExist: - UserProfile.objects.create(user=request.user) - messages.add_message(self.request, messages.INFO, - 'Please complete your user profile to proceed.') - return super(AccountSettingsView, self).dispatch(request, *args, **kwargs) - - def get_context_data(self, **kwargs): - context = super(AccountSettingsView, self).get_context_data(**kwargs) - context.update({'title': "Settings"}) - return context - - def get_initial(self): - user = self.request.user - initial = super(AccountSettingsView, self).get_initial() - initial['first_name'] = user.first_name - initial['last_name'] = user.last_name - initial['email'] = user.email - initial['company'] = user.userprofile.company - initial['ssh_public_key'] = user.userprofile.sshkey - initial['pgp_public_key'] = user.userprofile.pgpkey - initial['timezone'] = user.userprofile.timezone - return initial - - def form_valid(self, form): - user = self.request.user - user.first_name = form.cleaned_data['first_name'] - user.last_name = form.cleaned_data['last_name'] - user.email = form.cleaned_data['email'] - user.userprofile.company = form.cleaned_data['company'] - user.userprofile.sshkey = form.cleaned_data['ssh_public_key'] - user.userprofile.pgpkey = form.cleaned_data['pgp_public_key'] - user.userprofile.timezone = form.cleaned_data['timezone'] + user = User.objects.get(username=username) + except User.DoesNotExist: + # Save our permanent token and secret for later. + user = User.objects.create_user(username=username, + password=access_token['oauth_token_secret']) + profile = UserProfile() + profile.user = user + profile.save() + url = reverse('account:settings') + user.userprofile.oauth_token = access_token['oauth_token'] + user.userprofile.oauth_secret = access_token['oauth_token_secret'] user.userprofile.save() - if not user.is_active: - user.is_active = True + user.set_password(access_token['oauth_token_secret']) user.save() - messages.add_message(self.request, messages.INFO, - 'Settings saved') - return super(AccountSettingsView, self).form_valid(form) + user = authenticate(username=username, password=access_token['oauth_token_secret']) + login(self.request, user) + # redirect user to settings page to complete profile + return url -- cgit 1.2.3-korg