def __call__(self, auth_id, record_key): if self.is_valid_record_key(record_key): msg = 'record_key `{}` is not configured to verify'.format( record_key) raise SkygearException(msg, skyerror.InvalidArgument) with conn() as c: user = get_user(c, auth_id) if not user: msg = 'user `{}` not found'.format(auth_id) raise SkygearException(msg, skyerror.ResourceNotFound) user_record = fetch_user_record(auth_id) if not user_record: msg = 'user `{}` not found'.format(auth_id) raise SkygearException(msg, skyerror.ResourceNotFound) value_to_verify = user_record.get(record_key) if not value_to_verify: msg = 'there is nothing to verify for record_key `{}` ' \ 'with auth_id `{}`'.format(record_key, auth_id) raise SkygearException(msg, skyerror.InvalidArgument) code_str = self.get_code(record_key) with conn() as c: add_verify_code(c, auth_id, record_key, value_to_verify, code_str) logger.info('Added new verify code `{}` for user `{}`.'.format( code_str, auth_id)) self.call_provider(record_key, user, user_record, code_str)
def reset_password(user_id, code, expire_at, new_password): """ Lambda function to handle reset password request. """ if not user_id: raise SkygearException('user_id must be set', skyerror.InvalidArgument) if not code: raise SkygearException('code must be set', skyerror.InvalidArgument) if not expire_at: raise SkygearException('expire_at must be set', skyerror.InvalidArgument) with conn() as c: user = user_util.get_user_and_validate_code( c, user_id, code, expire_at) if not user: raise SkygearException('user_id is not found or code invalid', skyerror.ResourceNotFound) if not user.email: raise SkygearException('email must be set', skyerror.ResourceNotFound) user_util.set_new_password(c, user.id, new_password) logger.info('Successfully reset password for user.') return {'status': 'OK'}
def __call__(self, auth_id, code_str): with conn() as c: code = get_verify_code(c, auth_id, code_str) if not code or code.consumed: msg = 'the code `{}` is not valid ' \ 'for user `{}`'.format(code_str, auth_id) raise SkygearException(msg, skyerror.InvalidArgument) user_record = fetch_user_record(auth_id) if not user_record: msg = 'user `{}` not found'.format(auth_id) raise SkygearException(msg, skyerror.ResourceNotFound) if user_record.get(code.record_key) != code.record_value: msg = 'the user data has since been modified, ' \ 'a new verification is required' raise SkygearException(msg, skyerror.InvalidArgument) expiry = self.settings.keys[code.record_key].expiry if expiry: expire_at = code.created_at + datetime.timedelta(seconds=expiry) if expire_at < datetime.datetime.now(): msg = 'the code has expired' raise SkygearException(msg, skyerror.InvalidArgument) with conn() as c: set_code_consumed(c, code.id) user_record[verified_flag_name(code.record_key)] = True save_user_record(user_record)
def forgot_password(email): """ Lambda function to handle forgot password request. """ if email is None: raise SkygearException('email must be set', skyerror.InvalidArgument) with conn() as c: user = user_util.get_user_from_email(c, email) if not user: if not settings.secure_match: return {'status': 'OK'} raise SkygearException('user_id must be set', skyerror.InvalidArgument) if not user.email: raise SkygearException('email must be set', skyerror.InvalidArgument) user_record = user_util.get_user_record(c, user.id) expire_at = round(datetime.utcnow().timestamp()) + \ settings.reset_url_lifetime code = user_util.generate_code(user, expire_at) url_prefix = settings.url_prefix if url_prefix.endswith('/'): url_prefix = url_prefix[:-1] link = '{0}/reset-password?code={1}&user_id={2}&expire_at={3}'\ .format(url_prefix, code, user.id, expire_at) template_params = { 'appname': settings.app_name, 'link': link, 'url_prefix': url_prefix, 'email': user.email, 'user_id': user.id, 'code': code, 'user': user, 'user_record': user_record, 'expire_at': expire_at, } try: mail_sender.send( (settings.sender_name, settings.sender), user.email, settings.subject, reply_to=(settings.reply_to_name, settings.reply_to), template_params=template_params) except Exception as ex: logger.exception('An error occurred sending reset password' ' email to user.') raise SkygearException(str(ex), skyerror.UnexpectedError) return {'status': 'OK'}
def test_forgot_password_email(email, text_template=None, html_template=None, subject=None, sender=None, reply_to=None, sender_name=None, reply_to_name=None): access_key_type = current_context().get('access_key_type') if not access_key_type or access_key_type != 'master': raise SkygearException('master key is required', skyerror.AccessKeyNotAccepted) url_prefix = settings.url_prefix if url_prefix.endswith('/'): url_prefix = url_prefix[:-1] dummy_user = namedtuple('User', ['id', 'email'])('dummy-id', '*****@*****.**') dummy_record_id = RecordID('user', 'dummy-id') dummy_record = Record(dummy_record_id, dummy_record_id.key, None) template_params = { 'appname': settings.app_name, 'code': 'dummy-reset-code', 'url_prefix': url_prefix, 'link': '{}/example-reset-password-link'.format(url_prefix), 'user_record': dummy_record, 'user': dummy_user, 'email': dummy_user.email, 'user_id': dummy_user.id } email_sender = (sender_name, sender) if sender \ else (settings.sender_name, settings.sender) email_subject = subject if subject else settings.subject email_reply_to = (reply_to_name, reply_to) if reply_to \ else (settings.reply_to_name, settings.reply_to) try: mail_sender.send(email_sender, email, email_subject, reply_to=email_reply_to, text_template_string=text_template, html_template_string=html_template, template_params=template_params) except Exception as ex: logger.exception('An error occurred sending test reset password' ' email to user.') raise SkygearException(str(ex), skyerror.UnexpectedError) return {'status': 'OK'}
def _send_multi(self, action, **payload): result = self.container.send_action(action, payload) if 'error' in result: raise SkygearException.from_dict(result['error']) elif 'result' in result and isinstance(result['result'], list): return result else: raise SkygearException('unexpected result', UnexpectedError)
def get_payload(self): data = self.request.get_data(as_text=True) if data == '': return {} try: payload = json.loads(data) return payload except ValueError: raise SkygearException('unable to decode json', BadRequest)
def verify_code_lambda(code): """ This lambda checks the user submitted code. """ if not current_user_id(): raise SkygearException("You must log in to perform this action.", code=NotAuthenticated) thelambda = VerifyCodeLambda(settings) return thelambda(current_user_id(), code)
def verify_request_lambda(record_key): """ This lambda allows client to request verification (i.e. send email or send SMS). """ if not current_user_id(): raise SkygearException("You must log in to perform this action.", code=NotAuthenticated) thelambda = VerifyRequestLambda(settings, providers) return thelambda(current_user_id(), record_key)
def _send_single(self, action, **payload): result = self.container.send_action(action, payload) if 'error' in result: raise SkygearException.from_dict(result['error']) elif 'result' in result and isinstance(result['result'], list) \ and len(result['result']) > 0: first_result = result['result'][0] if first_result.get('_type', None) == 'error': raise SkygearException.from_dict(first_result) return first_result else: raise SkygearException('unexpected result', UnexpectedError)
def __call__(self, record_key, record_value): if not record_value: msg = 'missing record_value' raise SkygearException(msg, skyerror.InvalidArgument) code_str = self.get_code(record_key) user = namedtuple('User', ['id', record_key])('dummy-id', record_key) user_record = Record('user/dummy-id', 'dummy-id', {}, data={record_key: record_value}) self.call_provider(record_key, user, user_record, code_str)
def handle_request(self, base_name, request): self.request = request ident = get_ident(base_name, request) for r in self._routes(): if r['ident'] != bool(ident): continue if r['method'] != request.method: continue if not has_func(self, r['func']): continue if ident: return getattr(self, r['func'])(ident) else: return getattr(self, r['func'])() else: raise SkygearException('invalid request method', BadRequest)
def test_verify_request_lambda(record_key, record_value, provider_settings={}, templates={}): """ Allow passing extra provider_settings from api for provider configuration testing. e.g. sms api key is provided by user Example: curl 'http://127.0.0.1:3000/' --data-binary '{ "action": "user:verify_request:test", "api_key": "master_key", "args": { "record_key": "email", "record_value": "*****@*****.**", "provider_settings": { "name": "smtp" }, "templates": { "text_template": "testing", "html_template": "testing html" } } }' curl 'http://127.0.0.1:3000/' --data-binary '{ "action": "user:verify_request:test", "api_key": "master_key", "args": { "record_key": "phone", "record_value": "+15005550009", "provider_settings": { "name": "twilio", "twilio_from": "+15005550009", "twilio_account_sid": "", "twilio_auth_token": "" }, "templates": { "template": "testing sms" } } }' """ access_key_type = current_context().get('access_key_type') if not access_key_type or access_key_type != 'master': raise SkygearException('master key is required', skyerror.AccessKeyNotAccepted) provider_name = provider_settings.get('name') if not provider_name: raise SkygearException('Missing provider', skyerror.InvalidArgument) merged_settings = { **vars(test_provider_settings[provider_name]), **provider_settings } string_templates = { k: StringTemplate('verify_{}_{}'.format(record_key, k), v) for k, v in templates.items() } _providers = {} _providers[record_key] = get_provider( argparse.Namespace(**merged_settings), record_key, **string_templates) thelambda = VerifyRequestTestLambda(settings, _providers) return thelambda(record_key, record_value)