コード例 #1
0
    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)
コード例 #2
0
    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)
コード例 #3
0
    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'}
コード例 #4
0
ファイル: restful.py プロジェクト: rickmak/py-skygear
 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)
コード例 #5
0
    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'}
コード例 #6
0
    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'}
コード例 #7
0
ファイル: restful.py プロジェクト: ben181231/py-skygear
 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)
コード例 #8
0
ファイル: restful.py プロジェクト: rickmak/py-skygear
 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)
コード例 #9
0
ファイル: restful.py プロジェクト: ben181231/py-skygear
 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)
コード例 #10
0
ファイル: restful.py プロジェクト: rickmak/py-skygear
 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)
コード例 #11
0
 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)
コード例 #12
0
ファイル: schema.py プロジェクト: lyoung83/chat
def schema_add_key_verified_acl(flag_names):
    """
    Add field ACL to disallow owner from modifying verified flags.
    """
    container = SkygearContainer(api_key=skyoptions.masterkey)

    # Fetch the current field ACL. If no changes are required, we will
    # not try to update the field ACL.
    resp = container.send_action("schema:field_access:get", {},
                                 plugin_request=True)
    if "error" in resp:
        raise SkygearException.from_dict(resp["error"])

    # Create the new ACL settings. This is accomplished by first
    # copying the existing field ACLs, ignoring the entries we need
    # to enforce.
    new_acls = []
    for acl in resp['result']['access']:
        if acl['record_type'] == 'user' \
                and acl['record_field'] in flag_names \
                and acl['user_role'] == '_owner':
            continue
        new_acls.append(acl)

    for flag_name in flag_names:
        new_acls.append({
            'record_type': 'user',
            'record_field': flag_name,
            'user_role': '_owner',
            'writable': False,
            'readable': True,
            'comparable': True,
            'discoverable': True,
        })

    if not new_acls:
        return

    # Update the field ACL.
    resp = container.send_action("schema:field_access:update",
                                 {"access": new_acls},
                                 plugin_request=True)
    if "error" in resp:
        raise SkygearException.from_dict(resp["error"])
コード例 #13
0
 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)
コード例 #14
0
    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)
コード例 #15
0
ファイル: schema.py プロジェクト: lyoung83/chat
def schema_add_key_verified_flags(flag_names):
    """
    Add the fields into Skygear DB user record.

    This function adds the specified fields into the user record schema. It
    is expected that all fields have boolean data type.
    """
    if not flag_names:
        return

    container = SkygearContainer(api_key=skyoptions.masterkey)

    # Fetch schema first. If no changes are required, we will not try
    # to update the schema.
    resp = container.send_action("schema:fetch", {}, plugin_request=True)
    if "error" in resp:
        raise SkygearException.from_dict(resp["error"])

    for field in resp['result']['record_types']['user']['fields']:
        if field['name'] in flag_names:
            flag_names.remove(field['name'])

    # `flag_names` now contain the list of fields to create. Proceed
    # to create the field list and save the new fields to user record
    # schema.
    fields = [{
        'name': flag_name,
        'type': 'boolean'
    } for flag_name in flag_names]

    resp = container.send_action(
        "schema:create", {"record_types": {
            'user': {
                'fields': fields
            }
        }},
        plugin_request=True)
    if "error" in resp:
        raise SkygearException.from_dict(resp["error"])
コード例 #16
0
def save_user_record(user_record):
    """
    Save the user record to Skygear Record API.
    """
    container = SkygearContainer(api_key=skyoptions.masterkey)

    resp = container.send_action("record:save",
                                 {"records": [serialize_record(user_record)]},
                                 plugin_request=True)
    try:
        if "error" in resp:
            raise SkygearException.from_dict(resp["error"])
    except (ValueError, TypeError, KeyError):
        raise SkygearContainer("container.send_action is buggy")
コード例 #17
0
def set_new_password(user_id, new_password):
    """
    Set the password of a user to a new password
    with auth:reset_password
    """
    container = SkygearContainer(api_key=skyoptions.masterkey)
    resp = container.send_action("auth:reset_password", {
        "auth_id": user_id,
        "password": new_password,
    },
                                 plugin_request=True)
    try:
        if "error" in resp:
            raise SkygearException.from_dict(resp["error"])
    except (ValueError, TypeError, KeyError):
        raise SkygearContainer("container.send_action is buggy")
コード例 #18
0
def fetch_user_record(auth_id):
    """
    Fetch the user record from Skygear Record API. The returned value
    is a user record in Record class.
    """
    container = SkygearContainer(api_key=skyoptions.masterkey)

    resp = container.send_action("record:fetch",
                                 {"ids": ['user/{}'.format(auth_id)]},
                                 plugin_request=True)
    try:
        if "error" in resp:
            raise SkygearException.from_dict(resp["error"])
    except (ValueError, TypeError, KeyError):
        raise SkygearContainer("container.send_action is buggy")
    return deserialize_record(resp['result'][0])
コード例 #19
0
ファイル: restful.py プロジェクト: rickmak/py-skygear
    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)
コード例 #20
0
    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)