Skip to content

Commit

Permalink
Merge pull request #3096 from jeff1evesque/feature-2869
Browse files Browse the repository at this point in the history
#2869: Determine programmatic user login validation
  • Loading branch information
jeff1evesque committed Nov 4, 2017
2 parents b7341ab + ffecdf3 commit dd1b778
Show file tree
Hide file tree
Showing 13 changed files with 291 additions and 54 deletions.
24 changes: 21 additions & 3 deletions brain/load_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@
'''

import json
from flask import current_app, session
from brain.session.data_append import DataAppend
from brain.session.data_new import DataNew
from brain.session.model_generate import ModelGenerate
from brain.session.model_predict import ModelPredict
from brain.database.session import Session
from brain.database.account import Account


class Load_Data(object):
Expand All @@ -29,7 +31,7 @@ class Load_Data(object):
'''

def __init__(self, data):
def __init__(self, data, username=None):
'''
This constructor is responsible for defining class variables.
Expand All @@ -45,6 +47,22 @@ def __init__(self, data):
]
self.list_error = []

# flask session user
if 'uid' in session and session['uid']:
self.uid = session['uid']

# flask jwt-token user
elif username:
uid = Account().get_uid(username)
if uid['result']:
self.uid = uid['result']
else:
self.uid = current_app.config.get('USER_ID')

# unauthenticated user
else:
self.uid = current_app.config.get('USER_ID')

def load_data_new(self):
'''
Expand All @@ -54,7 +72,7 @@ def load_data_new(self):
'''

# instantiate class
session = DataNew(self.data)
session = DataNew(self.data, self.uid)

# implement class methods
if not session.validate_arg_none():
Expand Down Expand Up @@ -89,7 +107,7 @@ def load_data_append(self):
'''

# instantiate class
session = DataAppend(self.data)
session = DataAppend(self.data, self.uid)

# define current session id
collection = self.data['properties']['collection']
Expand Down
9 changes: 4 additions & 5 deletions brain/session/base_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

import datetime
from brain.session.base import Base
from flask import current_app, session
from flask import current_app
from brain.session.data.dataset import dataset2dict
from brain.database.dataset import Collection
from brain.database.entity import Entity
Expand All @@ -32,7 +32,7 @@ class BaseData(Base):
'''

def __init__(self, premodel_data):
def __init__(self, premodel_data, uid):
'''
This constructor inherits additional class properties, from the
Expand All @@ -50,8 +50,8 @@ def __init__(self, premodel_data):
self.model_type = premodel_data['properties']['model_type']
self.premodel_data = premodel_data

if 'uid' in session:
self.uid = session['uid']
if uid:
self.uid = uid
self.max_collection = current_app.config.get('MAXCOL_AUTH')
self.max_document = current_app.config.get('MAXDOC_AUTH')

Expand Down Expand Up @@ -116,7 +116,6 @@ def save_premodel_dataset(self):
):
current_utc = datetime.datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S")
self.premodel_data['properties']['datetime_saved'] = current_utc
self.premodel_data['properties']['uid'] = self.uid
document = {
'properties': self.premodel_data['properties'],
'dataset': self.dataset
Expand Down
4 changes: 2 additions & 2 deletions brain/session/data_append.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class DataAppend(BaseData):
'''

def __init__(self, premodel_data):
def __init__(self, premodel_data, uid):
'''
This constructor inherits additional class properties, from the
Expand All @@ -38,7 +38,7 @@ def __init__(self, premodel_data):
'''

# superclass constructor
BaseData.__init__(self, premodel_data)
BaseData.__init__(self, premodel_data, uid)

# class variable
if session.get('uid'):
Expand Down
4 changes: 2 additions & 2 deletions brain/session/data_new.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class DataNew(BaseData):
'''

def __init__(self, premodel_data):
def __init__(self, premodel_data, uid):
'''
This constructor inherits additional class properties, from the
Expand All @@ -36,7 +36,7 @@ def __init__(self, premodel_data):
'''

# superclass constructor
BaseData.__init__(self, premodel_data)
BaseData.__init__(self, premodel_data, uid)

def save_entity(self, session_type, id_entity=None):
'''
Expand Down
64 changes: 64 additions & 0 deletions doc/programmatic_interface/authentication/login.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
=====
Login
=====

Users can submit a ``/login`` request, for the programmatic-api:

.. code:: python
# request attributes
username = 'jeff1evesque'
password = 'password123'
payload = {'user[login]': username, 'user[password]': password}
# login and get flask-jwt token
login = client.post(
'/login',
headers={'Content-Type': 'application/json'},
data=payload
)
token = login.json['access_token']
The returned ``token`` value, can be supplied on successive requests, to a rest
endpoint, expecting the corresponding ``token`` value. For example, supplying
a valid token, to the ``/load-data`` endpoint:

.. code:: python
# request attributes
username = 'jeff1evesque'
password = 'password123'
payload = {'user[login]': username, 'user[password]': password}
# login and get flask-jwt token
login = client.post(
'/login',
headers={'Content-Type': 'application/json'},
data=payload
)
token = login.json['access_token']
# provide flask-jwt token
login = client.post(
'/load-data',
headers={
'Authorization': 'Bearer ' + token,
'Content-Type': 'application/json'
},
data=payload
)
The following sessions, can be implemented with the above token, or omitted as
an anonymous user:

- `data-new <https://github.com/jeff1evesque/machine-learning/blob/master/doc/programmatic_interface/data/data_new.rst>`_
- `data-append <https://github.com/jeff1evesque/machine-learning/blob/master/doc/programmatic_interface/data/data_new.rst>`_

The following unit tests, provides examples of the `flask-jwt <http:https://flask-jwt-extended.readthedocs.io/en/latest/>`_
implementation:

- |pytest_6_user_session.py|_

.. |pytest_6_user_session.py| replace:: ``pytest_6_user_session.py``
.. _pytest_6_user_session.py: https://github.com/jeff1evesque/machine-learning/tree/master/test/live_server/1_authentication/pytest_6_user_login.py

18 changes: 18 additions & 0 deletions doc/programmatic_interface/data/data_append.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,24 @@ the ``data`` attribute, in a given ``POST`` request:
requests.post(endpoint_url, headers=headers, data=json_string_here)
To submit a ``/load-data`` request, as a valid authenticated user, a valid token
must be suppied to the ``headers``:

.. code:: python
import requests
endpoint_url = 'https://localhost:8080/load-data'
headers={
'Authorization': 'Bearer ' + token,
'Content-Type': 'application/json'
},
requests.post(endpoint_url, headers=headers, data=json_string_here)
**Note:** more information, regarding how to obtain a valid ``token``, can be further
reviewed, in the ``/login`` `documentation <https://github.com/jeff1evesque/machine-learning/tree/master/doc/programmatic_interface/authentication/login.rst>`_.

The following properties define the above ``data`` attribute:

- ``collection``: collection of dataset documents, used as a reference to add additional dataset documents into
Expand Down
18 changes: 18 additions & 0 deletions doc/programmatic_interface/data/data_new.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,24 @@ the ``data`` attribute, in a given ``POST`` request:
requests.post(endpoint_url, headers=headers, data=json_string_here)
To submit a ``/load-data`` request, as a valid authenticated user, a valid token
must be suppied to the ``headers``:

.. code:: python
import requests
endpoint_url = 'https://localhost:8080/load-data'
headers={
'Authorization': 'Bearer ' + token,
'Content-Type': 'application/json'
},
requests.post(endpoint_url, headers=headers, data=json_string_here)
**Note:** more information, regarding how to obtain a valid ``token``, can be further
reviewed, in the ``/login`` `documentation <https://github.com/jeff1evesque/machine-learning/tree/master/doc/programmatic_interface/authentication/login.rst>`_.

The following properties define the above ``data`` attribute:

- ``session_name``: title for the corresponding ``data_new`` session
Expand Down
4 changes: 4 additions & 0 deletions factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from logging.handlers import RotatingFileHandler
from brain.cache.session import RedisSessionInterface
from interface.views import blueprint
from flask_jwt_extended import JWTManager


# application factory
Expand Down Expand Up @@ -80,6 +81,9 @@ def create_app(args={'prefix': '', 'settings': ''}):
# register blueprint
app.register_blueprint(blueprint)

# set the flask-jwt-extended extension
JWTManager(app)

# local logger: used for this module
root = general['root']
LOG_PATH = root + webserver['flask']['log_path']
Expand Down
1 change: 1 addition & 0 deletions hiera/packages.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ development:
pip:
general:
flask-script: '2.0.5'
flask-jwt-extended: '3.3.4'
jsonschema: '2.5.1'
pytest-flask: '0.10.0'
six: '1.5.2'
Expand Down
Loading

0 comments on commit dd1b778

Please sign in to comment.