mirror of
https://github.com/OpenBankProject/API-Manager.git
synced 2026-02-06 10:59:00 +00:00
Ported obp app from API Tester #31
Also enables possibility to use DirectLogin and GatewayLogin
This commit is contained in:
parent
5fc4e45577
commit
2358d8eed6
10
README.md
10
README.md
@ -25,14 +25,8 @@ apimanager/
|
||||
│ ├── requirements.txt
|
||||
│ └── supervisor.apimanager.conf
|
||||
├── db.sqlite3
|
||||
├── logs [error opening dir]
|
||||
├── logs
|
||||
├── static-collected
|
||||
│ ├── admin
|
||||
│ ├── consumers
|
||||
│ ├── css
|
||||
│ ├── img
|
||||
│ ├── js
|
||||
│ └── users
|
||||
└── venv
|
||||
├── bin
|
||||
└── lib
|
||||
@ -54,7 +48,7 @@ Edit `apimanager/apimanager/local_settings.py`:
|
||||
# Used internally by Django, can be anything of your choice
|
||||
SECRET_KEY = '<random string>'
|
||||
# API hostname, e.g. https://api.openbankproject.com
|
||||
OAUTH_API = '<hostname>'
|
||||
API_HOST = '<hostname>'
|
||||
# Consumer key + secret to authenticate the _app_ against the API
|
||||
OAUTH_CONSUMER_KEY = '<key>'
|
||||
OAUTH_CONSUMER_SECRET = '<secret>'
|
||||
|
||||
@ -45,7 +45,7 @@ INSTALLED_APPS = [
|
||||
'django.contrib.staticfiles',
|
||||
|
||||
'base',
|
||||
'oauth',
|
||||
'obp',
|
||||
'consumers',
|
||||
'users',
|
||||
'customers',
|
||||
@ -187,13 +187,18 @@ API_DATETIMEFORMAT = '%Y-%m-%dT%H:%M:%SZ'
|
||||
API_DATEFORMAT = '%Y-%m-%d'
|
||||
|
||||
|
||||
OAUTH_API = 'http://127.0.0.1:8080'
|
||||
API_HOST = 'http://127.0.0.1:8080'
|
||||
API_BASE_PATH = '/obp/v3.0.0'
|
||||
# For some reason, swagger is not available at the latest API version
|
||||
API_SWAGGER_BASE_PATH = '/obp/v1.4.0'
|
||||
|
||||
|
||||
# Always save session$
|
||||
SESSION_SAVE_EVERY_REQUEST = True
|
||||
|
||||
OAUTH_TOKEN_PATH = '/oauth/initiate'
|
||||
OAUTH_AUTHORIZATION_PATH = '/oauth/authorize'
|
||||
OAUTH_ACCESS_TOKEN_PATH = '/oauth/token'
|
||||
OAUTH_API_BASE_PATH = '/obp/v3.0.0'
|
||||
|
||||
|
||||
|
||||
# Set OAuth client key/secret in apimanager/local_settings.py
|
||||
OAUTH_CONSUMER_KEY = None
|
||||
|
||||
@ -6,11 +6,19 @@ URLs for apimanager
|
||||
from django.conf.urls import url, include
|
||||
|
||||
from base.views import HomeView
|
||||
from obp.views import OAuthInitiateView, OAuthAuthorizeView, LogoutView
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$', HomeView.as_view(), name="home"),
|
||||
url(r'^oauth/', include('oauth.urls')),
|
||||
# Defining authentication URLs here and not including oauth.urls for
|
||||
# backward compatibility
|
||||
url(r'^oauth/initiate$',
|
||||
OAuthInitiateView.as_view(), name='oauth-initiate'),
|
||||
url(r'^oauth/authorize$',
|
||||
OAuthAuthorizeView.as_view(), name='oauth-authorize'),
|
||||
url(r'^logout$',
|
||||
LogoutView.as_view(), name='oauth-logout'),
|
||||
url(r'^consumers/', include('consumers.urls')),
|
||||
url(r'^users/', include('users.urls')),
|
||||
url(r'^customers/', include('customers.urls')),
|
||||
|
||||
@ -1,116 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Module to handle the OBP API
|
||||
|
||||
It instantiates a convenience api object for imports, e.g.:
|
||||
from base.api import api
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
import logging
|
||||
import time
|
||||
|
||||
from requests.exceptions import ConnectionError
|
||||
from requests_oauthlib import OAuth1Session
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth import logout
|
||||
|
||||
|
||||
DATE_FORMAT = '%d/%b/%Y %H:%M:%S'
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def log(level, message):
|
||||
"""Logs a given message on a given level to log facility"""
|
||||
now = datetime.now().strftime(DATE_FORMAT)
|
||||
msg = '[{}] API: {}'.format(now, message)
|
||||
LOGGER.log(level, msg)
|
||||
|
||||
|
||||
class APIError(Exception):
|
||||
"""Exception class for API errors"""
|
||||
pass
|
||||
|
||||
|
||||
class API(object):
|
||||
"""Implements an interface to the OBP API"""
|
||||
|
||||
def get(self, request, urlpath=''):
|
||||
"""Gets data from the API"""
|
||||
return self.call(request, 'GET', urlpath)
|
||||
|
||||
def delete(self, request, urlpath):
|
||||
"""Deletes data from the API"""
|
||||
return self.call(request, 'DELETE', urlpath)
|
||||
|
||||
def post(self, request, urlpath, payload):
|
||||
"""Posts data to the API"""
|
||||
return self.call(request, 'POST', urlpath, payload)
|
||||
|
||||
def put(self, request, urlpath, payload):
|
||||
"""Puts data onto the API"""
|
||||
return self.call(request, 'PUT', urlpath, payload)
|
||||
|
||||
def handle_response_404(self, response, prefix):
|
||||
# Stripping HTML body ...
|
||||
if response.text.find('body'):
|
||||
msg = response.text.split('<body>')[1].split('</body>')[0]
|
||||
msg = '{} {}: {}'.format(
|
||||
prefix, response.status_code, msg)
|
||||
log(logging.ERROR, msg)
|
||||
raise APIError(msg)
|
||||
|
||||
def handle_response_500(self, response, prefix):
|
||||
msg = '{} {}: {}'.format(
|
||||
prefix, response.status_code, response.text)
|
||||
log(logging.ERROR, msg)
|
||||
raise APIError(msg)
|
||||
|
||||
def handle_response_error(self, request, prefix, error):
|
||||
if 'Invalid or expired access token' in error:
|
||||
logout(request)
|
||||
msg = '{} {}'.format(prefix, error)
|
||||
raise APIError(msg)
|
||||
|
||||
def handle_response(self, request, response):
|
||||
"""Handles the response, e.g. errors or conversion to JSON"""
|
||||
prefix = 'APIError'
|
||||
if response.status_code == 404:
|
||||
self.handle_response_404(response, prefix)
|
||||
elif response.status_code == 500:
|
||||
self.handle_response_500(response, prefix)
|
||||
elif response.status_code in [204]:
|
||||
return response.text
|
||||
else:
|
||||
data = response.json()
|
||||
if 'error' in data:
|
||||
self.handle_response_error(request, prefix, data['error'])
|
||||
return data
|
||||
|
||||
def call(self, request, method='GET', urlpath='', payload=None):
|
||||
"""Workhorse which actually calls the API"""
|
||||
url = settings.OAUTH_API + settings.OAUTH_API_BASE_PATH + urlpath
|
||||
log(logging.INFO, '{} {}'.format(method, url))
|
||||
if payload:
|
||||
log(logging.INFO, 'Payload: {}'.format(payload))
|
||||
if not hasattr(request, 'api'):
|
||||
request.api = OAuth1Session(
|
||||
settings.OAUTH_CONSUMER_KEY,
|
||||
client_secret=settings.OAUTH_CONSUMER_SECRET,
|
||||
resource_owner_key=request.session['oauth_token'],
|
||||
resource_owner_secret=request.session['oauth_secret']
|
||||
)
|
||||
time_start = time.time()
|
||||
try:
|
||||
if payload:
|
||||
response = request.api.request(method, url, json=payload)
|
||||
else:
|
||||
response = request.api.request(method, url)
|
||||
except ConnectionError as err:
|
||||
raise APIError(err)
|
||||
time_end = time.time()
|
||||
elapsed = int((time_end - time_start) * 1000)
|
||||
log(logging.INFO, 'Took {} ms'.format(elapsed))
|
||||
return self.handle_response(request, response)
|
||||
api = API()
|
||||
@ -1,32 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Helpers to reuse common API calls
|
||||
"""
|
||||
|
||||
from django.contrib import messages
|
||||
|
||||
from .api import api, APIError
|
||||
|
||||
|
||||
def get_bank_id_choices(request):
|
||||
"""Gets a list of bank ids and bank ids as used by form choices"""
|
||||
choices = [('', 'Choose ...')]
|
||||
try:
|
||||
result = api.get(request, '/banks')
|
||||
for bank in result['banks']:
|
||||
choices.append((bank['id'], bank['id']))
|
||||
except APIError as err:
|
||||
messages.error(request, err)
|
||||
return choices
|
||||
|
||||
|
||||
def get_user_id_choices(request):
|
||||
"""Gets a list of user ids and usernames as used by form choices"""
|
||||
choices = [('', 'Choose ...')]
|
||||
try:
|
||||
result = api.get(request, '/users')
|
||||
for user in result['users']:
|
||||
choices.append((user['user_id'], user['username']))
|
||||
except APIError as err:
|
||||
messages.error(request, err)
|
||||
return choices
|
||||
@ -6,13 +6,13 @@ Context processors for base app
|
||||
from django.conf import settings
|
||||
from django.contrib import messages
|
||||
|
||||
from base.api import api, APIError
|
||||
from obp.api import API, APIError
|
||||
|
||||
|
||||
|
||||
def api_root(request):
|
||||
"""Returns the configured API_ROOT"""
|
||||
return {'API_ROOT': settings.OAUTH_API + settings.OAUTH_API_BASE_PATH}
|
||||
return {'API_ROOT': settings.API_HOST + settings.API_BASE_PATH}
|
||||
|
||||
|
||||
|
||||
@ -21,7 +21,8 @@ def api_username(request):
|
||||
username = 'not authenticated'
|
||||
if request.user.is_authenticated:
|
||||
try:
|
||||
data = api.get(request, '/users/current')
|
||||
api = API(request.session.get('obp'))
|
||||
data = api.get('/users/current')
|
||||
username = data['username']
|
||||
except APIError as err:
|
||||
messages.error(request, err)
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
|
||||
<div class="well" id="intro">
|
||||
<p>
|
||||
This app gives you access to management functionality for the sandbox at <a href="{{ OAUTH_API }}">{{ OAUTH_API }}</a>. You have to <a href="{{ OAUTH_API }}/user_mgt/sign_up" title="Register at {{ OAUTH_API }}">register</a> an account before being able to proceed. The logged-in user needs to have specific roles granted to use the functionality.
|
||||
This app gives you access to management functionality for the sandbox at <a href="{{ API_HOST }}">{{ API_HOST }}</a>. You have to <a href="{{ API_HOST }}/user_mgt/sign_up" title="Register at {{ API_HOST }}">register</a> an account before being able to proceed. The logged-in user needs to have specific roles granted to use the functionality.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
@ -13,5 +13,5 @@ class HomeView(TemplateView):
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(HomeView, self).get_context_data(**kwargs)
|
||||
context['OAUTH_API'] = settings.OAUTH_API
|
||||
context['API_HOST'] = settings.API_HOST
|
||||
return context
|
||||
|
||||
@ -9,9 +9,7 @@ from django.contrib import messages
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.views.generic import FormView, TemplateView, View
|
||||
|
||||
from base.api import api, APIError
|
||||
|
||||
|
||||
from obp.api import API, APIError
|
||||
|
||||
|
||||
class IndexView(LoginRequiredMixin, TemplateView):
|
||||
@ -20,9 +18,10 @@ class IndexView(LoginRequiredMixin, TemplateView):
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(IndexView, self).get_context_data(**kwargs)
|
||||
api = API(self.request.session.get('obp'))
|
||||
try:
|
||||
urlpath = '/config'
|
||||
config = api.get(self.request, urlpath)
|
||||
config = api.get(urlpath)
|
||||
except APIError as err:
|
||||
messages.error(self.request, err)
|
||||
config = {}
|
||||
|
||||
@ -11,7 +11,7 @@ from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.urls import reverse
|
||||
from django.views.generic import TemplateView, RedirectView
|
||||
|
||||
from base.api import api, APIError
|
||||
from obp.api import API, APIError
|
||||
from base.filters import BaseFilter, FilterTime
|
||||
|
||||
|
||||
@ -68,10 +68,10 @@ class IndexView(LoginRequiredMixin, TemplateView):
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(IndexView, self).get_context_data(**kwargs)
|
||||
consumers = []
|
||||
|
||||
api = API(self.request.session.get('obp'))
|
||||
try:
|
||||
urlpath = '/management/consumers'
|
||||
consumers = api.get(self.request, urlpath)
|
||||
consumers = api.get(urlpath)
|
||||
consumers = FilterEnabled(context, self.request.GET)\
|
||||
.apply(consumers['list'])
|
||||
consumers = FilterAppType(context, self.request.GET)\
|
||||
@ -98,10 +98,11 @@ class DetailView(LoginRequiredMixin, TemplateView):
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(DetailView, self).get_context_data(**kwargs)
|
||||
api = API(self.request.session.get('obp'))
|
||||
|
||||
try:
|
||||
urlpath = '/management/consumers/{}'.format(kwargs['consumer_id'])
|
||||
consumer = api.get(self.request, urlpath)
|
||||
consumer = api.get(urlpath)
|
||||
consumer['created'] = datetime.strptime(
|
||||
consumer['created'], settings.API_DATETIMEFORMAT)
|
||||
except APIError as err:
|
||||
@ -120,10 +121,11 @@ class EnableDisableView(LoginRequiredMixin, RedirectView):
|
||||
success = None
|
||||
|
||||
def get_redirect_url(self, *args, **kwargs):
|
||||
api = API(self.request.session.get('obp'))
|
||||
try:
|
||||
urlpath = '/management/consumers/{}'.format(kwargs['consumer_id'])
|
||||
payload = {'enabled': self.enabled}
|
||||
api.put(self.request, urlpath, payload)
|
||||
api.put(urlpath, payload)
|
||||
messages.success(self.request, self.success)
|
||||
except APIError as err:
|
||||
messages.error(self.request, err)
|
||||
|
||||
@ -12,8 +12,7 @@ from django.http import HttpResponseRedirect
|
||||
from django.urls import reverse_lazy
|
||||
from django.views.generic import FormView
|
||||
|
||||
from base.api import api, APIError
|
||||
from base.api_helper import get_bank_id_choices, get_user_id_choices
|
||||
from obp.api import API, APIError
|
||||
|
||||
from .forms import CreateCustomerForm
|
||||
|
||||
@ -24,53 +23,56 @@ class CreateView(LoginRequiredMixin, FormView):
|
||||
template_name = 'customers/create.html'
|
||||
success_url = reverse_lazy('customers-create')
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
self.api = API(request.session.get('obp'))
|
||||
return super(CreateView, self).dispatch(request, *args,**kwargs)
|
||||
|
||||
def get_form(self, *args, **kwargs):
|
||||
form = super(CreateView, self).get_form(*args, **kwargs)
|
||||
fields = form.fields
|
||||
fields['bank_id'].choices = get_bank_id_choices(self.request)
|
||||
fields['user_id'].choices = get_user_id_choices(self.request)
|
||||
fields['bank_id'].choices = self.api.get_bank_id_choices()
|
||||
fields['user_id'].choices = self.api.get_user_id_choices()
|
||||
fields['last_ok_date'].initial =\
|
||||
datetime.datetime.now().strftime(settings.API_DATETIMEFORMAT)
|
||||
return form
|
||||
|
||||
def form_valid(self, form):
|
||||
data = form.cleaned_data
|
||||
urlpath = '/banks/{}/customers'.format(data['bank_id'])
|
||||
payload = {
|
||||
'user_id': data['user_id'],
|
||||
'customer_number': data['customer_number'],
|
||||
'legal_name': data['legal_name'],
|
||||
'mobile_phone_number': data['mobile_phone_number'],
|
||||
'email': data['email'],
|
||||
'face_image': {
|
||||
'url': data['face_image_url'],
|
||||
'date': data['face_image_date'],
|
||||
},
|
||||
'date_of_birth': data['date_of_birth'],
|
||||
'relationship_status': data['relationship_status'],
|
||||
'dependants': data['dependants'],
|
||||
'dob_of_dependants': data['dob_of_dependants'],
|
||||
'credit_rating': {
|
||||
'rating': data['credit_rating_rating'],
|
||||
'source': data['credit_rating_source'],
|
||||
},
|
||||
'credit_limit': {
|
||||
'currency': data['credit_limit_currency'],
|
||||
'amount': data['credit_limit_amount'],
|
||||
},
|
||||
'highest_education_attained':
|
||||
data['highest_education_attained'],
|
||||
'employment_status': data['employment_status'],
|
||||
'kyc_status': data['kyc_status'],
|
||||
'last_ok_date':
|
||||
data['last_ok_date'].strftime(settings.API_DATETIMEFORMAT),
|
||||
}
|
||||
try:
|
||||
data = form.cleaned_data
|
||||
urlpath = '/banks/{}/customers'.format(data['bank_id'])
|
||||
payload = {
|
||||
'user_id': data['user_id'],
|
||||
'customer_number': data['customer_number'],
|
||||
'legal_name': data['legal_name'],
|
||||
'mobile_phone_number': data['mobile_phone_number'],
|
||||
'email': data['email'],
|
||||
'face_image': {
|
||||
'url': data['face_image_url'],
|
||||
'date': data['face_image_date'],
|
||||
},
|
||||
'date_of_birth': data['date_of_birth'],
|
||||
'relationship_status': data['relationship_status'],
|
||||
'dependants': data['dependants'],
|
||||
'dob_of_dependants': data['dob_of_dependants'],
|
||||
'credit_rating': {
|
||||
'rating': data['credit_rating_rating'],
|
||||
'source': data['credit_rating_source'],
|
||||
},
|
||||
'credit_limit': {
|
||||
'currency': data['credit_limit_currency'],
|
||||
'amount': data['credit_limit_amount'],
|
||||
},
|
||||
'highest_education_attained':
|
||||
data['highest_education_attained'],
|
||||
'employment_status': data['employment_status'],
|
||||
'kyc_status': data['kyc_status'],
|
||||
'last_ok_date':
|
||||
data['last_ok_date'].strftime(settings.API_DATETIMEFORMAT),
|
||||
}
|
||||
result = api.post(self.request, urlpath, payload=payload)
|
||||
result = self.api.post(urlpath, payload=payload)
|
||||
except APIError as err:
|
||||
messages.error(self.request, err)
|
||||
return super(CreateView, self).form_invalid(form)
|
||||
|
||||
msg = 'Customer number {} has been created successfully!'.format(
|
||||
result['customer_number'])
|
||||
messages.success(self.request, msg)
|
||||
|
||||
@ -15,7 +15,7 @@ from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.views.generic import FormView, TemplateView
|
||||
from django.utils.http import urlquote
|
||||
|
||||
from base.api import api, APIError
|
||||
from obp.api import API, APIError
|
||||
|
||||
from .forms import APIMetricsForm, ConnectorMetricsForm
|
||||
|
||||
@ -117,8 +117,9 @@ class MetricsView(LoginRequiredMixin, TemplateView):
|
||||
metrics = []
|
||||
params = self.to_api(cleaned_data)
|
||||
urlpath = '{}?{}'.format(self.api_urlpath, params)
|
||||
api = API(self.request.session.get('obp'))
|
||||
try:
|
||||
metrics = api.get(self.request, urlpath)
|
||||
metrics = api.get(urlpath)
|
||||
metrics = self.to_django(metrics['metrics'])
|
||||
except APIError as err:
|
||||
messages.error(self.request, err)
|
||||
|
||||
@ -1,11 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
App config for OAuth 1 app
|
||||
"""
|
||||
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class OauthConfig(AppConfig):
|
||||
"""Config for OAuth 1"""
|
||||
name = 'oauth'
|
||||
@ -1,14 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
URLs for OAuth 1 app
|
||||
"""
|
||||
|
||||
from django.conf.urls import url
|
||||
|
||||
from .views import InitiateView, AuthorizeView, LogoutView
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^initiate$', InitiateView.as_view(), name='oauth-initiate'),
|
||||
url(r'^authorize$', AuthorizeView.as_view(), name='oauth-authorize'),
|
||||
url(r'^logout$', LogoutView.as_view(), name='oauth-logout'),
|
||||
]
|
||||
@ -1,102 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Views for OAuth 1 app
|
||||
"""
|
||||
|
||||
import hashlib
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth import login, logout
|
||||
from django.contrib.auth.models import User
|
||||
from django.urls import reverse
|
||||
from django.views.generic import RedirectView
|
||||
|
||||
from requests_oauthlib import OAuth1Session
|
||||
from requests_oauthlib.oauth1_session import TokenRequestDenied
|
||||
|
||||
from base.api import api
|
||||
|
||||
|
||||
class InitiateView(RedirectView):
|
||||
"""View to initiate OAuth 1 session"""
|
||||
|
||||
def get_callback_uri(self, request):
|
||||
"""
|
||||
Gets the callback URI to where the user shall be returned after
|
||||
authorization at OAuth 1 server
|
||||
"""
|
||||
base_url = '{}://{}'.format(
|
||||
request.scheme, request.environ['HTTP_HOST'])
|
||||
uri = base_url + reverse('oauth-authorize')
|
||||
if 'next' in request.GET:
|
||||
uri = '{}?next={}'.format(uri, request.GET['next'])
|
||||
return uri
|
||||
|
||||
def get_redirect_url(self, *args, **kwargs):
|
||||
callback_uri = self.get_callback_uri(self.request)
|
||||
session = OAuth1Session(
|
||||
settings.OAUTH_CONSUMER_KEY,
|
||||
client_secret=settings.OAUTH_CONSUMER_SECRET,
|
||||
callback_uri=callback_uri,
|
||||
)
|
||||
|
||||
try:
|
||||
url = settings.OAUTH_API + settings.OAUTH_TOKEN_PATH
|
||||
response = session.fetch_request_token(url)
|
||||
except (ValueError, TokenRequestDenied) as err:
|
||||
messages.error(self.request, err)
|
||||
return reverse('home')
|
||||
|
||||
url = settings.OAUTH_API + settings.OAUTH_AUTHORIZATION_PATH
|
||||
authorization_url = session.authorization_url(url)
|
||||
self.request.session['oauth_token'] = response.get('oauth_token')
|
||||
self.request.session['oauth_secret'] = response.get('oauth_token_secret')
|
||||
self.request.session.modified = True
|
||||
return authorization_url
|
||||
|
||||
|
||||
class AuthorizeView(RedirectView):
|
||||
"""View to authorize user"""
|
||||
|
||||
def login_to_django(self):
|
||||
"""
|
||||
Logs the user into Django
|
||||
Kind of faking it to establish if a user is authenticated later on
|
||||
"""
|
||||
data = api.get(self.request, '/users/current')
|
||||
userid = data['user_id'] or data['email']
|
||||
username = hashlib.sha256(userid.encode('utf-8')).hexdigest()
|
||||
password = username
|
||||
user, _ = User.objects.get_or_create(
|
||||
username=username, password=password,
|
||||
)
|
||||
login(self.request, user)
|
||||
|
||||
def get_redirect_url(self, *args, **kwargs):
|
||||
session = OAuth1Session(
|
||||
settings.OAUTH_CONSUMER_KEY,
|
||||
settings.OAUTH_CONSUMER_SECRET,
|
||||
resource_owner_key=self.request.session.get('oauth_token'),
|
||||
resource_owner_secret=self.request.session.get('oauth_secret')
|
||||
)
|
||||
session.parse_authorization_response(self.request.build_absolute_uri())
|
||||
url = settings.OAUTH_API + settings.OAUTH_ACCESS_TOKEN_PATH
|
||||
try:
|
||||
response = session.fetch_access_token(url)
|
||||
self.request.session['oauth_token'] = response.get('oauth_token')
|
||||
self.request.session['oauth_secret'] = response.get('oauth_token_secret')
|
||||
self.request.session.modified = True
|
||||
self.login_to_django()
|
||||
except TokenRequestDenied as err:
|
||||
messages.error(self.request, err)
|
||||
redirect_url = self.request.GET.get('next', reverse('home'))
|
||||
return redirect_url
|
||||
|
||||
|
||||
class LogoutView(RedirectView):
|
||||
"""View to logout"""
|
||||
|
||||
def get_redirect_url(self, *args, **kwargs):
|
||||
logout(self.request)
|
||||
return reverse('home')
|
||||
175
apimanager/obp/api.py
Normal file
175
apimanager/obp/api.py
Normal file
@ -0,0 +1,175 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Module to handle the OBP API
|
||||
|
||||
It instantiates a convenience api object for imports, e.g.:
|
||||
from obp.api import api
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
import importlib
|
||||
import logging
|
||||
import time
|
||||
|
||||
import requests
|
||||
from requests.exceptions import ConnectionError
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
DATE_FORMAT = '%d/%b/%Y %H:%M:%S'
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def log(level, message):
|
||||
"""Logs a given message on a given level to log facility"""
|
||||
now = datetime.now().strftime(DATE_FORMAT)
|
||||
msg = '[{}] API: {}'.format(now, message)
|
||||
LOGGER.log(level, msg)
|
||||
|
||||
|
||||
class APIError(Exception):
|
||||
"""Exception class for API errors"""
|
||||
pass
|
||||
|
||||
|
||||
class API(object):
|
||||
"""Implements an interface to the OBP API"""
|
||||
session = None
|
||||
swagger = None
|
||||
|
||||
def __init__(self, session_data=None):
|
||||
self.set_base_path()
|
||||
if session_data:
|
||||
self.start_session(session_data)
|
||||
self.session_data = session_data
|
||||
|
||||
def set_base_path(self, base_path=None):
|
||||
"""Sets the basepath for API calls"""
|
||||
if base_path:
|
||||
self.base_path = settings.API_HOST + base_path
|
||||
else:
|
||||
self.base_path = settings.API_HOST + settings.API_BASE_PATH
|
||||
|
||||
def call(self, method='GET', urlpath='', payload=None):
|
||||
"""Workhorse which actually calls the API"""
|
||||
url = self.base_path + urlpath
|
||||
log(logging.INFO, '{} {}'.format(method, url))
|
||||
if payload:
|
||||
log(logging.INFO, 'Payload: {}'.format(payload))
|
||||
# use `requests` if no session has been started
|
||||
session = self.session or requests
|
||||
time_start = time.time()
|
||||
try:
|
||||
if payload:
|
||||
response = session.request(method, url, json=payload)
|
||||
else:
|
||||
response = session.request(method, url)
|
||||
except ConnectionError as err:
|
||||
raise APIError(err)
|
||||
time_end = time.time()
|
||||
elapsed = int((time_end - time_start) * 1000)
|
||||
log(logging.INFO, 'Took {} ms'.format(elapsed))
|
||||
response.execution_time = elapsed
|
||||
return response
|
||||
|
||||
def get(self, urlpath=''):
|
||||
"""Gets data from the API"""
|
||||
response = self.call('GET', urlpath)
|
||||
return self.handle_response(response)
|
||||
|
||||
def delete(self, urlpath):
|
||||
"""Deletes data from the API"""
|
||||
response = self.call('DELETE', urlpath)
|
||||
return self.handle_response(response)
|
||||
|
||||
def post(self, urlpath, payload):
|
||||
"""Posts data to the API"""
|
||||
response = self.call('POST', urlpath, payload)
|
||||
return self.handle_response(response)
|
||||
|
||||
def put(self, urlpath, payload):
|
||||
"""Puts data onto the API"""
|
||||
response = self.call('PUT', urlpath, payload)
|
||||
return self.handle_response(response)
|
||||
|
||||
def handle_response_404(self, response, prefix):
|
||||
# Stripping HTML body ...
|
||||
if response.text.find('body'):
|
||||
msg = response.text.split('<body>')[1].split('</body>')[0]
|
||||
msg = '{} {}: {}'.format(
|
||||
prefix, response.status_code, msg)
|
||||
log(logging.ERROR, msg)
|
||||
raise APIError(msg)
|
||||
|
||||
def handle_response_500(self, response, prefix):
|
||||
msg = '{} {}: {}'.format(
|
||||
prefix, response.status_code, response.text)
|
||||
log(logging.ERROR, msg)
|
||||
raise APIError(msg)
|
||||
|
||||
def handle_response_error(self, prefix, error):
|
||||
if 'Invalid or expired access token' in error:
|
||||
raise APIError(error)
|
||||
msg = '{} {}'.format(prefix, error)
|
||||
raise APIError(msg)
|
||||
|
||||
def handle_response(self, response):
|
||||
"""Handles the response, e.g. errors or conversion to JSON"""
|
||||
prefix = 'APIError'
|
||||
if response.status_code == 404:
|
||||
self.handle_response_404(response, prefix)
|
||||
elif response.status_code == 500:
|
||||
self.handle_response_500(response, prefix)
|
||||
elif response.status_code in [204]:
|
||||
return response.text
|
||||
else:
|
||||
data = response.json()
|
||||
if 'error' in data:
|
||||
self.handle_response_error(prefix, data['error'])
|
||||
return data
|
||||
|
||||
def start_session(self, session_data):
|
||||
"""
|
||||
Starts a session with given session_data:
|
||||
- Authenticator class name (e.g. obp.oauth.OAuthAuthenticator)
|
||||
- Token data
|
||||
for subsequent requests to the API
|
||||
"""
|
||||
if 'authenticator' in session_data and\
|
||||
'authenticator_kwargs' in session_data:
|
||||
mod_name, cls_name = session_data['authenticator'].rsplit('.', 1)
|
||||
log(logging.INFO, 'Authenticator {}'.format(cls_name))
|
||||
cls = getattr(importlib.import_module(mod_name), cls_name)
|
||||
authenticator = cls(**session_data['authenticator_kwargs'])
|
||||
self.session = authenticator.get_session()
|
||||
return self.session
|
||||
else:
|
||||
return None
|
||||
|
||||
def get_swagger(self):
|
||||
"""Gets the swagger definition from the API"""
|
||||
if not self.session_data.get('swagger'):
|
||||
self.set_base_path(settings.API_SWAGGER_BASE_PATH)
|
||||
urlpath = '/resource-docs/v3.0.0/swagger'
|
||||
response = self.get(urlpath)
|
||||
# Set base path back
|
||||
self.set_base_path()
|
||||
self.session_data['swagger'] = response
|
||||
return self.session_data.get('swagger')
|
||||
|
||||
def get_bank_id_choices(self):
|
||||
"""Gets a list of bank ids and bank ids as used by form choices"""
|
||||
choices = [('', 'Choose ...')]
|
||||
result = self.get('/banks')
|
||||
for bank in result['banks']:
|
||||
choices.append((bank['id'], bank['id']))
|
||||
return choices
|
||||
|
||||
def get_user_id_choices(self):
|
||||
"""Gets a list of user ids and usernames as used by form choices"""
|
||||
choices = [('', 'Choose ...')]
|
||||
result = self.get('/users')
|
||||
for user in result['users']:
|
||||
choices.append((user['user_id'], user['username']))
|
||||
return choices
|
||||
11
apimanager/obp/apps.py
Normal file
11
apimanager/obp/apps.py
Normal file
@ -0,0 +1,11 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
App config for OBP app
|
||||
"""
|
||||
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class OBPConfig(AppConfig):
|
||||
"""Config for OBP"""
|
||||
name = 'obp'
|
||||
20
apimanager/obp/authenticator.py
Normal file
20
apimanager/obp/authenticator.py
Normal file
@ -0,0 +1,20 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Base authenticator for OBP app
|
||||
"""
|
||||
|
||||
import hashlib
|
||||
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth import login
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
|
||||
class AuthenticatorError(Exception):
|
||||
"""Exception class for Authenticator errors"""
|
||||
pass
|
||||
|
||||
|
||||
class Authenticator(object):
|
||||
"""Generic authenticator to the API"""
|
||||
pass
|
||||
51
apimanager/obp/directlogin.py
Normal file
51
apimanager/obp/directlogin.py
Normal file
@ -0,0 +1,51 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
DirectLogin authenticator for OBP app
|
||||
"""
|
||||
|
||||
|
||||
import requests
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
from .authenticator import Authenticator, AuthenticatorError
|
||||
|
||||
|
||||
class DirectLoginAuthenticator(Authenticator):
|
||||
"""Implements a DirectLogin authenticator to the API"""
|
||||
|
||||
token = None
|
||||
|
||||
def __init__(self, token=None):
|
||||
self.token = token
|
||||
|
||||
def login_to_api(self, data):
|
||||
"""
|
||||
Logs into the API and returns the token
|
||||
|
||||
data is a dict which contains keys username, password and consumer_key
|
||||
"""
|
||||
url = settings.API_HOST + settings.DIRECTLOGIN_PATH
|
||||
authorization = 'DirectLogin username="{}",password="{}",consumer_key="{}"'.format( # noqa
|
||||
data['username'],
|
||||
data['password'],
|
||||
data['consumer_key'])
|
||||
headers = {'Authorization': authorization}
|
||||
|
||||
try:
|
||||
response = requests.get(url, headers=headers)
|
||||
except requests.exceptions.ConnectionError as err:
|
||||
raise AuthenticatorError(err)
|
||||
|
||||
result = response.json()
|
||||
if response.status_code != 200:
|
||||
raise AuthenticatorError(result['error'])
|
||||
else:
|
||||
self.token = result['token']
|
||||
|
||||
def get_session(self):
|
||||
"""Returns a session object to make authenticated requests"""
|
||||
headers = {'Authorization': 'DirectLogin token={}'.format(self.token)}
|
||||
session = requests.Session()
|
||||
session.headers.update(headers)
|
||||
return session
|
||||
53
apimanager/obp/forms.py
Normal file
53
apimanager/obp/forms.py
Normal file
@ -0,0 +1,53 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Forms for OBP app
|
||||
"""
|
||||
|
||||
from django import forms
|
||||
|
||||
from .authenticator import AuthenticatorError
|
||||
from .directlogin import DirectLoginAuthenticator
|
||||
from .gatewaylogin import GatewayLoginAuthenticator
|
||||
|
||||
|
||||
class DirectLoginForm(forms.Form):
|
||||
username = forms.CharField(widget=forms.TextInput(
|
||||
attrs={'class': 'form-control'}))
|
||||
password = forms.CharField(widget=forms.PasswordInput(
|
||||
attrs={'class': 'form-control'}))
|
||||
consumer_key = forms.CharField(widget=forms.TextInput(
|
||||
attrs={'class': 'form-control'}))
|
||||
|
||||
def clean(self):
|
||||
"""
|
||||
Stores an authenticator in cleaned_data after successful login to API
|
||||
"""
|
||||
cleaned_data = super(DirectLoginForm, self).clean()
|
||||
authenticator = DirectLoginAuthenticator()
|
||||
try:
|
||||
authenticator.login_to_api(cleaned_data)
|
||||
cleaned_data['authenticator'] = authenticator
|
||||
except AuthenticatorError as err:
|
||||
raise forms.ValidationError(err)
|
||||
return cleaned_data
|
||||
|
||||
|
||||
class GatewayLoginForm(forms.Form):
|
||||
username = forms.CharField(widget=forms.TextInput(
|
||||
attrs={'class': 'form-control'}))
|
||||
secret = forms.CharField(widget=forms.PasswordInput(
|
||||
attrs={'class': 'form-control'}))
|
||||
|
||||
def clean(self):
|
||||
"""
|
||||
Stores an authenticator in cleaned_data after successful login to API
|
||||
"""
|
||||
cleaned_data = super(GatewayLoginForm, self).clean()
|
||||
authenticator = GatewayLoginAuthenticator()
|
||||
try:
|
||||
authenticator.login_to_api(cleaned_data)
|
||||
cleaned_data['authenticator'] = authenticator
|
||||
except AuthenticatorError as err:
|
||||
raise forms.ValidationError(err)
|
||||
return cleaned_data
|
||||
|
||||
67
apimanager/obp/gatewaylogin.py
Normal file
67
apimanager/obp/gatewaylogin.py
Normal file
@ -0,0 +1,67 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
GatewayLogin authenticator for OBP app
|
||||
"""
|
||||
|
||||
|
||||
import jwt
|
||||
import requests
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
from .authenticator import Authenticator, AuthenticatorError
|
||||
|
||||
|
||||
class GatewayLoginAuthenticator(Authenticator):
|
||||
"""Implements a GatewayLogin authenticator to the API"""
|
||||
|
||||
token = None
|
||||
|
||||
def __init__(self, token=None):
|
||||
self.token = token
|
||||
|
||||
def create_jwt(self, data):
|
||||
"""
|
||||
Creates a JWT used for future requests tothe API
|
||||
data is a dict which contains keys username, secret
|
||||
"""
|
||||
url = settings.API_HOST + settings.DIRECTLOGIN_PATH
|
||||
message = {
|
||||
'username': data['username'],
|
||||
'timestamp': 'unused',
|
||||
'consumer_id': '', # Do not create new consumer
|
||||
'consumer_name': '', # Do not create new consumer
|
||||
}
|
||||
if settings.GATEWAYLOGIN_HAS_CBS:
|
||||
# Not sure if that is the right thing to do
|
||||
message['is_first'] = True
|
||||
else:
|
||||
# Fake when there is no core banking system
|
||||
message.update({
|
||||
'is_first': False,
|
||||
'CBS_auth_token': 'dummy',
|
||||
})
|
||||
token = jwt.encode(message, data['secret'], 'HS256')
|
||||
self.token = token.decode('utf-8')
|
||||
return self.token
|
||||
|
||||
def login_to_api(self, data):
|
||||
token = self.create_jwt(data)
|
||||
# Make a test call to see if the token works
|
||||
url = settings.API_HOST + settings.API_BASE_PATH + '/users/current'
|
||||
api = self.get_session()
|
||||
try:
|
||||
response = api.get(url)
|
||||
except requests.exceptions.ConnectionError as err:
|
||||
raise AuthenticationError(err)
|
||||
if response.status_code != 200:
|
||||
raise AuthenticatorError(response.json()['error'])
|
||||
else:
|
||||
return token
|
||||
|
||||
def get_session(self):
|
||||
"""Returns a session object to make authenticated requests"""
|
||||
headers = {'Authorization': 'GatewayLogin token="{}"'.format(self.token)}
|
||||
session = requests.Session()
|
||||
session.headers.update(headers)
|
||||
return session
|
||||
77
apimanager/obp/oauth.py
Normal file
77
apimanager/obp/oauth.py
Normal file
@ -0,0 +1,77 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
OAuth authenticator for OBP app
|
||||
"""
|
||||
|
||||
import logging
|
||||
import time
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
from requests.exceptions import ConnectionError
|
||||
from requests_oauthlib import OAuth1Session
|
||||
from requests_oauthlib.oauth1_session import TokenRequestDenied
|
||||
|
||||
from .authenticator import Authenticator, AuthenticatorError
|
||||
|
||||
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class OAuthAuthenticator(Authenticator):
|
||||
"""Implements an OAuth authenticator to the API"""
|
||||
|
||||
token = None
|
||||
secret = None
|
||||
|
||||
def __init__(self, token=None, secret=None):
|
||||
self.token = token
|
||||
self.secret = secret
|
||||
|
||||
def get_authorization_url(self, callback_uri):
|
||||
session = OAuth1Session(
|
||||
settings.OAUTH_CONSUMER_KEY,
|
||||
client_secret=settings.OAUTH_CONSUMER_SECRET,
|
||||
callback_uri=callback_uri,
|
||||
)
|
||||
try:
|
||||
url = settings.API_HOST + settings.OAUTH_TOKEN_PATH
|
||||
response = session.fetch_request_token(url)
|
||||
except (ValueError, TokenRequestDenied, ConnectionError) as err:
|
||||
raise AuthenticatorError(err)
|
||||
else:
|
||||
self.token = response.get('oauth_token')
|
||||
self.secret = response.get('oauth_token_secret')
|
||||
url = settings.API_HOST + settings.OAUTH_AUTHORIZATION_PATH
|
||||
authorization_url = session.authorization_url(url)
|
||||
LOGGER.log(logging.INFO, 'Initial token {}, secret {}'.format(
|
||||
self.token, self.secret))
|
||||
return authorization_url
|
||||
|
||||
def set_access_token(self, authorization_url):
|
||||
session = OAuth1Session(
|
||||
settings.OAUTH_CONSUMER_KEY,
|
||||
settings.OAUTH_CONSUMER_SECRET,
|
||||
resource_owner_key=self.token,
|
||||
resource_owner_secret=self.secret,
|
||||
)
|
||||
session.parse_authorization_response(authorization_url)
|
||||
url = settings.API_HOST + settings.OAUTH_ACCESS_TOKEN_PATH
|
||||
try:
|
||||
response = session.fetch_access_token(url)
|
||||
except (TokenRequestDenied, ConnectionError) as err:
|
||||
raise AuthenticatorError(err)
|
||||
else:
|
||||
self.token = response.get('oauth_token')
|
||||
self.secret = response.get('oauth_token_secret')
|
||||
LOGGER.log(logging.INFO, 'Updated token {}, secret {}'.format(
|
||||
self.token, self.secret))
|
||||
|
||||
def get_session(self):
|
||||
session = OAuth1Session(
|
||||
settings.OAUTH_CONSUMER_KEY,
|
||||
client_secret=settings.OAUTH_CONSUMER_SECRET,
|
||||
resource_owner_key=self.token,
|
||||
resource_owner_secret=self.secret,
|
||||
)
|
||||
return session
|
||||
25
apimanager/obp/templates/obp/directlogin.html
Normal file
25
apimanager/obp/templates/obp/directlogin.html
Normal file
@ -0,0 +1,25 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<h1>DirectLogin</h1>
|
||||
<form action="{% url 'directlogin' %}" method="post">
|
||||
{% if form.non_field_errors %}<div class="alert alert-danger">{{ form.non_field_errors }}</div>{% endif %}
|
||||
{% csrf_token %}
|
||||
<div class="form-group">
|
||||
{% if form.username.errors %}<div class="alert alert-danger">{{ form.username.errors }}</div>{% endif %}
|
||||
<label for="username">Username:</label>
|
||||
{{ form.username }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
{% if form.password.errors %}<div class="alert alert-danger">{{ form.password.errors }}</div>{% endif %}
|
||||
<label for="password">Password:</label>
|
||||
{{ form.password }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
{% if form.consumer_key.errors %}<div class="alert alert-danger">{{ form.consumer_key.errors }}</div>{% endif %}
|
||||
<label for="consumer-key">Consumer Key:</label>
|
||||
{{ form.consumer_key }}
|
||||
</div>
|
||||
<button class="btn btn-primary">Login</button>
|
||||
</form>
|
||||
{% endblock content %}
|
||||
20
apimanager/obp/templates/obp/gatewaylogin.html
Normal file
20
apimanager/obp/templates/obp/gatewaylogin.html
Normal file
@ -0,0 +1,20 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<h1>GatewayLogin</h1>
|
||||
<form action="{% url 'gatewaylogin' %}" method="post">
|
||||
{% if form.non_field_errors %}<div class="alert alert-danger">{{ form.non_field_errors }}</div>{% endif %}
|
||||
{% csrf_token %}
|
||||
<div class="form-group">
|
||||
{% if form.username.errors %}<div class="alert alert-danger">{{ form.username.errors }}</div>{% endif %}
|
||||
<label for="username">Username:</label>
|
||||
{{ form.username }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
{% if form.secret.errors %}<div class="alert alert-danger">{{ form.secret.errors }}</div>{% endif %}
|
||||
<label for="secret">Secret:</label>
|
||||
{{ form.secret }}
|
||||
</div>
|
||||
<button class="btn btn-primary">Login</button>
|
||||
</form>
|
||||
{% endblock content %}
|
||||
27
apimanager/obp/urls.py
Normal file
27
apimanager/obp/urls.py
Normal file
@ -0,0 +1,27 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
URLs for OBP app
|
||||
"""
|
||||
|
||||
from django.conf.urls import url
|
||||
|
||||
from .views import (
|
||||
OAuthInitiateView, OAuthAuthorizeView,
|
||||
DirectLoginView,
|
||||
GatewayLoginView,
|
||||
LogoutView,
|
||||
)
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^oauth/initiate$',
|
||||
OAuthInitiateView.as_view(), name='oauth-initiate'),
|
||||
url(r'^oauth/authorize$',
|
||||
OAuthAuthorizeView.as_view(), name='oauth-authorize'),
|
||||
url(r'^directlogin$',
|
||||
DirectLoginView.as_view(), name='directlogin'),
|
||||
url(r'^gatewaylogin$',
|
||||
GatewayLoginView.as_view(), name='gatewaylogin'),
|
||||
url(r'^logout$',
|
||||
LogoutView.as_view(), name='oauth-logout'),
|
||||
]
|
||||
160
apimanager/obp/views.py
Normal file
160
apimanager/obp/views.py
Normal file
@ -0,0 +1,160 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Views for OBP app
|
||||
"""
|
||||
|
||||
|
||||
import hashlib
|
||||
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth import login, logout
|
||||
from django.contrib.auth.models import User
|
||||
from django.urls import reverse
|
||||
from django.views.generic import RedirectView, FormView
|
||||
|
||||
from .api import API, APIError
|
||||
from .authenticator import AuthenticatorError
|
||||
from .forms import DirectLoginForm, GatewayLoginForm
|
||||
from .oauth import OAuthAuthenticator
|
||||
|
||||
|
||||
class LoginToDjangoMixin(object):
|
||||
"""Mixin to login to Django from views."""
|
||||
def login_to_django(self):
|
||||
"""
|
||||
Logs the user into Django
|
||||
Kind of faking it to establish if a user is authenticated later on
|
||||
"""
|
||||
api = API(self.request.session.get('obp'))
|
||||
try:
|
||||
data = api.get('/users/current')
|
||||
except APIError as err:
|
||||
messages.error(self.request, err)
|
||||
return False
|
||||
else:
|
||||
userid = data['user_id'] or data['email']
|
||||
username = hashlib.sha256(userid.encode('utf-8')).hexdigest()
|
||||
password = username
|
||||
user, _ = User.objects.get_or_create(
|
||||
username=username, password=password,
|
||||
)
|
||||
login(self.request, user)
|
||||
return True
|
||||
|
||||
|
||||
class OAuthInitiateView(RedirectView):
|
||||
"""View to initiate OAuth session"""
|
||||
|
||||
def get_callback_uri(self, request):
|
||||
"""
|
||||
Gets the callback URI to where the user shall be returned after
|
||||
initiation at OAuth server
|
||||
"""
|
||||
base_url = '{}://{}'.format(
|
||||
request.scheme, request.environ['HTTP_HOST'])
|
||||
uri = base_url + reverse('oauth-authorize')
|
||||
if 'next' in request.GET:
|
||||
uri = '{}?next={}'.format(uri, request.GET['next'])
|
||||
return uri
|
||||
|
||||
def get_redirect_url(self, *args, **kwargs):
|
||||
callback_uri = self.get_callback_uri(self.request)
|
||||
try:
|
||||
authenticator = OAuthAuthenticator()
|
||||
authorization_url = authenticator.get_authorization_url(
|
||||
callback_uri)
|
||||
except AuthenticatorError as err:
|
||||
messages.error(self.request, err)
|
||||
return reverse('home')
|
||||
else:
|
||||
self.request.session['obp'] = {
|
||||
'authenticator': 'obp.oauth.OAuthAuthenticator',
|
||||
'authenticator_kwargs': {
|
||||
'token': authenticator.token,
|
||||
'secret': authenticator.secret,
|
||||
}
|
||||
}
|
||||
return authorization_url
|
||||
|
||||
|
||||
class OAuthAuthorizeView(RedirectView, LoginToDjangoMixin):
|
||||
"""View to authorize user after OAuth 1 initiation"""
|
||||
|
||||
def get_redirect_url(self, *args, **kwargs):
|
||||
session_data = self.request.session.get('obp')
|
||||
authenticator_kwargs = session_data.get('authenticator_kwargs')
|
||||
authenticator = OAuthAuthenticator(**authenticator_kwargs)
|
||||
authorization_url = self.request.build_absolute_uri()
|
||||
try:
|
||||
authenticator.set_access_token(authorization_url)
|
||||
except AuthenticatorError as err:
|
||||
messages.error(self.request, err)
|
||||
else:
|
||||
session_data['authenticator_kwargs'] = {
|
||||
'token': authenticator.token,
|
||||
'secret': authenticator.secret,
|
||||
}
|
||||
self.login_to_django()
|
||||
messages.success(self.request, 'OAuth login successful!')
|
||||
redirect_url = self.request.GET.get('next', reverse('home'))
|
||||
return redirect_url
|
||||
|
||||
|
||||
class DirectLoginView(FormView, LoginToDjangoMixin):
|
||||
"""View to login via DirectLogin"""
|
||||
form_class = DirectLoginForm
|
||||
template_name = 'obp/directlogin.html'
|
||||
|
||||
def get_success_url(self):
|
||||
messages.success(self.request, 'DirectLogin successful!')
|
||||
return reverse('runtests-index')
|
||||
|
||||
def form_valid(self, form):
|
||||
"""
|
||||
Stores a DirectLogin token in the request's session for use in
|
||||
future requests. It also logs in to Django.
|
||||
"""
|
||||
authenticator = form.cleaned_data['authenticator']
|
||||
self.request.session['obp'] = {
|
||||
'authenticator': 'obp.directlogin.DirectLoginAuthenticator',
|
||||
'authenticator_kwargs': {
|
||||
'token': authenticator.token,
|
||||
}
|
||||
}
|
||||
self.login_to_django()
|
||||
return super(DirectLoginView, self).form_valid(form)
|
||||
|
||||
|
||||
class GatewayLoginView(FormView, LoginToDjangoMixin):
|
||||
"""View to login via GatewayLogin"""
|
||||
form_class = GatewayLoginForm
|
||||
template_name = 'obp/gatewaylogin.html'
|
||||
|
||||
def get_success_url(self):
|
||||
messages.success(self.request, 'GatewayLogin successful!')
|
||||
return reverse('runtests-index')
|
||||
|
||||
def form_valid(self, form):
|
||||
"""
|
||||
Stores a GatewayLogin token in the request's session for use in
|
||||
future requests. It also logs in to Django.
|
||||
"""
|
||||
authenticator = form.cleaned_data['authenticator']
|
||||
self.request.session['obp'] = {
|
||||
'authenticator': 'obp.gatewaylogin.GatewayLoginAuthenticator',
|
||||
'authenticator_kwargs': {
|
||||
'token': authenticator.token,
|
||||
}
|
||||
}
|
||||
self.login_to_django()
|
||||
return super(GatewayLoginView, self).form_valid(form)
|
||||
|
||||
|
||||
class LogoutView(RedirectView):
|
||||
"""View to logout"""
|
||||
|
||||
def get_redirect_url(self, *args, **kwargs):
|
||||
logout(self.request)
|
||||
if 'obp' in self.request.session:
|
||||
del self.request.session['obp']
|
||||
return reverse('home')
|
||||
@ -10,8 +10,7 @@ from django.urls import reverse, reverse_lazy
|
||||
from django.views.generic import FormView, TemplateView, View
|
||||
|
||||
from base.filters import BaseFilter
|
||||
from base.api import api, APIError
|
||||
from base.api_helper import get_bank_id_choices
|
||||
from obp.api import API, APIError
|
||||
|
||||
from .forms import AddEntitlementForm
|
||||
|
||||
@ -42,9 +41,10 @@ class IndexView(LoginRequiredMixin, TemplateView):
|
||||
|
||||
def get_users_rolenames(self, context):
|
||||
users = []
|
||||
api = API(self.request.session.get('obp'))
|
||||
try:
|
||||
urlpath = '/users'
|
||||
users = api.get(self.request, urlpath)
|
||||
users = api.get(urlpath)
|
||||
except APIError as err:
|
||||
messages.error(self.request, err)
|
||||
return [], []
|
||||
@ -85,9 +85,13 @@ class DetailView(LoginRequiredMixin, FormView):
|
||||
form_class = AddEntitlementForm
|
||||
template_name = 'users/detail.html'
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
self.api = API(request.session.get('obp'))
|
||||
return super(DetailView, self).dispatch(request, *args,**kwargs)
|
||||
|
||||
def get_form(self, *args, **kwargs):
|
||||
form = super(DetailView, self).get_form(*args, **kwargs)
|
||||
form.fields['bank_id'].choices = get_bank_id_choices(self.request)
|
||||
form.fields['bank_id'].choices = self.api.get_bank_id_choices()
|
||||
return form
|
||||
|
||||
def form_valid(self, form):
|
||||
@ -99,7 +103,7 @@ class DetailView(LoginRequiredMixin, FormView):
|
||||
'bank_id': data['bank_id'],
|
||||
'role_name': data['role_name'],
|
||||
}
|
||||
entitlement = api.post(self.request, urlpath, payload=payload)
|
||||
entitlement = self.api.post(urlpath, payload=payload)
|
||||
except APIError as err:
|
||||
messages.error(self.request, err)
|
||||
return super(DetailView, self).form_invalid(form)
|
||||
@ -112,13 +116,12 @@ class DetailView(LoginRequiredMixin, FormView):
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(DetailView, self).get_context_data(**kwargs)
|
||||
|
||||
# NOTE: assuming there is just one user with that email address
|
||||
# The API needs a call 'get user by id'!
|
||||
user = {}
|
||||
try:
|
||||
urlpath = '/users/user_id/{}'.format(self.kwargs['user_id'])
|
||||
user = api.get(self.request, urlpath)
|
||||
user = self.api.get(urlpath)
|
||||
context['form'].fields['user_id'].initial = user['user_id']
|
||||
except APIError as err:
|
||||
messages.error(self.request, err)
|
||||
@ -134,10 +137,11 @@ class DeleteEntitlementView(LoginRequiredMixin, View):
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
"""Deletes entitlement from API"""
|
||||
api = API(self.request.session.get('obp'))
|
||||
try:
|
||||
urlpath = '/users/{}/entitlement/{}'.format(
|
||||
kwargs['user_id'], kwargs['entitlement_id'])
|
||||
api.delete(request, urlpath)
|
||||
api.delete(urlpath)
|
||||
msg = 'Entitlement with role {} has been deleted.'.format(
|
||||
request.POST.get('role_name', '<undefined>'))
|
||||
messages.success(request, msg)
|
||||
|
||||
@ -2,4 +2,5 @@ Django==1.11.5
|
||||
oauthlib==2.0.0
|
||||
requests==2.11.1
|
||||
requests-oauthlib==0.6.2
|
||||
PyJWT==1.5.3
|
||||
gunicorn==19.6.0
|
||||
|
||||
Loading…
Reference in New Issue
Block a user