def insert_user(user: User, password=None): con = get_db() cur = con.cursor() cur.execute( '''insert into app_user values (default, %(email)s, '', %(role)s, %(first_name)s, %(last_name)s, false, now(), now()) returning id''', { 'email': user.email, 'role': user.role, 'first_name': user.first_name, 'last_name': user.last_name }) row = cur.fetchone() if (not row): return None user_id = row[0] if (password): cur.execute( '''update app_user set pwd_hash = crypt(%(pas)s, gen_salt('bf')) where id = %(id)s ''', { 'pas': password, 'id': user_id }) cur.close() con.commit() return user_id
def login(email: str, password: str): '''User login''' # email = flask.request.args.get('email') # password = str(flask.request.args.get('password')) # print(email, password) con = get_db() cur = con.cursor() cur.execute( '''select id, (pwd_hash = crypt(%(password)s, pwd_hash)) as pwd_match from app_user where email = %(email)s''', { 'password': password, 'email': email }) row = cur.fetchone() if (row and row[1]): token = encode_session_token(row[0]) resp = flask.jsonify({'access_token': token.decode('utf-8')}) else: status = 401 data = {'mesage': 'Invalid password or user name.', 'status': status} resp = flask.jsonify(data) resp.status = str(status) return resp
def update_user(user: User): con = get_db() cur = con.cursor() cur.execute( '''update app_user set role= %(role)s, set first_name= %(first_name)s, set first_name= %(first_name)s where id = %(id)sget_role_permitions_lookup ''', { 'id': user.id, 'role': user.role, 'first_name': user.first_name, 'last_name': user.last_name }) rowcount = cur.rowcount # TODO this imitates trigger: Introduce trigger and remove this from part if (not rowcount): cur.close() return 0 cur.execute('update app_user set updated_at = now() where id = %(id)s', {'id': user.id}) cur.close() con.commit() return rowcount
def get_users(user_id: int = None): '''List all users or one user depending on user_id parameter supplied and permitions''' perm = flask.g.user.permitions # print(bin(perm)) # print(flask.g.user.id) if (user_id is None): # all if (not (perm & permits.read)): return unautorized_exit() con = get_db() cur = con.cursor() cur.execute( '''select id, email, active, role_id as role, first_name, last_name from app_user''' ) columns = cur.description res = [] for value in cur: res.append({ columns[index][0]: column for index, column in enumerate(value) }) cur.close() return flask.jsonify({'users': res}) else: if ((perm & permits.read) or ((perm & permits.read_own) and flask.g.user.id == user_id)): user = get_user(user_id) return flask.jsonify({'user': vars(user)}) else: return unautorized_exit()
def activate_user(activation_token: str): ''' User activation user registration->activation state tranitons: registered-> created-> pending_activation(<30min)->activated registered-> created-> pending_activation(>30min)->created (activation email has to be sent again manually by manager or admin not nedded in spec) user creation->registration->activation state tranitons: if created by admin user has to be invited the way it is done for manager (this is not covered in specification and it is not implemented here) created-> invited (<30min)-> (registred->activated) invited(>30min)-> created invited(email refused invald address) -> created ''' # activation_token = flask.request.args.get('activation_token') # password = flask.request.args.get('password') try: payload = decode_email_token(activation_token, 'activation') except (jwt.DecodeError, jwt.InvalidTokenError) as err: print(err) return unautorized_exit() try: user_id = payload['user_id'] except: return unautorized_exit() user = get_user(user_id) if (user.active): return flask.jsonify({ 'affected': 0, 'msg': 'User has already been activated' }) #TODO check if subjected user could be a e.g. does it have acivable role user = get_user(user_id) if (user.role != 'user'): return bad_request_400( 'User can not be invited. Most probably user role has been changed' ) con = get_db() cur = con.cursor() # print (password, user_id) cur.execute( '''update app_user set active=true, updated_at=now() where id = %(id)s and not active ''', {'id': user_id}) rowcount = cur.rowcount cur.close() con.commit() return flask.jsonify({'affected': rowcount})
def get_user(user_id): con = get_db() cur = con.cursor() cur.execute( '''select id, email, active, role_id, first_name, last_name from app_user where id = %(id)s''', {'id': user_id}) row = cur.fetchone() if (not row): return None user = User() user.id, user.email, user.active, user.role, user.first_name, user.last_name = row cur.close() return user
def email_queue(email_id: int = None): '''List email queue to bypass email service functionality''' con = get_db() cur = con.cursor() cur.execute('''select * from email_queue order by id desc''') columns = cur.description res = [] for value in cur: res.append( {columns[index][0]: column for index, column in enumerate(value)}) cur.close() return flask.jsonify({'email_queue': res})
def delete_user(user_id: int): con = get_db() cur = con.cursor() try: cur.execute('delete from table user_details where id = %(id)s', id=user_id) cur.execute('delete from table user where id = %(id)s', id=user_id) except (psycopg2.IntegrityError, psycopg2.DatabaseError) as err: con.rollback() cur.close() raise err rowcount = cur.rowcount cur.close() return rowcount
def wrap(*args, **kwargs): user = getattr(flask.g, 'user', None) assert (user is not None) # print('user ', user) con = get_db() cur = con.cursor() cur.execute('select permitions from role where id = %(id)s', {'id': user.role}) row = cur.fetchone() assert row is not None user.permitions = int(row[0], 2) if (required_permitions & user.permitions): return func(*args, **kwargs) else: return unautorized_exit()
def wrap(*args, **kwargs): token = flask.request.headers.get('Authorization') try: payload = decode_session_token(token) except (jwt.DecodeError, jwt.InvalidTokenError) as err: print(err) return unautorized_exit() con = get_db() cur = con.cursor() cur.execute( 'select id, email, active, role_id from app_user where id = %(id)s', {'id': payload['user_id']}) row = cur.fetchone() if (row): user = User() user.id, user.email, user.active, user.role = row if (user.active): flask.g.user = user return func(*args, **kwargs) return unautorized_exit()
def create_user(email: str, role: str, first_name: str = None, last_name: str = None): '''Creation of user by user who has create permit e.g. adminitrator''' user = User() user.email = email #flask.request.args.get('email') user.role = role #flask.request.args.get('role') user.first_name = first_name #flask.request.args.get('first_name') user.last_name = last_name #flask.request.args.get('last_name') print('email', user.email) if (not validate_email(user.email)): return bad_request_400('Validation error: invalid email') if (not validate_role(user.role)): return bad_request_400( 'Validation error: invalid role' ) #this is close to asert this is client app responsability # pwd = generate_password() con = get_db() cur = con.cursor() try: cur.execute( '''insert into app_user values (default, %(email)s, '', %(role)s, %(first_name)s, %(last_name)s, false, now(), now()) returning id''', { 'email': user.email, 'role': user.role, 'first_name': user.first_name, 'last_name': user.last_name }) except psycopg2.IntegrityError as err: return bad_request_400( "User with email '{}' already registered".format(user.email)) con.commit() id = cur.fetchone()[0] user.id = id return flask.jsonify({'user': vars(user)})
def send_activation_email(user: User) -> int: atoken = encode_email_token(user.id, 'activation') url = flask.current_app.config['CLIENT_APP_URL'] + '/activate-account' message = build_activation_mail_body(url, atoken, user) sender_email = flask.current_app.config['ACTIVATION_SENDER'] con = get_db() cur = con.cursor() cur.execute( '''insert into email_queue(id, status, sender, recipient, message, data) values(default, 'pending', %(sender)s, %(receiver)s, %(message)s, null) returning id ''', { 'sender': sender_email, 'receiver': user.email, 'message': message }) id = cur.fetchone()[0] cur.close() con.commit() #TODO add mail job into redis queue #TODO implement consumer service sender return {'email_queue_id': id}
def invite_user(user_id: int = None): ''' Invite user with inviteable permition e.g. manager to register and activate account. User that has invite permit e.g. user with administrator role can invite manager by email. After clicking on link in the email manager creates password by submitting password manager is registered and activated in atomic operation. Invite proces sequence diagram client server email admin sends invitation invite_user sends mail user clicks on link in email client reg-act page openss client request user from token get_invited_user_data server returns user client allow usr to create pass clinet send regact request register_and_activate_manager server validates request server activates user server returns status redirect to login page #(in this case direct login might be more appropriate) ''' # user_id = flask.request.args.get('user_id') #validate user #although this should work only for manager I will allow it for all con = get_db() cur = con.cursor() cur.execute( '''select id, email, active, role_id, first_name, last_name from app_user where id = %(id)s''', {'id': user_id}) row = cur.fetchone() if (not row): return bad_request_400('Invalid user.') user = User() user.id, user.email, user.active, user.role, user.first_name, user.last_name = row if (user.active): return flask.jsonify({'msg': 'User already activated', 'affected': 0}) url = '' if (rolepermits[user.role] & permits.inivteable): url = flask.current_app.config[ 'SERVER_NAME'] + '/register-activate' #this is client app path else: return bad_request_400('Invitation trough web api forbiden.') #email states and tranitions #pending->dispatched dipatched->sent dispatched->refused atoken = encode_email_token(user_id, 'invitation') message = build_invitation_mail_body(url, atoken, user) #put email into db sender_email = flask.current_app.config['INVITATION_SENDER'] cur.execute( '''insert into email_queue(id, status, sender, recipient, message, data) values(default, 'pending', %(sender)s, %(receiver)s, %(message)s, null) returning id ''', { 'sender': sender_email, 'receiver': user.email, 'message': message }) id = cur.fetchone()[0] cur.close() con.commit() #TODO add mail job into redis queue #TODO implement consumer service sender return flask.jsonify({'email_queue_id': id})
def register_and_activate_manager(invitation_token: str, password: str): ''' Registration and activation of a user witn invitable permition e.g. user with manager role Registration - in this case adding password to already created user data and Activation - performed upon user confirmation od email are one atomic process performed after clicking on manager activation link on restricted register page where manager has to create password and cant change his email name registration is followed by automatic activation manager state tranitons: created -> invited (30min)->(registred->activated) invited(>30min) -> created invited(email refused invald address) -> created ''' # invitation_token = flask.request.args.get('invitation_token') # password = str(flask.request.args.get('password')) try: payload = decode_email_token(invitation_token, 'invitation') except (jwt.DecodeError, jwt.InvalidTokenError) as err: print(err) return unautorized_exit() try: user_id = payload['user_id'] except: return unautorized_exit() if (not validate_password(password)): return bad_request_400('Invalid password') # check if subjected user could be invited e.g. does it have invitable role user = get_user(user_id) if (user.role != 'manager'): return bad_request_400( 'User can not be invited. Most probably users permition has been changed' ) if (user.active): return flask.jsonify({ 'affected': 0, 'msg': 'User has already been activated' }) con = get_db() cur = con.cursor() # print (password, user_id) cur.execute( '''update app_user set pwd_hash = crypt(%(pas)s, gen_salt('bf')), active=true, updated_at=now() where id = %(id)s and not active ''', { 'pas': password, 'id': user_id }) rowcount = cur.rowcount cur.close() con.commit() return flask.jsonify({'affected': rowcount})