def post(self): if any(f for f in ('email', 'password', 'name') if f not in self.data): raise APIError('Missing parameter(s)') if not utils.is_valid_email(self.data['email']): raise APIError('Invalid email address') if len(self.data['password']) < 8: raise APIError('Invalid password') if len(self.data['name']) < 3: raise APIError('Invalid name') try: models.get_active_account(email=self.data['email']) raise APIError('Email already registered') except models.Account.DoesNotExist: account = models.Account(name=self.data['name'], email=self.data['email'], password=generate_password_hash(self.data['password']), calories_goal=settings['default_calories_goal']) account.save() # make this account a user models.AccountRole(account=account, role=models.Role.get(code='user')).save() update_token = urlserializer.dumps({'id': account.id, 'active': True}, salt='account-update') url = url_for('public', path='apply-account-changes', account_id=account.id, token=update_token, _external=True) tasks.send_activation_email.delay(account.id, url, update_token) return created_response('account', account_id=account.id)
def login(): if any(f for f in ('grant_type', 'username', 'password') if f not in request.form) or \ any(k for k in request.form if k not in ('grant_type', 'username', 'password')): raise AppError('invalid_request') if request.form['grant_type'] != 'password': # only implementing Resource Owner Password Credentials Grant from RFC 6749 raise AppError('unsupported_grant_type') try: account = models.get_active_account(email=request.form['username']) except models.Account.DoesNotExist: raise AppError('invalid_grant') if not check_password_hash(account.password, request.form['password']): raise AppError('invalid_grant') if not account.active: update_token = urlserializer.dumps({'id': account.id, 'active': True}, salt='account-update') url = url_for('public', path='apply-account-changes', account_id=account.id, token=update_token, _external=True) tasks.send_activation_email.delay(account.id, url, update_token) raise AppError('invalid_grant', error_description='Your account is not active. An email has been sent ' 'to you with instructions to activate your account.') roles = map(lambda x: x.code, models.Role.select().join(models.AccountRole).where(models.AccountRole.account == account)) # not generating refresh tokens as it's making it all the way to the user's browser. If an attacker got their # hands on a refresh token, they could do anything without a time restriction return jsonify({ 'access_token': urlserializer.dumps({'id': account.id, 'roles': roles}, salt='access-token'), 'token_type': 'bearer', 'expires_in': settings['oauth-token-expiration-seconds'], 'scope': ' '.join(roles) })
def decorated_function(*args, **kwargs): account_id, token = verify_token_get_account(roles, accountid_free_roles, account_should_have_roles, kwargs) try: account = models.get_active_account(id=account_id) except models.Account.DoesNotExist: raise APIError('Not Found', 404) if 'account_id' in funct.__code__.co_varnames: kwargs['account_id'] = account_id if 'account' in funct.__code__.co_varnames: kwargs['account'] = account if 'roles' in funct.__code__.co_varnames: kwargs['roles'] = token['roles'] if len(args) and isinstance(args[0], Resource): args[0].logged_in_account_id = token['id'] args[0].account = account args[0].roles = token['roles'] return funct(*args, **kwargs)
def put(self, account_id): # if the account is being updated through the update_token, then we don't need to authenticate # the user (they may not even be able to log in) if 'update_token' in self.data: return self.update_account(account_id) # verify authorization account_id, token = verify_token_get_account(['user', 'user-manager', 'admin'], ['user-manager', 'admin'], kwargs={'account_id': account_id}) try: account = models.get_active_account(id=account_id) except models.Account.DoesNotExist: raise APIError('Account not found', 404) # perform updates if 'roles' in self.data: if not isinstance(self.data['roles'], list) or any(r for r in self.data['roles'] if not isinstance(r, basestring)): raise APIError('Invalid roles value') final_roles = models.Role.select().where(models.Role.code << self.data['roles']) if len(final_roles) != len(self.data['roles']): raise APIError('Invalid roles value') current_roles = models.Role.select().join(models.AccountRole).where(models.AccountRole.account == account) # changing roles is something only an admin can do if any(r for r in current_roles if r not in final_roles) or any(r for r in final_roles if r not in current_roles): verify_token_get_account(['admin'], ['admin'], kwargs={'account_id': account_id}) # handle deletions for role in [r for r in current_roles if r not in final_roles]: models.AccountRole.get(models.AccountRole.account == account, models.AccountRole.role == role).delete_instance() # handle additions for role in [r for r in final_roles if r not in current_roles]: models.AccountRole(account=account, role=role).save() if 'name' in self.data: if not isinstance(self.data['name'], basestring): raise APIError('Invalid name') account.name = self.data['name'] if 'email' in self.data and self.data['email'] != account.email: if not utils.is_valid_email(self.data['email']): raise APIError('Invalid email') try: models.get_active_account(email=self.data['email']) raise APIError('Email already used') except models.Account.DoesNotExist: pass if any(r for r in token['roles'] if r in ('user-manager', 'admin')): account.email = self.data['email'] else: # verify the current password and send a link to update the email if 'current_password' not in self.data: raise APIError('Missing current password') if not check_password_hash(account.password, self.data['current_password']): raise APIError('Invalid current password') update_token = urlserializer.dumps({'id': account.id, 'email': self.data['email']}, salt='account-update') url = url_for('public', path='apply-account-changes', account_id=account.id, token=update_token, _external=True) tasks.send_account_update_email.delay(account.id, url, update_token, email=self.data['email']) if 'password' in self.data: if len(self.data['password']) < 8: raise APIError('Invalid password') if not any(r for r in token['roles'] if r in ('user-manager', 'admin')): if 'current_password' not in self.data: raise APIError('Missing current password') if not check_password_hash(account.password, self.data['current_password']): raise APIError('Invalid current password') account.password = generate_password_hash(self.data['password']) if 'calories_goal' in self.data: try: account.calories_goal = int(self.data['calories_goal']) except: raise APIError('Invalid calories amount') account.save() return '', 204
token = urlserializer.loads(token, settings['oauth-token-expiration-seconds'], salt='access-token') except BadData, e: log.error(e) raise APIError('Unauthorized', 401) if roles and not any(r for r in roles if r in token['roles']): raise APIError('Unauthorized', 401) account_id = (kwargs['account_id'] if 'account_id' in kwargs else 0) or token['id'] if account_id != token['id'] and not any(r for r in accountid_free_roles if r in token['roles']): raise APIError('Unauthorized', 401) # this is the destination account, to allow certain actions only if the account the request # is affecting if that account has certain roles if account_should_have_roles: roles = map(lambda x: x.code, models.Role.select().join(models.AccountRole).where(models.AccountRole.account == models.get_active_account(id=account_id))) if not any(r for r in account_should_have_roles if r in roles): raise APIError("This action can't be performed on this account") return account_id, token def require_auth(roles=None, accountid_free_roles=[], account_should_have_roles=[]): def decorator(funct_or_class): def decorate_function(funct): def decorated_function(*args, **kwargs): account_id, token = verify_token_get_account(roles, accountid_free_roles, account_should_have_roles, kwargs) try: account = models.get_active_account(id=account_id) except models.Account.DoesNotExist: