688 lines
25 KiB
Python
688 lines
25 KiB
Python
"""Clipperz API handler."""
|
|
import json
|
|
import random
|
|
import hashlib
|
|
|
|
from flask import jsonify, session, g
|
|
from datetime import datetime
|
|
from flask.ext.login import logout_user, current_user, login_user, \
|
|
login_required
|
|
from sqlalchemy.orm.exc import NoResultFound
|
|
from clipperz import app, db
|
|
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']
|
|
if 'message' in parameters:
|
|
message = parameters['message']
|
|
app.logger.debug('message: %s', message)
|
|
app.logger.debug('parameters: %s', parameters)
|
|
try:
|
|
handler = getattr(self, message)
|
|
except AttributeError:
|
|
raise InvalidUsage(
|
|
'This message handler is not yet implemented for this method',
|
|
status_code=501)
|
|
return handler(parameters, request)
|
|
|
|
|
|
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()
|
|
user.updateCredentials(credentials)
|
|
user.update(data)
|
|
db.session.add(user)
|
|
db.session.commit()
|
|
return jsonify(lock=user.lock,
|
|
result='done')
|
|
|
|
|
|
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']
|
|
app.logger.debug('username: %s', session['C'])
|
|
|
|
user = User().query.filter_by(username=session['C']).one()
|
|
|
|
if user is not None and session['A'] != 0:
|
|
session['s'] = user.srp_s
|
|
session['v'] = user.srp_v
|
|
if 'otpid' in session:
|
|
try:
|
|
otpId = session['otpId']
|
|
|
|
one_time_password = OneTimePassword().filter_by(
|
|
id=otpId
|
|
).one()
|
|
|
|
if one_time_password.user.username != user.username:
|
|
one_time_password.reset('DISABLED')
|
|
raise Exception(("user mismatch between the current "
|
|
"session and 'one time password' "
|
|
"user"))
|
|
elif one_time_password.status != 'requested':
|
|
one_time_password.reset('DISABLED')
|
|
raise Exception(("Trying to use an 'one time password'"
|
|
" in the wrong state"))
|
|
|
|
one_time_password.reset("USED")
|
|
|
|
result['oneTimePassword'] = one_time_password.reference
|
|
db.session.add(one_time_password)
|
|
db.session.commit()
|
|
|
|
except Exception, detail:
|
|
app.logger.error("connect.optid: " + str(detail))
|
|
|
|
else:
|
|
# invalid user
|
|
invalid = ('112233445566778899aabbccddeeff00112233445566778899'
|
|
'aabbccddeeff00')
|
|
session['s'] = invalid
|
|
session['v'] = invalid
|
|
|
|
session['b'] = randomSeed()
|
|
k = '0x64398bff522814e306a97cb9bfc4364b7eed16a8c17c5208a40a2bad2933c8e'
|
|
k = long(k, 16)
|
|
app.logger.debug('k: %s (%s)', k, hex(k))
|
|
session['B'] = hex(k * long("0x%s" % session['v'], 16) +
|
|
pow(self.srp_g,
|
|
long("0x%s" % session['b'], 16),
|
|
self.srp_n)
|
|
)[2:-1]
|
|
result['s'] = session['s']
|
|
result['B'] = session['B']
|
|
app.logger.debug('Session: %s', session)
|
|
return jsonify({'result': result})
|
|
|
|
def credentialCheck(self, parameters, request):
|
|
"""Check credentials.
|
|
|
|
Handles the SRP process.
|
|
"""
|
|
country = 'US'
|
|
# hard-coded for development/personal use.
|
|
result = {
|
|
'accountInfo': {
|
|
'features': [
|
|
'UPDATE_CREDENTIALS',
|
|
'EDIT_CARD',
|
|
'CARD_DETAILS',
|
|
'ADD_CARD',
|
|
'DELETE_CARD',
|
|
'OFFLINE_COPY',
|
|
'LIST_CARDS'
|
|
],
|
|
'paramentVerificationPending': False,
|
|
'currentSubscriptionType': 'EARLY_ADOPTER',
|
|
'isExpiring': False,
|
|
'latestActiveLevel': 'EARLY_ADOPTER',
|
|
'payments': [],
|
|
'featureSet': 'FULL',
|
|
'latestActiveThreshold': -1.0,
|
|
'referenceDate': datetime.now(),
|
|
'isExpired': False,
|
|
'expirationDate': datetime(4001, 1, 1)
|
|
},
|
|
}
|
|
|
|
A = long("0x%s" % session['A'], 16)
|
|
B = long("0x%s" % session['B'], 16)
|
|
b = long("0x%s" % session['b'], 16)
|
|
v = long("0x%s" % session['v'], 16)
|
|
u = long("0x%s" % clipperzHash(str(A) + str(B)), 16)
|
|
s = long("0x%s" % session['s'], 16)
|
|
C = session['C']
|
|
n = self.srp_n
|
|
|
|
S = pow((A * pow(v, u, n)), b, n)
|
|
K = clipperzHash(str(S))
|
|
M1 = '{0}{1}{2}{3}{4}{5}{6}'.format(
|
|
'5976268709782868014401975621485889074340014836557888656093758064',
|
|
'39877501869636875571920406529',
|
|
clipperzHash(str(C)),
|
|
str(s),
|
|
str(A),
|
|
str(B),
|
|
str(K)
|
|
)
|
|
M1 = clipperzHash(M1)
|
|
if M1 == parameters['parameters']['M1']:
|
|
session['K'] = K
|
|
M2 = clipperzHash(str(A) + M1 + K)
|
|
|
|
result['M2'] = M2
|
|
result['connectionId'] = ''
|
|
result['loginInfo'] = {}
|
|
result['loginInfo']['current'] = {
|
|
'date': datetime.now(),
|
|
'ip': request.remote_addr,
|
|
'browser': request.user_agent.browser,
|
|
'operatingSystem': request.user_agent.platform,
|
|
'disconnectionType': 'STILL_CONNECTED',
|
|
'country': country
|
|
},
|
|
result['loginInfo']['latest'] = {}
|
|
result['offlineCopyNeeded'] = False
|
|
user = User().query.filter_by(username=session['C']).one()
|
|
result['lock'] = user.lock
|
|
login_user(user)
|
|
session['User'] = user
|
|
else:
|
|
result['error'] = '?'
|
|
|
|
result['s'] = session['s']
|
|
result['B'] = session['B']
|
|
return jsonify({'result': result})
|
|
|
|
def oneTimePassword(self, parameters, request):
|
|
"""Handle one time password logins."""
|
|
# "parameters": {
|
|
# "message": "oneTimePassword",
|
|
# "version": "0.2",
|
|
# "parameters": {
|
|
# "oneTimePasswordKey": "03bd882...396082c",
|
|
# "oneTimePasswordKeyChecksum": "f73f629...041031d"
|
|
# }
|
|
# }
|
|
result = {}
|
|
|
|
try:
|
|
key = parameters['parameters']['oneTimePasswordKey']
|
|
checksum = parameters['parameters']['oneTimePasswordKeyChecksum']
|
|
otp = OneTimePassword().query.filter_by(key_value=key).one()
|
|
if otp.status == 'ACTIVE':
|
|
if otp.key_checksum == checksum:
|
|
session['userId'] = otp.user.id
|
|
session['otpId'] = otp.id
|
|
result['data'] = otp.data
|
|
result['version'] = otp.version
|
|
otp.data = ''
|
|
otp.status = 'REQUESTED'
|
|
else:
|
|
otp.data = ''
|
|
otp.status = 'DISABLED'
|
|
db.session.add(otp)
|
|
db.session.commit()
|
|
except NoResultFound, details:
|
|
app.logger.debug('OTP No Results Found: ', details)
|
|
|
|
return jsonify({'result': result})
|
|
|
|
|
|
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(
|
|
'Mal-formed message format.',
|
|
status_code=400)
|
|
srpSharedSecret = parameters['srpSharedSecret']
|
|
if srpSharedSecret != session['K']:
|
|
raise InvalidUsage(
|
|
'Your session is invalid, please re-authenticate',
|
|
status_code=401)
|
|
# 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\"}", # NOQA
|
|
# "lock": "3D4B4501-D7A9-6E4F-A487-9428C0B6E79D",
|
|
# "version": "0.4",
|
|
# "recordsStats": {
|
|
# "383036...eeefbe48":{
|
|
# "updateDate": "Sun, 12 April 2015 17:11:01 UTC",
|
|
# "accessDate": "Sun, 12 April 2015 17:11:01 UTC"
|
|
# }
|
|
# },
|
|
# "offlineCopyNeeded":true,
|
|
# "statistics":"ByYItDeZMdZ+e/pafp14bGrR"
|
|
# }
|
|
# }
|
|
# 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\"}", # NOQA
|
|
# "recordStats": {
|
|
# "843a95d8...5f734b": {
|
|
# "updateDate": "Sun, 12 Apr 2015 13:08:44 GMT"
|
|
# }
|
|
# },
|
|
# "statistics": "",
|
|
# "version": "0.4"}}
|
|
result = {}
|
|
user = User().query.filter_by(username=session['C']).one()
|
|
|
|
records_stats = {}
|
|
for record in user.records:
|
|
records_stats[record.reference] = {
|
|
'updateDate': record.update_date,
|
|
'accessDate': record.access_date
|
|
}
|
|
|
|
result['recordsStats'] = records_stats
|
|
result['header'] = user.header
|
|
result['statistics'] = user.statistics
|
|
result['version'] = user.version
|
|
result['offlineCopyNeeded'] = not user.offline_saved
|
|
result['lock'] = user.lock
|
|
return jsonify({'result': result})
|
|
|
|
@login_required
|
|
def saveChanges(self, parameters, request):
|
|
"""Save changes to a user's settings."""
|
|
result = {}
|
|
parameters = parameters['parameters']
|
|
if ('user' not in parameters
|
|
or 'records' not in parameters):
|
|
app.logger.debug('saveChanges parameters: %s', parameters)
|
|
raise InvalidUsage(
|
|
'Mal-formed message format.',
|
|
status_code=400)
|
|
|
|
user_data = parameters['user']
|
|
record_data = parameters['records']
|
|
|
|
user = User().query.filter_by(username=session['C']).one()
|
|
app.logger.debug('user_data: %s', user_data)
|
|
user.update(user_data)
|
|
db.session.add(user)
|
|
|
|
if 'updated' in record_data:
|
|
for entry in record_data['updated']:
|
|
reference = entry['record']['reference']
|
|
try:
|
|
record = Record().query.filter_by(reference=reference).one()
|
|
except NoResultFound:
|
|
record = Record(user=user)
|
|
record_version = RecordVersion(record=record)
|
|
record_version.update(entry)
|
|
db.session.add(record)
|
|
db.session.add(record_version)
|
|
|
|
if 'deleted' in record_data:
|
|
for reference in record_data['deleted']:
|
|
try:
|
|
record = Record().query.filter_by(reference=reference).one()
|
|
db.session.delete(record)
|
|
except NoResultFound:
|
|
pass
|
|
|
|
db.session.commit()
|
|
result['lock'] = user.lock
|
|
result['result'] = 'done'
|
|
|
|
return jsonify({'result': result})
|
|
|
|
@login_required
|
|
def getRecordDetail(self, parameters, request):
|
|
"""Get details about a record."""
|
|
# {
|
|
# "parameters": {
|
|
# "srpSharedSecret": "bf79ad3cf0c1...63462a9fb560",
|
|
# "message": "getRecordDetail",
|
|
# "parameters": {
|
|
# "reference": "e3a5856...20e080fc97f13c14c"
|
|
# }
|
|
# }
|
|
# }
|
|
app.logger.debug(parameters)
|
|
if 'srpSharedSecret' not in parameters:
|
|
raise InvalidUsage(
|
|
'Mal-formed message format.',
|
|
status_code=400)
|
|
srpSharedSecret = parameters['srpSharedSecret']
|
|
if (srpSharedSecret != session['K'] and session['User'] != g.user):
|
|
raise InvalidUsage(
|
|
'Your session is incorrect, please re-authenticate',
|
|
status_code=401)
|
|
|
|
reference = parameters['parameters']['reference']
|
|
result = {}
|
|
|
|
record = Record().query.filter_by(reference=reference).one()
|
|
app.logger.debug(record.current_record_version)
|
|
record_versions = {}
|
|
oldest_encryption_version = None
|
|
versions = RecordVersion().query.filter_by(record=record).all()
|
|
for record_version in versions:
|
|
version_entry = {}
|
|
version_entry['reference'] = record_version.reference
|
|
version_entry['data'] = record_version.data
|
|
version_entry['header'] = record_version.header
|
|
version_entry['version'] = record_version.api_version
|
|
version_entry['creationDate'] = record_version.creation_date
|
|
version_entry['updateDate'] = record_version.update_date
|
|
version_entry['accessDate'] = record_version.access_date
|
|
try:
|
|
previous_version = RecordVersion().query.filter_by(
|
|
id=record_version.previous_version_id).one()
|
|
reference = previous_version.reference
|
|
key = record_version.previous_version_key
|
|
version_entry['previousVersion'] = reference
|
|
version_entry['previousVersionKey'] = key
|
|
except NoResultFound:
|
|
pass
|
|
if (not oldest_encryption_version
|
|
or oldest_encryption_version > record_version.api_version):
|
|
oldest_encryption_version = record_version.api_version
|
|
record_versions[record_version.reference] = version_entry
|
|
|
|
result['reference'] = record.reference
|
|
result['data'] = record.data
|
|
result['version'] = record.api_version
|
|
result['creationDate'] = str(record.creation_date)
|
|
result['updateDate'] = str(record.update_date)
|
|
result['accessDate'] = str(record.access_date)
|
|
result['oldestUsedEncryptedVersion'] = oldest_encryption_version
|
|
result['versions'] = record_versions
|
|
result['currentVersion'] = record.current_record_version.reference
|
|
record.current_record_version.access()
|
|
record.access()
|
|
db.session.add(record)
|
|
db.session.add(record_version)
|
|
db.session.commit()
|
|
return jsonify({'result': result})
|
|
|
|
@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.',
|
|
status_code=400)
|
|
srpSharedSecret = parameters['srpSharedSecret']
|
|
if (srpSharedSecret != session['K'] and session['User'] != g.user):
|
|
raise InvalidUsage(
|
|
'Your session is incorrect, please re-authenticate',
|
|
status_code=401)
|
|
|
|
result = {}
|
|
|
|
otps = OneTimePassword().query.filter_by(user=current_user).all()
|
|
for otp in otps:
|
|
# {"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.',
|
|
status_code=400)
|
|
srpSharedSecret = parameters['srpSharedSecret']
|
|
if (srpSharedSecret != session['K'] and session['User'] != g.user):
|
|
raise InvalidUsage(
|
|
'Your session is incorrect, please re-authenticate',
|
|
status_code=401)
|
|
|
|
result = {}
|
|
|
|
user = User().query.filter_by(username=session['C']).one()
|
|
|
|
if (user != g.user):
|
|
raise InvalidUsage(
|
|
'Your session is incorrect, please re-authenticate',
|
|
status_code=401)
|
|
|
|
return jsonify({'result': result})
|
|
|
|
@login_required
|
|
def addNewOneTimePassword(self, parameters, request):
|
|
"""Add a new one time password."""
|
|
# "parameters": {
|
|
# "message": "addNewOneTimePassword",
|
|
# "srpSharedSecret": "1e8e037a8...85680f931d45dfc20472cf9d1",
|
|
# "parameters": {
|
|
# "user": {
|
|
# "header": <header>
|
|
# "statistics": "WxHa6VSMmZunOjLCwAVQrkYI",
|
|
# "version": "0.4",
|
|
# "lock": "new lock"
|
|
# },
|
|
# "oneTimePassword": {
|
|
# "reference": "ffaec6f...7b123d39b8965e7e5",
|
|
# "key": "496dc431db...faec137698b16c",
|
|
# "keyChecksum": "f927c1...eb970552360a311dda",
|
|
# "data": "GcfCFsoSc5RT...MF8nstFXXHYSXF+Vyj4w=",
|
|
# "version": "0.4"
|
|
# }
|
|
# }
|
|
# }
|
|
# }
|
|
if 'srpSharedSecret' not in parameters:
|
|
raise InvalidUsage(
|
|
'Mal-formed message format.',
|
|
status_code=400)
|
|
srpSharedSecret = parameters['srpSharedSecret']
|
|
if (srpSharedSecret != session['K'] and session['User'] != g.user):
|
|
raise InvalidUsage(
|
|
'Your session is incorrect, please re-authenticate',
|
|
status_code=401)
|
|
|
|
result = {}
|
|
parameters = parameters['parameters']
|
|
|
|
user = User().query.filter_by(username=session['C']).one()
|
|
|
|
if (user != g.user):
|
|
raise InvalidUsage(
|
|
'Your session is incorrect, please re-authenticate',
|
|
status_code=401)
|
|
|
|
user_data = parameters['user']
|
|
|
|
app.logger.debug('user_data: %s', user_data)
|
|
user.update(user_data)
|
|
db.session.add(user)
|
|
|
|
one_time_password = parameters['oneTimePassword']
|
|
otp = OneTimePassword(
|
|
reference=one_time_password['reference'],
|
|
key_value=one_time_password['key'],
|
|
key_checksum=one_time_password['keyChecksum'],
|
|
data=one_time_password['data'],
|
|
version=one_time_password['version'],
|
|
user=user,
|
|
status='ACTIVE'
|
|
)
|
|
db.session.add(otp)
|
|
db.session.commit()
|
|
|
|
return jsonify({'result': result})
|
|
|
|
def echo(self, parameters, request):
|
|
"""Check the status of the session."""
|
|
result = {}
|
|
if 'srpSharedSecret' not in parameters:
|
|
raise InvalidUsage(
|
|
'Mal-formed message format.',
|
|
status_code=400)
|
|
srpSharedSecret = parameters['srpSharedSecret']
|
|
if (srpSharedSecret != session['K'] and session['User'] != g.user):
|
|
raise InvalidUsage(
|
|
'Your session is incorrect, please re-authenticate',
|
|
status_code=401)
|
|
|
|
user = User().query.filter_by(username=session['C']).one()
|
|
|
|
if (user != g.user):
|
|
raise InvalidUsage(
|
|
'Your session is incorrect, please re-authenticate',
|
|
status_code=401)
|
|
|
|
user.offline_saved = True
|
|
db.session.add(user)
|
|
db.session.commit()
|
|
return jsonify({'result': result})
|
|
|
|
@login_required
|
|
def deleteUser(self, parameters, request):
|
|
"""Delete a user and all of its records."""
|
|
result = {}
|
|
if 'srpSharedSecret' not in parameters:
|
|
raise InvalidUsage(
|
|
'Mal-formed message format.',
|
|
status_code=400)
|
|
srpSharedSecret = parameters['srpSharedSecret']
|
|
if (srpSharedSecret != session['K'] and session['User'] != g.user):
|
|
raise InvalidUsage(
|
|
'Your session is incorrect, please re-authenticate',
|
|
status_code=401)
|
|
|
|
user = User().query.filter_by(username=session['C']).one()
|
|
|
|
if (user != g.user):
|
|
raise InvalidUsage(
|
|
'Your session is incorrect, please re-authenticate',
|
|
status_code=401)
|
|
|
|
db.session.delete(user)
|
|
db.session.commit()
|
|
return jsonify({'result': result})
|
|
|
|
@login_required
|
|
def upgradeUserCredentials(self, parameters, request):
|
|
"""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(
|
|
'Mal-formed message format.',
|
|
status_code=400)
|
|
srpSharedSecret = parameters['srpSharedSecret']
|
|
if (srpSharedSecret != session['K'] and session['User'] != g.user):
|
|
raise InvalidUsage(
|
|
'Your session is incorrect, please re-authenticate',
|
|
status_code=401)
|
|
|
|
user = User().query.filter_by(username=session['C']).one()
|
|
|
|
if (user != g.user):
|
|
raise InvalidUsage(
|
|
'Your session is incorrect, please re-authenticate',
|
|
status_code=401)
|
|
|
|
parameters = parameters['parameters']
|
|
user.updateCredentials(parameters['credentials'])
|
|
user.update(parameters['user'])
|
|
|
|
if 'oneTimePasswords' in parameters:
|
|
for otpRef in parameters['oneTimePasswords']:
|
|
try:
|
|
otp = OneTimePassword().query.filter_by(
|
|
reference=otpRef).one()
|
|
otp.data = parameters['oneTimePasswords'][otpRef]
|
|
db.session.add(otp)
|
|
except NoResultFound:
|
|
pass
|
|
|
|
db.session.add(user)
|
|
db.session.commit()
|
|
result['lock'] = user.lock
|
|
result['result'] = 'done'
|
|
return jsonify({'result': result})
|
|
|
|
@login_required
|
|
def getCertificatesStatus(self, parameters, request):
|
|
"""
|
|
Provides support for BTC Certificate feature.
|
|
|
|
No idea how it works.
|
|
"""
|
|
return jsonify({'result': {}})
|
|
|
|
|
|
class logout(HandlerMixin):
|
|
|
|
"""Logout handler."""
|
|
|
|
def handle_request(self, request):
|
|
"""Handle a logout request."""
|
|
result = {}
|
|
logout_user()
|
|
session.clear()
|
|
result['method'] = 'logout'
|
|
return jsonify({'result': result})
|