From 35960ad2dfbea71fc453e4f0dc8b0c5bafc5af4d Mon Sep 17 00:00:00 2001 From: jokajak Date: Sun, 2 Aug 2015 13:58:05 -0400 Subject: [PATCH 1/4] Add a gitignore file for flask backend This file will allow virtualenv's to be created in the src directory for development of the flask backend directly. --- backend/flask/src/.gitignore | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 backend/flask/src/.gitignore diff --git a/backend/flask/src/.gitignore b/backend/flask/src/.gitignore new file mode 100644 index 0000000..6d40e51 --- /dev/null +++ b/backend/flask/src/.gitignore @@ -0,0 +1,6 @@ +bin/ +lib/ +include/ +clipperz.egg-info/ +.Python +app.db \ No newline at end of file From 3a1188a779dbb9e63aaee48499fe16b9b29c5489 Mon Sep 17 00:00:00 2001 From: jokajak Date: Sun, 2 Aug 2015 14:01:02 -0400 Subject: [PATCH 2/4] Fix flask model to support sqlalchemy-migrate --- backend/flask/src/clipperz/models.py | 32 ++++-- backend/flask/src/db_create.py | 2 +- backend/flask/src/db_migrate.py | 2 +- .../db_repository/versions/001_migration.py | 97 +++++++++++++++++++ backend/flask/src/setup.py | 1 + 5 files changed, 124 insertions(+), 10 deletions(-) create mode 100644 backend/flask/src/db_repository/versions/001_migration.py diff --git a/backend/flask/src/clipperz/models.py b/backend/flask/src/clipperz/models.py index 9737f37..8de66a1 100644 --- a/backend/flask/src/clipperz/models.py +++ b/backend/flask/src/clipperz/models.py @@ -59,9 +59,9 @@ class RecordVersion(db.Model): previous_version_key = db.Column(db.String()) previous_version_id = db.Column(db.Integer(), db.ForeignKey('record_version.id')) - creation_date = db.Column(db.DateTime(), default=datetime.datetime.utcnow) - update_date = db.Column(db.DateTime(), default=datetime.datetime.utcnow) - access_date = db.Column(db.DateTime(), default=datetime.datetime.utcnow) + creation_date = db.Column(db.DateTime()) + update_date = db.Column(db.DateTime()) + access_date = db.Column(db.DateTime()) record_id = db.Column(db.Integer(), db.ForeignKey('record.id'), @@ -72,6 +72,10 @@ class RecordVersion(db.Model): order_by=id, cascade='all,delete')) + def __init__(self): + """Initialize a record version.""" + self.creation_date = datetime.datetime.utcnow() + def update(self, someData): app.logger.debug(someData) recordVersionData = someData['currentRecordVersion'] @@ -93,15 +97,19 @@ class Record(db.Model): data = db.Column(db.Text()) api_version = db.Column(db.String()) version = db.Column(db.Integer(), default=0) - creation_date = db.Column(db.DateTime(), default=datetime.datetime.utcnow) - update_date = db.Column(db.DateTime(), default=datetime.datetime.utcnow) - access_date = db.Column(db.DateTime(), default=datetime.datetime.utcnow) + creation_date = db.Column(db.DateTime()) + update_date = db.Column(db.DateTime()) + access_date = db.Column(db.DateTime()) current_record_version = db.relationship( 'RecordVersion', uselist=False, cascade='save-update, merge, delete, delete-orphan') + def __init__(self): + """Initialize a record.""" + self.creation_date = datetime.datetime.utcnow() + def update(self, data, record_version): self.reference = data['reference'] self.data = data['data'] @@ -125,10 +133,14 @@ class OneTimePassword(db.Model): key_checksum = db.Column(db.String()) data = db.Column(db.Text()) version = db.Column(db.String()) - creation_date = db.Column(db.DateTime(), default=datetime.datetime.utcnow) + creation_date = db.Column(db.DateTime()) request_date = db.Column(db.DateTime()) usage_date = db.Column(db.DateTime()) + def __init__(self): + """Initialize a OneTimePassword.""" + self.creation_date = datetime.datetime.utcnow() + def update(self, someParameters, aStatus): self.reference = someParameters['reference'] self.key_value = someParameters['key'] @@ -148,4 +160,8 @@ class OneTimePassword(db.Model): class Session(db.Model): id = db.Column(db.Integer(), primary_key=True) sessionId = db.Column(db.String()) - access_date = db.Column(db.DateTime(), default=datetime.datetime.utcnow) + access_date = db.Column(db.DateTime()) + + def __init__(self): + """Initialize a session.""" + self.access_date = datetime.datetime.utcnow() diff --git a/backend/flask/src/db_create.py b/backend/flask/src/db_create.py index cd6ce69..2d62abb 100644 --- a/backend/flask/src/db_create.py +++ b/backend/flask/src/db_create.py @@ -2,7 +2,7 @@ from migrate.versioning import api from config import SQLALCHEMY_DATABASE_URI from config import SQLALCHEMY_MIGRATE_REPO -from app import db +from clipperz import db import os.path db.create_all() if not os.path.exists(SQLALCHEMY_MIGRATE_REPO): diff --git a/backend/flask/src/db_migrate.py b/backend/flask/src/db_migrate.py index 03f65d1..2eab93d 100644 --- a/backend/flask/src/db_migrate.py +++ b/backend/flask/src/db_migrate.py @@ -1,7 +1,7 @@ #!/usr/bin/env python import imp from migrate.versioning import api -from app import db +from clipperz import db from config import SQLALCHEMY_DATABASE_URI from config import SQLALCHEMY_MIGRATE_REPO v = api.db_version(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO) diff --git a/backend/flask/src/db_repository/versions/001_migration.py b/backend/flask/src/db_repository/versions/001_migration.py new file mode 100644 index 0000000..53f50c9 --- /dev/null +++ b/backend/flask/src/db_repository/versions/001_migration.py @@ -0,0 +1,97 @@ +from sqlalchemy import * +from migrate import * + + +from migrate.changeset import schema +pre_meta = MetaData() +post_meta = MetaData() +one_time_password = Table('one_time_password', post_meta, + Column('id', Integer, primary_key=True, nullable=False), + Column('user_id', Integer), + Column('status', String), + Column('reference', String), + Column('key_value', String), + Column('key_checksum', String), + Column('data', Text), + Column('version', String), + Column('creation_date', DateTime), + Column('request_date', DateTime), + Column('usage_date', DateTime), +) + +record = Table('record', post_meta, + Column('id', Integer, primary_key=True, nullable=False), + Column('user_id', Integer), + Column('reference', String), + Column('data', Text), + Column('api_version', String), + Column('version', Integer, default=ColumnDefault(0)), + Column('creation_date', DateTime), + Column('update_date', DateTime), + Column('access_date', DateTime), +) + +record_version = Table('record_version', post_meta, + Column('id', Integer, primary_key=True, nullable=False), + Column('reference', String), + Column('header', Text), + Column('data', Text), + Column('api_version', String), + Column('version', Integer), + Column('previous_version_key', String), + Column('previous_version_id', Integer), + Column('creation_date', DateTime), + Column('update_date', DateTime), + Column('access_date', DateTime), + Column('record_id', Integer, nullable=False), +) + +session = Table('session', post_meta, + Column('id', Integer, primary_key=True, nullable=False), + Column('sessionId', String), + Column('access_date', DateTime), +) + +sessions = Table('sessions', post_meta, + Column('key', String(length=250), primary_key=True, nullable=False), + Column('value', LargeBinary, nullable=False), +) + +user = Table('user', post_meta, + Column('id', Integer, primary_key=True, nullable=False), + Column('username', String), + Column('srp_s', String(length=128)), + Column('srp_v', String(length=128)), + Column('header', Text), + Column('statistics', Text), + Column('auth_version', String), + Column('version', String), + Column('lock', String), + Column('offline_saved', Boolean, default=ColumnDefault(False)), + Column('update_date', DateTime), +) + + +def upgrade(migrate_engine): + # Upgrade operations go here. Don't create your own engine; bind + # migrate_engine to your metadata + pre_meta.bind = migrate_engine + post_meta.bind = migrate_engine + post_meta.tables['one_time_password'].create() + post_meta.tables['record'].create() + post_meta.tables['record_version'].create() + post_meta.tables['session'].create() + post_meta.tables['sessions'].create() + post_meta.tables['user'].create() + + +def downgrade(migrate_engine): + # Operations to reverse the above upgrade go here. + pre_meta.bind = migrate_engine + post_meta.bind = migrate_engine + post_meta.tables['one_time_password'].drop() + post_meta.tables['record'].drop() + post_meta.tables['record_version'].drop() + post_meta.tables['session'].drop() + post_meta.tables['sessions'].drop() + post_meta.tables['user'].drop() diff --git a/backend/flask/src/setup.py b/backend/flask/src/setup.py index affb5e5..f024a18 100644 --- a/backend/flask/src/setup.py +++ b/backend/flask/src/setup.py @@ -45,6 +45,7 @@ setup( install_requires=['Flask>=0.10.1', 'Flask-SQLAlchemy>=1.0', 'SQLAlchemy>=0.8.2', + 'SQLAlchemy-migrate', 'Flask-Login', 'Flask-KVSession', ], From a141d2e8879841a3577c86e5021f02635572b218 Mon Sep 17 00:00:00 2001 From: jokajak Date: Sun, 2 Aug 2015 14:01:23 -0400 Subject: [PATCH 3/4] Add docstrings to flask backend --- backend/flask/src/clipperz/api.py | 101 ++++++++++++++++++++------- backend/flask/src/clipperz/models.py | 42 +++++++++-- backend/flask/src/clipperz/views.py | 16 ++++- 3 files changed, 128 insertions(+), 31 deletions(-) diff --git a/backend/flask/src/clipperz/api.py b/backend/flask/src/clipperz/api.py index 26d67d0..4f6a277 100644 --- a/backend/flask/src/clipperz/api.py +++ b/backend/flask/src/clipperz/api.py @@ -1,3 +1,4 @@ +"""Clipperz API handler.""" import json import random import hashlib @@ -12,27 +13,36 @@ from .exceptions import InvalidUsage from .models import User, Record, RecordVersion, OneTimePassword -#============================================================================== +# ============================================================================== # Helpers -#============================================================================== +# ============================================================================== def randomSeed(): + """Generate a random seed.""" return hex(random.getrandbits(32*8))[2:-1] def clipperzHash(aString): + """Calculate a clipperz hash. + + sha256(sha256(aString)) + """ firstRound = hashlib.sha256() firstRound.update(aString) result = hashlib.sha256() result.update(firstRound.digest()) return result.hexdigest() -#============================================================================== +# ============================================================================== # Method handlers -#============================================================================== +# ============================================================================== class HandlerMixin: + + """Mixin for handling requests.""" + def handle_request(self, request): + """Default method to handle a request.""" parameters = json.loads(request.form['parameters']) app.logger.debug('raw parameters: %s', parameters) parameters = parameters['parameters'] @@ -50,7 +60,14 @@ class HandlerMixin: class registration(HandlerMixin): + + """Registration handler.""" + def completeRegistration(self, parameters, request): + """Complete a registration. + + Create a new user. + """ credentials = parameters['credentials'] data = parameters['user'] user = User() @@ -63,11 +80,21 @@ class registration(HandlerMixin): class handshake(HandlerMixin): + + """Handshake handler. + + This handles the logon process. + """ + srp_n = '115b8b692e0e045692cf280b436735c77a5a9e8a9e7ed56c965f87db5b2a2ece3' srp_g = 2 srp_n = long(srp_n, 16) def connect(self, parameters, request): + """Process a connect request. + + Attempt to log in by processing the parameters. + """ result = {} session['C'] = parameters['parameters']['C'] session['A'] = parameters['parameters']['A'] @@ -127,8 +154,12 @@ class handshake(HandlerMixin): return jsonify({'result': result}) def credentialCheck(self, parameters, request): + """Check credentials. + + Handles the SRP process. + """ country = 'US' - # hard-coded for development + # hard-coded for development/personal use. result = { 'accountInfo': { 'features': [ @@ -203,14 +234,15 @@ class handshake(HandlerMixin): return jsonify({'result': result}) def oneTimePassword(self, parameters, request): - #"parameters": { - #"message": "oneTimePassword", - #"version": "0.2", - #"parameters": { - # "oneTimePasswordKey": "03bd882...396082c", - # "oneTimePasswordKeyChecksum": "f73f629...041031d" - #} - #} + """Handle one time password logins.""" + # "parameters": { + # "message": "oneTimePassword", + # "version": "0.2", + # "parameters": { + # "oneTimePasswordKey": "03bd882...396082c", + # "oneTimePasswordKeyChecksum": "f73f629...041031d" + # } + # } result = {} try: @@ -237,8 +269,12 @@ class handshake(HandlerMixin): class message(HandlerMixin): + + """Handle messages once logged in.""" + @login_required def getUserDetails(self, parameters, request): + """Get a user's details.""" app.logger.debug(parameters) if 'srpSharedSecret' not in parameters: raise InvalidUsage( @@ -252,7 +288,7 @@ class message(HandlerMixin): # Online results # {"result": # { - # "header": "{\"records\":{\"index\":{\"383036...eeefbe48\":\"0\"},\"data\":\"zrhb3/791SDdb48v3vXfPzeDrv0Jhs4rAaOKHx+jDF6pwm/qi9DGSR0JwrprOgwv3bjYJgU2xHA8cuA0bPvABHSHK6fnGwvhSlyYjskY2Cy/WbRJhcA4kw+VUsOjZPRxtM8bSJnSxViAXsghTcya6+5M3MdMJHE=\"},\"directLogins\":{\"index\":{},\"data\":\"s7KYzHwKISmjYufv9h0mpTiM\"},\"preferences\":{\"data\":\"mf8fWjpOQjlV18ukEO9FN3LP\"},\"oneTimePasswords\":{\"data\":\"8tV1yRHv30lsl3FadG9YnTOo\"},\"version\":\"0.1\"}", + # "header": "{\"records\":{\"index\":{\"383036...eeefbe48\":\"0\"},\"data\":\"zrhb3/791SDdb48v3vXfPzeDrv0Jhs4rAaOKHx+jDF6pwm/qi9DGSR0JwrprOgwv3bjYJgU2xHA8cuA0bPvABHSHK6fnGwvhSlyYjskY2Cy/WbRJhcA4kw+VUsOjZPRxtM8bSJnSxViAXsghTcya6+5M3MdMJHE=\"},\"directLogins\":{\"index\":{},\"data\":\"s7KYzHwKISmjYufv9h0mpTiM\"},\"preferences\":{\"data\":\"mf8fWjpOQjlV18ukEO9FN3LP\"},\"oneTimePasswords\":{\"data\":\"8tV1yRHv30lsl3FadG9YnTOo\"},\"version\":\"0.1\"}", # NOQA # "lock": "3D4B4501-D7A9-6E4F-A487-9428C0B6E79D", # "version": "0.4", # "recordsStats": { @@ -266,8 +302,8 @@ class message(HandlerMixin): # } # } # Dev results - #{"result": - # {"header": "{\"records\":{\"index\":{\"843a95d8...5f734b\":\"1\"},\"data\":\"fKgc5Jt9JH/CibCIpcRmwyLuLIvufWchNJga7GoFcWT9K8LR+ai0BvzWBUxcPccivE9zPv2Swe5E8wPEIc+Lv0U73NobJEct7WqBcCdLxszBE1SokxPEZDUVdWVQtAiwgOS219inCFmI5CaB\"},\"directLogins\":{\"index\":{},\"data\":\"rnMQBB81ezh6JKNGXkDCyY+q\"},\"preferences\":{\"data\":\"9jzR9Goo5PGpXbAdmsXHuQGp\"},\"oneTimePasswords\":{\"data\":\"iXEUuQGskZhMyHEwU+3tRGQM\"},\"version\":\"0.1\"}", + # {"result": + # {"header": "{\"records\":{\"index\":{\"843a95d8...5f734b\":\"1\"},\"data\":\"fKgc5Jt9JH/CibCIpcRmwyLuLIvufWchNJga7GoFcWT9K8LR+ai0BvzWBUxcPccivE9zPv2Swe5E8wPEIc+Lv0U73NobJEct7WqBcCdLxszBE1SokxPEZDUVdWVQtAiwgOS219inCFmI5CaB\"},\"directLogins\":{\"index\":{},\"data\":\"rnMQBB81ezh6JKNGXkDCyY+q\"},\"preferences\":{\"data\":\"9jzR9Goo5PGpXbAdmsXHuQGp\"},\"oneTimePasswords\":{\"data\":\"iXEUuQGskZhMyHEwU+3tRGQM\"},\"version\":\"0.1\"}", # NOQA # "recordStats": { # "843a95d8...5f734b": { # "updateDate": "Sun, 12 Apr 2015 13:08:44 GMT" @@ -295,6 +331,7 @@ class message(HandlerMixin): @login_required def saveChanges(self, parameters, request): + """Save changes to a user's settings.""" result = {} parameters = parameters['parameters'] if ('user' not in parameters @@ -340,7 +377,8 @@ class message(HandlerMixin): @login_required def getRecordDetail(self, parameters, request): - #{ + """Get details about a record.""" + # { # "parameters": { # "srpSharedSecret": "bf79ad3cf0c1...63462a9fb560", # "message": "getRecordDetail", @@ -348,7 +386,7 @@ class message(HandlerMixin): # "reference": "e3a5856...20e080fc97f13c14c" # } # } - #} + # } app.logger.debug(parameters) if 'srpSharedSecret' not in parameters: raise InvalidUsage( @@ -404,13 +442,14 @@ class message(HandlerMixin): @login_required def getOneTimePasswordsDetails(self, parameters, request): - #{ + """Get details about a one time password.""" + # { # "parameters": { # "srpSharedSecret": "bf79ad3cf0c1...63462a9fb560", # "message": "getOneTimePasswordsDetails", # "parameters": {} # } - #} + # } if 'srpSharedSecret' not in parameters: raise InvalidUsage( 'Mal-formed message format.', @@ -425,20 +464,24 @@ class message(HandlerMixin): otps = OneTimePassword().query.filter_by(user=current_user).all() for otp in otps: - #{"e8541...af0c6b951":{"status":"ACTIVE"}} + # {"e8541...af0c6b951":{"status":"ACTIVE"}} result[otp.reference] = {'status': otp.status} return jsonify({'result': result}) @login_required def getLoginHistory(self, parameters, request): - #{ + """Get login history. + + Not currently fully implemented. + """ + # { # "parameters": { # "srpSharedSecret": "bf79ad3cf0c1...63462a9fb560", # "message": "getOneTimePasswordsDetails", # "parameters": {} # } - #} + # } if 'srpSharedSecret' not in parameters: raise InvalidUsage( 'Mal-formed message format.', @@ -462,6 +505,7 @@ class message(HandlerMixin): @login_required def addNewOneTimePassword(self, parameters, request): + """Add a new one time password.""" # "parameters": { # "message": "addNewOneTimePassword", # "srpSharedSecret": "1e8e037a8...85680f931d45dfc20472cf9d1", @@ -481,7 +525,7 @@ class message(HandlerMixin): # } # } # } - #} + # } if 'srpSharedSecret' not in parameters: raise InvalidUsage( 'Mal-formed message format.', @@ -524,6 +568,7 @@ class message(HandlerMixin): return jsonify({'result': result}) def echo(self, parameters, request): + """Check the status of the session.""" result = {} if 'srpSharedSecret' not in parameters: raise InvalidUsage( @@ -549,6 +594,7 @@ class message(HandlerMixin): @login_required def deleteUser(self, parameters, request): + """Delete a user and all of its records.""" result = {} if 'srpSharedSecret' not in parameters: raise InvalidUsage( @@ -573,7 +619,8 @@ class message(HandlerMixin): @login_required def upgradeUserCredentials(self, parameters, request): - #{"parameters":{"message":"upgradeUserCredentials","srpSharedSecret":"36...d6","parameters":{"credentials":{"C":"59d02038fdb47cee5b7837a697bc8ff41cc66d8844c8fce844cdf45b0b08b1e4","s":"fe40513b99fbaca9bfe51b8d6e9b3eb42b1e01ce8b0ae32461bec0294c1030ed","v":"300b92f4a3e34034d78cd5081f8db36dbf2a4c5f7a41db6954518815a3554278","version":"0.2"},"user":{"header":"{\"records\":{\"index\":{},\"data\":\"VIIDc5vFNoIflyXF8syb8fRS\"},\"directLogins\":{\"index\":{},\"data\":\"9elg3tu2UqsJ0zbUAdQkLE69\"},\"preferences\":{\"data\":\"Sbwar35Ynd/XobuAm4K66lqj\"},\"oneTimePasswords\":{\"data\":\"tAcTsWVTwALSfxXvCChHi4FD\"},\"version\":\"0.1\"}","statistics":"","version":"0.4","lock":null}}}} + """Upgrade a user's credentials to a new password.""" + # {"parameters":{"message":"upgradeUserCredentials","srpSharedSecret":"36...d6","parameters":{"credentials":{"C":"59d02038fdb47cee5b7837a697bc8ff41cc66d8844c8fce844cdf45b0b08b1e4","s":"fe40513b99fbaca9bfe51b8d6e9b3eb42b1e01ce8b0ae32461bec0294c1030ed","v":"300b92f4a3e34034d78cd5081f8db36dbf2a4c5f7a41db6954518815a3554278","version":"0.2"},"user":{"header":"{\"records\":{\"index\":{},\"data\":\"VIIDc5vFNoIflyXF8syb8fRS\"},\"directLogins\":{\"index\":{},\"data\":\"9elg3tu2UqsJ0zbUAdQkLE69\"},\"preferences\":{\"data\":\"Sbwar35Ynd/XobuAm4K66lqj\"},\"oneTimePasswords\":{\"data\":\"tAcTsWVTwALSfxXvCChHi4FD\"},\"version\":\"0.1\"}","statistics":"","version":"0.4","lock":null}}}} # NOQA result = {} if 'srpSharedSecret' not in parameters: raise InvalidUsage( @@ -614,7 +661,11 @@ class message(HandlerMixin): class logout(HandlerMixin): + + """Logout handler.""" + def handle_request(self, request): + """Handle a logout request.""" result = {} logout_user() session.clear() diff --git a/backend/flask/src/clipperz/models.py b/backend/flask/src/clipperz/models.py index 8de66a1..76d8bef 100644 --- a/backend/flask/src/clipperz/models.py +++ b/backend/flask/src/clipperz/models.py @@ -1,3 +1,4 @@ +"""Clipperz models.""" import datetime from flask.ext.login import UserMixin @@ -6,6 +7,9 @@ from clipperz import app, db class User(db.Model, UserMixin): + + """Clipperz User model.""" + id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(), unique=True, index=True) srp_s = db.Column(db.String(128)) @@ -29,12 +33,14 @@ class User(db.Model, UserMixin): update_date = db.Column(db.DateTime(), nullable=True) def updateCredentials(self, credentials): + """Update user credentials.""" self.username = credentials['C'] self.srp_s = credentials['s'] self.srp_v = credentials['v'] self.auth_version = credentials['version'] def update(self, data): + """Update user object.""" self.header = data['header'] self.statistics = data['statistics'] self.version = data['version'] @@ -44,12 +50,21 @@ class User(db.Model, UserMixin): self.offline_saved = False def __repr__(self): + """User representation.""" return '' % (self.username) -#------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ class RecordVersion(db.Model): + + """ + Model a RecordVersion. + + RecordVersion store attributes associated with a specific version of a + record. + """ + id = db.Column(db.Integer(), primary_key=True) reference = db.Column(db.String(), unique=True, index=True) header = db.Column(db.Text()) @@ -77,6 +92,7 @@ class RecordVersion(db.Model): self.creation_date = datetime.datetime.utcnow() def update(self, someData): + """Update a record version.""" app.logger.debug(someData) recordVersionData = someData['currentRecordVersion'] self.reference = recordVersionData['reference'] @@ -87,10 +103,16 @@ class RecordVersion(db.Model): self.update_date = datetime.datetime.utcnow() self.record.update(someData['record'], self) -#------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ class Record(db.Model): + + """Model a record. + + A Record has multiple record versions. + """ + id = db.Column(db.Integer(), primary_key=True) user_id = db.Column(db.ForeignKey('user.id')) reference = db.Column(db.String(), unique=True, index=True) @@ -111,6 +133,7 @@ class Record(db.Model): self.creation_date = datetime.datetime.utcnow() def update(self, data, record_version): + """Update a record.""" self.reference = data['reference'] self.data = data['data'] self.api_version = data['version'] @@ -121,10 +144,16 @@ class Record(db.Model): else: self.version = 1 -#------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ class OneTimePassword(db.Model): + + """Model a OneTimePassword. + + OneTimePasswords are used to log in to clipperz only once. + """ + id = db.Column(db.Integer(), primary_key=True) user_id = db.Column(db.ForeignKey('user.id')) status = db.Column(db.String()) @@ -142,6 +171,7 @@ class OneTimePassword(db.Model): self.creation_date = datetime.datetime.utcnow() def update(self, someParameters, aStatus): + """Update a one time password.""" self.reference = someParameters['reference'] self.key_value = someParameters['key'] self.key_checksum = someParameters['keyChecksum'] @@ -150,14 +180,18 @@ class OneTimePassword(db.Model): self.status = aStatus def reset(self, aStatus): + """Reset a one time password.""" self.data = "" self.status = aStatus return self -#------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ class Session(db.Model): + + """Model a session.""" + id = db.Column(db.Integer(), primary_key=True) sessionId = db.Column(db.String()) access_date = db.Column(db.DateTime()) diff --git a/backend/flask/src/clipperz/views.py b/backend/flask/src/clipperz/views.py index e70f0ac..0bece2a 100644 --- a/backend/flask/src/clipperz/views.py +++ b/backend/flask/src/clipperz/views.py @@ -1,23 +1,30 @@ -from flask import session, request, g, jsonify +"""Clipperz views.""" +from flask import session, request, g from clipperz import app, db, lm from .models import User -from .api import * +from .api import * # NOQA from .exceptions import InvalidUsage from flask.ext.login import login_required @lm.user_loader def load_user(id): + """Load a user. + + Converts a user id in to a User object. + """ return User.query.get(int(id)) @app.before_request def before_request(): + """Store the current user.""" g.user = current_user @app.teardown_appcontext def shutdown_session(exception=None): + """Remove the session from the database.""" db.session.remove() @@ -26,6 +33,7 @@ def shutdown_session(exception=None): @app.route('/delta/dump/') @login_required def dump(frontend_version): + """Return JSON for a user's data.""" user = User().query.filter_by(username=session['C']).one() if (user != g.user): @@ -104,21 +112,25 @@ def dump(frontend_version): @app.route('/beta/') def beta(path): + """Fallback for serving beta version.""" return send_from_directory('beta', path) @app.route('/gamma/') def gamma(path): + """Fallback for serving gamma version.""" return send_from_directory('gamma', path) @app.route('/delta/') def delta(path): + """Fallback for serving delta version.""" return send_from_directory('delta', path) @app.route('/pm', methods=['GET', 'OPTIONS', 'POST']) def pm(): + """Main request handler.""" method = request.form['method'] if method not in globals(): raise InvalidUsage('This method is not yet implemented', From 88e51068ca45b2c9c49cbb39a74d8d12a2937380 Mon Sep 17 00:00:00 2001 From: jokajak Date: Sun, 2 Aug 2015 17:49:02 -0400 Subject: [PATCH 4/4] Update flask models to fix table creation Mysql requires a length in the table definition for String columns --- backend/flask/src/clipperz/models.py | 30 ++++++++++++++-------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/backend/flask/src/clipperz/models.py b/backend/flask/src/clipperz/models.py index 76d8bef..61ca25d 100644 --- a/backend/flask/src/clipperz/models.py +++ b/backend/flask/src/clipperz/models.py @@ -11,14 +11,14 @@ class User(db.Model, UserMixin): """Clipperz User model.""" id = db.Column(db.Integer, primary_key=True) - username = db.Column(db.String(), unique=True, index=True) + username = db.Column(db.String(255), unique=True, index=True) srp_s = db.Column(db.String(128)) srp_v = db.Column(db.String(128)) header = db.Column(db.Text()) statistics = db.Column(db.Text()) - auth_version = db.Column(db.String()) - version = db.Column(db.String()) - lock = db.Column(db.String()) + auth_version = db.Column(db.String(255)) + version = db.Column(db.String(255)) + lock = db.Column(db.String(255)) records = db.relationship( 'Record', backref='user', @@ -66,12 +66,12 @@ class RecordVersion(db.Model): """ id = db.Column(db.Integer(), primary_key=True) - reference = db.Column(db.String(), unique=True, index=True) + reference = db.Column(db.String(255), unique=True, index=True) header = db.Column(db.Text()) data = db.Column(db.Text()) - api_version = db.Column(db.String()) + api_version = db.Column(db.String(255)) version = db.Column(db.Integer()) - previous_version_key = db.Column(db.String()) + previous_version_key = db.Column(db.String(255)) previous_version_id = db.Column(db.Integer(), db.ForeignKey('record_version.id')) creation_date = db.Column(db.DateTime()) @@ -115,9 +115,9 @@ class Record(db.Model): id = db.Column(db.Integer(), primary_key=True) user_id = db.Column(db.ForeignKey('user.id')) - reference = db.Column(db.String(), unique=True, index=True) + reference = db.Column(db.String(255), unique=True, index=True) data = db.Column(db.Text()) - api_version = db.Column(db.String()) + api_version = db.Column(db.String(255)) version = db.Column(db.Integer(), default=0) creation_date = db.Column(db.DateTime()) update_date = db.Column(db.DateTime()) @@ -156,12 +156,12 @@ class OneTimePassword(db.Model): id = db.Column(db.Integer(), primary_key=True) user_id = db.Column(db.ForeignKey('user.id')) - status = db.Column(db.String()) - reference = db.Column(db.String(), unique=True) - key_value = db.Column(db.String()) - key_checksum = db.Column(db.String()) + status = db.Column(db.String(255)) + reference = db.Column(db.String(255), unique=True) + key_value = db.Column(db.String(255)) + key_checksum = db.Column(db.String(255)) data = db.Column(db.Text()) - version = db.Column(db.String()) + version = db.Column(db.String(255)) creation_date = db.Column(db.DateTime()) request_date = db.Column(db.DateTime()) usage_date = db.Column(db.DateTime()) @@ -193,7 +193,7 @@ class Session(db.Model): """Model a session.""" id = db.Column(db.Integer(), primary_key=True) - sessionId = db.Column(db.String()) + sessionId = db.Column(db.String(255)) access_date = db.Column(db.DateTime()) def __init__(self):