Exemple #1
0
def async_genkey(crypto_util_: CryptoUtil, db_uri: str, filesystem_id: str,
                 codename: str) -> None:
    # We pass in the `crypto_util_` so we don't have to reference `current_app`
    # here. The app might not have a pushed context during testing which would
    # cause this asynchronous function to break.
    crypto_util_.genkeypair(filesystem_id, codename)

    # Register key generation as update to the source, so sources will
    # filter to the top of the list in the journalist interface if a
    # flagged source logs in and has a key generated for them. #789
    session = sessionmaker(bind=create_engine(db_uri))()
    try:
        source = session.query(Source).filter(
            Source.filesystem_id == filesystem_id).one()
        source.last_updated = datetime.utcnow()
        session.commit()
    except Exception as e:
        logging.getLogger(__name__).error(
            "async_genkey for source (filesystem_id={}): {}".format(
                filesystem_id, e))
    session.close()
Exemple #2
0
def create_app(config):
    # type: (SDConfig) -> Flask
    app = Flask(__name__,
                template_folder=config.SOURCE_TEMPLATES_DIR,
                static_folder=path.join(config.SECUREDROP_ROOT, 'static'))
    app.request_class = RequestThatSecuresFileUploads
    app.config.from_object(config.SourceInterfaceFlaskConfig)  # type: ignore
    app.sdconfig = config

    # The default CSRF token expiration is 1 hour. Since large uploads can
    # take longer than an hour over Tor, we increase the valid window to 24h.
    app.config['WTF_CSRF_TIME_LIMIT'] = 60 * 60 * 24
    CSRFProtect(app)

    if config.DATABASE_ENGINE == "sqlite":
        db_uri = (config.DATABASE_ENGINE + ":///" + config.DATABASE_FILE)
    else:
        db_uri = (config.DATABASE_ENGINE + '://' + config.DATABASE_USERNAME +
                  ':' + config.DATABASE_PASSWORD + '@' + config.DATABASE_HOST +
                  '/' + config.DATABASE_NAME)
    app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
    app.config['SQLALCHEMY_DATABASE_URI'] = db_uri
    db.init_app(app)

    app.storage = Storage(config.STORE_DIR, config.TEMP_DIR,
                          config.JOURNALIST_KEY)

    app.crypto_util = CryptoUtil(
        scrypt_params=config.SCRYPT_PARAMS,
        scrypt_id_pepper=config.SCRYPT_ID_PEPPER,
        scrypt_gpg_pepper=config.SCRYPT_GPG_PEPPER,
        securedrop_root=config.SECUREDROP_ROOT,
        word_list=config.WORD_LIST,
        nouns_file=config.NOUNS,
        adjectives_file=config.ADJECTIVES,
        gpg_key_dir=config.GPG_KEY_DIR,
    )

    @app.errorhandler(CSRFError)
    def handle_csrf_error(e):
        msg = render_template('session_timeout.html')
        session.clear()
        flash(Markup(msg), "important")
        return redirect(url_for('main.index'))

    assets = Environment(app)
    app.config['assets'] = assets

    i18n.setup_app(config, app)

    app.jinja_env.trim_blocks = True
    app.jinja_env.lstrip_blocks = True
    app.jinja_env.globals['version'] = version.__version__
    if getattr(config, 'CUSTOM_HEADER_IMAGE', None):
        app.jinja_env.globals['header_image'] = \
            config.CUSTOM_HEADER_IMAGE  # type: ignore
        app.jinja_env.globals['use_custom_header_image'] = True
    else:
        app.jinja_env.globals['header_image'] = 'logo.png'
        app.jinja_env.globals['use_custom_header_image'] = False

    app.jinja_env.filters['rel_datetime_format'] = \
        template_filters.rel_datetime_format
    app.jinja_env.filters['nl2br'] = evalcontextfilter(template_filters.nl2br)
    app.jinja_env.filters['filesizeformat'] = template_filters.filesizeformat

    for module in [main, info, api]:
        app.register_blueprint(module.make_blueprint(config))  # type: ignore

    @app.before_request
    @ignore_static
    def setup_i18n():
        """Store i18n-related values in Flask's special g object"""
        g.locale = i18n.get_locale(config)
        g.text_direction = i18n.get_text_direction(g.locale)
        g.html_lang = i18n.locale_to_rfc_5646(g.locale)
        g.locales = i18n.get_locale2name()

    @app.before_request
    @ignore_static
    def check_tor2web():
        # ignore_static here so we only flash a single message warning
        # about Tor2Web, corresponding to the initial page load.
        if 'X-tor2web' in request.headers:
            flash(
                Markup(
                    gettext(
                        '<strong>WARNING:&nbsp;</strong> '
                        'You appear to be using Tor2Web. '
                        'This <strong>&nbsp;does not&nbsp;</strong> '
                        'provide anonymity. '
                        '<a href="{url}">Why is this dangerous?</a>').format(
                            url=url_for('info.tor2web_warning'))),
                "banner-warning")

    @app.before_request
    @ignore_static
    def load_instance_config():
        app.instance_config = InstanceConfig.get_current()

    @app.before_request
    @ignore_static
    def setup_g():
        """Store commonly used values in Flask's special g object"""

        if 'expires' in session and datetime.utcnow() >= session['expires']:
            msg = render_template('session_timeout.html')

            # clear the session after we render the message so it's localized
            session.clear()

            # Redirect to index with flashed message
            flash(Markup(msg), "important")
            return redirect(url_for('main.index'))

        session['expires'] = datetime.utcnow() + \
            timedelta(minutes=getattr(config,
                                      'SESSION_EXPIRATION_MINUTES',
                                      120))

        # ignore_static here because `crypto_util.hash_codename` is scrypt
        # (very time consuming), and we don't need to waste time running if
        # we're just serving a static resource that won't need to access
        # these common values.
        if logged_in():
            g.codename = session['codename']
            g.filesystem_id = app.crypto_util.hash_codename(g.codename)
            try:
                g.source = Source.query \
                            .filter(Source.filesystem_id == g.filesystem_id) \
                            .one()
            except NoResultFound as e:
                app.logger.error("Found no Sources when one was expected: %s" %
                                 (e, ))
                del session['logged_in']
                del session['codename']
                return redirect(url_for('main.index'))
            g.loc = app.storage.path(g.filesystem_id)

    @app.errorhandler(404)
    def page_not_found(error):
        return render_template('notfound.html'), 404

    @app.errorhandler(500)
    def internal_error(error):
        return render_template('error.html'), 500

    return app
Exemple #3
0
def create_app(config: 'SDConfig') -> Flask:
    app = Flask(__name__,
                template_folder=config.JOURNALIST_TEMPLATES_DIR,
                static_folder=path.join(config.SECUREDROP_ROOT, 'static'))

    app.config.from_object(config.JOURNALIST_APP_FLASK_CONFIG_CLS)
    app.session_interface = JournalistInterfaceSessionInterface()

    csrf = CSRFProtect(app)
    Environment(app)

    app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
    app.config['SQLALCHEMY_DATABASE_URI'] = config.DATABASE_URI
    db.init_app(app)

    v2_enabled = path.exists(
        path.join(config.SECUREDROP_DATA_ROOT, 'source_v2_url'))
    v3_enabled = path.exists(
        path.join(config.SECUREDROP_DATA_ROOT, 'source_v3_url'))
    app.config.update(V2_ONION_ENABLED=v2_enabled, V3_ONION_ENABLED=v3_enabled)

    # TODO: Attaching a Storage dynamically like this disables all type checking (and
    # breaks code analysis tools) for code that uses current_app.storage; it should be refactored
    app.storage = Storage(config.STORE_DIR, config.TEMP_DIR,
                          config.JOURNALIST_KEY)

    # TODO: Attaching a CryptoUtil dynamically like this disables all type checking (and
    # breaks code analysis tools) for code that uses current_app.storage; it should be refactored
    app.crypto_util = CryptoUtil(
        scrypt_params=config.SCRYPT_PARAMS,
        scrypt_id_pepper=config.SCRYPT_ID_PEPPER,
        scrypt_gpg_pepper=config.SCRYPT_GPG_PEPPER,
        securedrop_root=config.SECUREDROP_ROOT,
        word_list=config.WORD_LIST,
        nouns_file=config.NOUNS,
        adjectives_file=config.ADJECTIVES,
        gpg_key_dir=config.GPG_KEY_DIR,
    )

    @app.errorhandler(CSRFError)
    def handle_csrf_error(e: CSRFError) -> 'Response':
        # render the message first to ensure it's localized.
        msg = gettext('You have been logged out due to inactivity.')
        session.clear()
        flash(msg, 'error')
        return redirect(url_for('main.login'))

    def _handle_http_exception(
        error: 'HTTPException'
    ) -> 'Tuple[Union[Response, str], Optional[int]]':
        # Workaround for no blueprint-level 404/5 error handlers, see:
        # https://github.com/pallets/flask/issues/503#issuecomment-71383286
        handler = list(app.error_handler_spec['api'][error.code].values())[0]
        if request.path.startswith('/api/') and handler:
            return handler(error)

        return render_template('error.html', error=error), error.code

    for code in default_exceptions:
        app.errorhandler(code)(_handle_http_exception)

    i18n.setup_app(config, app)

    app.jinja_env.trim_blocks = True
    app.jinja_env.lstrip_blocks = True
    app.jinja_env.globals['version'] = version.__version__
    app.jinja_env.filters['rel_datetime_format'] = \
        template_filters.rel_datetime_format
    app.jinja_env.filters['filesizeformat'] = template_filters.filesizeformat

    @app.before_first_request
    def expire_blacklisted_tokens() -> None:
        cleanup_expired_revoked_tokens()

    @app.before_request
    def load_instance_config() -> None:
        app.instance_config = InstanceConfig.get_current()

    @app.before_request
    def setup_g() -> 'Optional[Response]':
        """Store commonly used values in Flask's special g object"""
        if 'expires' in session and datetime.utcnow() >= session['expires']:
            session.clear()
            flash(gettext('You have been logged out due to inactivity.'),
                  'error')

        uid = session.get('uid', None)
        if uid:
            user = Journalist.query.get(uid)
            if user and 'nonce' in session and \
               session['nonce'] != user.session_nonce:
                session.clear()
                flash(
                    gettext('You have been logged out due to password change'),
                    'error')

        session['expires'] = datetime.utcnow() + \
            timedelta(minutes=getattr(config,
                                      'SESSION_EXPIRATION_MINUTES',
                                      120))

        # Work around https://github.com/lepture/flask-wtf/issues/275
        # -- after upgrading from Python 2 to Python 3, any existing
        # session's csrf_token value will be retrieved as bytes,
        # causing a TypeError. This simple fix, deleting the existing
        # token, was suggested in the issue comments. This code will
        # be safe to remove after Python 2 reaches EOL in 2020, and no
        # supported SecureDrop installations can still have this
        # problem.
        if sys.version_info.major > 2 and type(
                session.get('csrf_token')) is bytes:
            del session['csrf_token']

        uid = session.get('uid', None)
        if uid:
            g.user = Journalist.query.get(uid)

        g.locale = i18n.get_locale(config)
        g.text_direction = i18n.get_text_direction(g.locale)
        g.html_lang = i18n.locale_to_rfc_5646(g.locale)
        g.locales = i18n.get_locale2name()

        if not app.config['V3_ONION_ENABLED'] or app.config['V2_ONION_ENABLED']:
            g.show_v2_onion_eol_warning = True

        if request.path.split('/')[1] == 'api':
            pass  # We use the @token_required decorator for the API endpoints
        else:  # We are not using the API
            if request.endpoint not in _insecure_views and not logged_in():
                return redirect(url_for('main.login'))

        if request.method == 'POST':
            filesystem_id = request.form.get('filesystem_id')
            if filesystem_id:
                g.filesystem_id = filesystem_id
                g.source = get_source(filesystem_id)

        return None

    app.register_blueprint(main.make_blueprint(config))
    app.register_blueprint(account.make_blueprint(config),
                           url_prefix='/account')
    app.register_blueprint(admin.make_blueprint(config), url_prefix='/admin')
    app.register_blueprint(col.make_blueprint(config), url_prefix='/col')
    api_blueprint = api.make_blueprint(config)
    app.register_blueprint(api_blueprint, url_prefix='/api/v1')
    csrf.exempt(api_blueprint)

    return app
Exemple #4
0
def create_app(config: SDConfig) -> Flask:
    app = Flask(__name__,
                template_folder=config.SOURCE_TEMPLATES_DIR,
                static_folder=path.join(config.SECUREDROP_ROOT, 'static'))
    app.request_class = RequestThatSecuresFileUploads
    app.config.from_object(config.SOURCE_APP_FLASK_CONFIG_CLS)

    # The default CSRF token expiration is 1 hour. Since large uploads can
    # take longer than an hour over Tor, we increase the valid window to 24h.
    app.config['WTF_CSRF_TIME_LIMIT'] = 60 * 60 * 24
    CSRFProtect(app)

    app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
    app.config['SQLALCHEMY_DATABASE_URI'] = config.DATABASE_URI
    db.init_app(app)

    # TODO: Attaching a Storage dynamically like this disables all type checking (and
    # breaks code analysis tools) for code that uses current_app.storage; it should be refactored
    app.storage = Storage(config.STORE_DIR,
                          config.TEMP_DIR,
                          config.JOURNALIST_KEY)

    # TODO: Attaching a CryptoUtil dynamically like this disables all type checking (and
    # breaks code analysis tools) for code that uses current_app.storage; it should be refactored
    app.crypto_util = CryptoUtil(
        scrypt_params=config.SCRYPT_PARAMS,
        scrypt_id_pepper=config.SCRYPT_ID_PEPPER,
        scrypt_gpg_pepper=config.SCRYPT_GPG_PEPPER,
        securedrop_root=config.SECUREDROP_ROOT,
        word_list=config.WORD_LIST,
        nouns_file=config.NOUNS,
        adjectives_file=config.ADJECTIVES,
        gpg_key_dir=config.GPG_KEY_DIR,
    )

    @app.errorhandler(CSRFError)
    def handle_csrf_error(e: CSRFError) -> werkzeug.Response:
        msg = render_template('session_timeout.html')
        session.clear()
        flash(Markup(msg), "important")
        return redirect(url_for('main.index'))

    assets = Environment(app)
    app.config['assets'] = assets

    i18n.setup_app(config, app)

    app.jinja_env.trim_blocks = True
    app.jinja_env.lstrip_blocks = True
    app.jinja_env.globals['version'] = version.__version__
    # Exported to source templates for being included in instructions
    app.jinja_env.globals['submission_key_fpr'] = config.JOURNALIST_KEY
    app.jinja_env.filters['rel_datetime_format'] = \
        template_filters.rel_datetime_format
    app.jinja_env.filters['nl2br'] = evalcontextfilter(template_filters.nl2br)
    app.jinja_env.filters['filesizeformat'] = template_filters.filesizeformat

    for module in [main, info, api]:
        app.register_blueprint(module.make_blueprint(config))  # type: ignore

    @app.before_request
    @ignore_static
    def setup_i18n() -> None:
        """Store i18n-related values in Flask's special g object"""
        g.locale = i18n.get_locale(config)
        g.text_direction = i18n.get_text_direction(g.locale)
        g.html_lang = i18n.locale_to_rfc_5646(g.locale)
        g.locales = i18n.get_locale2name()

    @app.before_request
    @ignore_static
    def check_tor2web() -> None:
        # ignore_static here so we only flash a single message warning
        # about Tor2Web, corresponding to the initial page load.
        if 'X-tor2web' in request.headers:
            flash(Markup(gettext(
                '<strong>WARNING:&nbsp;</strong> '
                'You appear to be using Tor2Web. '
                'This <strong>&nbsp;does not&nbsp;</strong> '
                'provide anonymity. '
                '<a href="{url}">Why is this dangerous?</a>')
                .format(url=url_for('info.tor2web_warning'))),
                "banner-warning")

    @app.before_request
    @ignore_static
    def load_instance_config() -> None:
        app.instance_config = InstanceConfig.get_current()

    @app.before_request
    @ignore_static
    def setup_g() -> Optional[werkzeug.Response]:
        """Store commonly used values in Flask's special g object"""

        if 'expires' in session and datetime.utcnow() >= session['expires']:
            msg = render_template('session_timeout.html')

            # Show expiration message only if the user was
            # either in the codename generation flow or logged in
            show_expiration_message = any([
                session.get('show_expiration_message'),
                logged_in(),
                was_in_generate_flow(),
            ])

            # clear the session after we render the message so it's localized
            session.clear()

            # Persist this properety across sessions to distinguish users whose sessions expired
            # from users who never logged in or generated a codename
            session['show_expiration_message'] = show_expiration_message

            # Redirect to index with flashed message
            if session['show_expiration_message']:
                flash(Markup(msg), "important")
            return redirect(url_for('main.index'))

        session['expires'] = datetime.utcnow() + \
            timedelta(minutes=getattr(config,
                                      'SESSION_EXPIRATION_MINUTES',
                                      120))

        # ignore_static here because `crypto_util.hash_codename` is scrypt
        # (very time consuming), and we don't need to waste time running if
        # we're just serving a static resource that won't need to access
        # these common values.
        if logged_in():
            g.codename = session['codename']
            g.filesystem_id = app.crypto_util.hash_codename(g.codename)
            try:
                g.source = Source.query \
                            .filter(Source.filesystem_id == g.filesystem_id) \
                            .filter_by(deleted_at=None) \
                            .one()
            except NoResultFound as e:
                app.logger.error(
                    "Found no Sources when one was expected: %s" %
                    (e,))
                del session['logged_in']
                del session['codename']
                return redirect(url_for('main.index'))
            g.loc = app.storage.path(g.filesystem_id)
        return None

    @app.errorhandler(404)
    def page_not_found(error: werkzeug.exceptions.HTTPException) -> Tuple[str, int]:
        return render_template('notfound.html'), 404

    @app.errorhandler(500)
    def internal_error(error: werkzeug.exceptions.HTTPException) -> Tuple[str, int]:
        return render_template('error.html'), 500

    return app
Exemple #5
0
    def exec_main_proc(self):
        body = json.loads(self.event.get('body'))
        code = body['code']
        client_id = os.environ['LINE_CHANNEL_ID']
        client_secret = os.environ['LINE_CHANNEL_SECRET']

        try:
            # JWTの取得
            got_jwt = self.__get_line_jwt(code, client_id, client_secret,
                                          settings.LINE_REQUEST_HEADER)

        except LineOauthError as e:
            logging.info(self.event)
            logging.fatal(e)
            traceback.print_exc()
            return ResponseBuilder.response(
                status_code=e.status_code,
                body={'message': json.loads(e.message)})
        # JWTのデコード
        decoded_id_token = self.__decode_jwt(got_jwt, client_secret, client_id)

        user_id = settings.LINE_USERNAME_PREFIX + decoded_id_token['sub']

        if UserUtil.exists_user(self.dynamodb, user_id):
            try:
                external_provider_users = self.dynamodb.Table(
                    os.environ['EXTERNAL_PROVIDER_USERS_TABLE_NAME'])
                external_provider_user = external_provider_users.get_item(
                    Key={
                        'external_provider_user_id': user_id
                    }).get('Item')
                hash_data = external_provider_user['password']
                byte_hash_data = hash_data.encode()
                decoded_iv = external_provider_user['iv']
                iv = decoded_iv.encode()
                password = CryptoUtil.decrypt_password(byte_hash_data, iv)

                has_user_id = UserUtil.has_user_id(self.dynamodb, user_id)
                if external_provider_user is not None and 'user_id' in external_provider_user:
                    user_id = external_provider_user['user_id']

                response = UserUtil.external_provider_login(
                    cognito=self.cognito,
                    user_pool_id=os.environ['COGNITO_USER_POOL_ID'],
                    user_pool_app_id=os.environ['COGNITO_USER_POOL_APP_ID'],
                    user_id=user_id,
                    password=password,
                    provider=os.environ['EXTERNAL_PROVIDER_LOGIN_MARK'])

                return ResponseBuilder.response(
                    status_code=200,
                    body={
                        'access_token':
                        response['AuthenticationResult']['AccessToken'],
                        'id_token':
                        response['AuthenticationResult']['IdToken'],
                        'refresh_token':
                        response['AuthenticationResult']['RefreshToken'],
                        'last_auth_user':
                        user_id,
                        'has_user_id':
                        has_user_id,
                        'status':
                        'login'
                    })

            except ClientError as e:
                logging.info(self.event)
                logging.fatal(e)
                traceback.print_exc()
                return ResponseBuilder.response(
                    status_code=500, body={'message': 'Internal server error'})
        else:
            try:
                if 'email' not in decoded_id_token:
                    return ResponseBuilder.response(
                        status_code=400, body={'message': 'NotRegistered'})
                if not decoded_id_token['email']:
                    email = user_id + '@' + settings.FAKE_USER_EMAIL_DOMAIN
                else:
                    email = decoded_id_token['email']
                backed_temp_password = os.environ[
                    'EXTERNAL_PROVIDER_LOGIN_COMMON_TEMP_PASSWORD']
                backed_password = UserUtil.generate_password()
                response = UserUtil.create_external_provider_user(
                    cognito=self.cognito,
                    user_pool_id=os.environ['COGNITO_USER_POOL_ID'],
                    user_pool_app_id=os.environ['COGNITO_USER_POOL_APP_ID'],
                    user_id=user_id,
                    email=email,
                    backed_temp_password=backed_temp_password,
                    backed_password=backed_password,
                    provider=os.environ['EXTERNAL_PROVIDER_LOGIN_MARK'])

                aes_iv = os.urandom(settings.AES_IV_BYTES)
                encrypted_password = CryptoUtil.encrypt_password(
                    backed_password, aes_iv)
                iv = base64.b64encode(aes_iv).decode()

                UserUtil.add_external_provider_user_info(
                    dynamodb=self.dynamodb,
                    external_provider_user_id=user_id,
                    password=encrypted_password,
                    iv=iv,
                    email=email)
                return ResponseBuilder.response(
                    status_code=200,
                    body={
                        'access_token':
                        response['AuthenticationResult']['AccessToken'],
                        'id_token':
                        response['AuthenticationResult']['IdToken'],
                        'refresh_token':
                        response['AuthenticationResult']['RefreshToken'],
                        'last_auth_user':
                        user_id,
                        'has_user_id':
                        False,
                        'status':
                        'sign_up'
                    })
            except ClientError as e:
                logging.info(self.event)
                logging.fatal(e)
                traceback.print_exc()
                if e.response['Error']['Code'] == 'UsernameExistsException':
                    return ResponseBuilder.response(
                        status_code=400,
                        body={'message': 'EmailExistsException'})
                return ResponseBuilder.response(
                    status_code=500, body={'message': 'Internal server error'})
Exemple #6
0
    def exec_main_proc(self):
        yahoo = YahooUtil(client_id=os.environ['YAHOO_CLIENT_ID'],
                          secret=os.environ['YAHOO_SECRET'],
                          callback_url=os.environ['YAHOO_OAUTH_CALLBACK_URL'])
        try:
            yahoo.verify_state_nonce(dynamodb=self.dynamodb,
                                     state=self.params['state'])

            token = yahoo.get_access_token(code=self.params['code'])

            yahoo.verify_access_token(dynamodb=self.dynamodb,
                                      access_token=token['access_token'],
                                      id_token=token['id_token'])

            user_info = yahoo.get_user_info(access_token=token['access_token'])
        except YahooOauthError as e:
            if e.status_code == 401:
                message = json.loads(e.message)
                return ResponseBuilder.response(
                    status_code=401,
                    body={'message': message['error_description']})
            logging.info(self.event)
            logging.fatal(e)
            traceback.print_exc()
            return ResponseBuilder.response(
                status_code=500, body={'message': 'Internal server error'})

        except (jwt.ExpiredSignatureError, jwt.InvalidTokenError, ClientError,
                YahooVerifyException) as e:
            logging.info(self.event)
            logging.fatal(e)
            traceback.print_exc()
            return ResponseBuilder.response(
                status_code=500, body={'message': 'Internal server error'})

        if UserUtil.exists_user(self.dynamodb, user_info['user_id']):
            try:
                has_user_id = UserUtil.has_user_id(
                    dynamodb=self.dynamodb,
                    external_provider_user_id=user_info['user_id'],
                )
                if has_user_id is True:
                    user_id = UserUtil.get_user_id(
                        dynamodb=self.dynamodb,
                        external_provider_user_id=user_info['user_id'])
                else:
                    user_id = user_info['user_id']

                # パスワードの取得、デコード処理追加
                password = CryptoUtil.get_external_provider_password(
                    dynamodb=self.dynamodb, user_id=user_info['user_id'])

                response = UserUtil.external_provider_login(
                    cognito=self.cognito,
                    user_pool_id=os.environ['COGNITO_USER_POOL_ID'],
                    user_pool_app_id=os.environ['COGNITO_USER_POOL_APP_ID'],
                    user_id=user_id,
                    password=password,
                    provider=os.environ['EXTERNAL_PROVIDER_LOGIN_MARK'])
                return ResponseBuilder.response(
                    status_code=200,
                    body={
                        'access_token':
                        response['AuthenticationResult']['AccessToken'],
                        'id_token':
                        response['AuthenticationResult']['IdToken'],
                        'refresh_token':
                        response['AuthenticationResult']['RefreshToken'],
                        'last_auth_user':
                        user_id,
                        'has_user_id':
                        has_user_id,
                        'status':
                        'login'
                    })
            except ClientError as e:
                logging.info(self.event)
                logging.fatal(e)
                traceback.print_exc()
                return ResponseBuilder.response(
                    status_code=500, body={'message': 'Internal server error'})

        try:
            backed_password = UserUtil.generate_backend_password()
            response = UserUtil.create_external_provider_user(
                cognito=self.cognito,
                user_pool_id=os.environ['COGNITO_USER_POOL_ID'],
                user_pool_app_id=os.environ['COGNITO_USER_POOL_APP_ID'],
                user_id=user_info['user_id'],
                email=user_info['email'],
                backed_temp_password=os.
                environ['EXTERNAL_PROVIDER_LOGIN_COMMON_TEMP_PASSWORD'],
                backed_password=backed_password,
                provider=os.environ['EXTERNAL_PROVIDER_LOGIN_MARK'])

            aes_iv = os.urandom(settings.AES_IV_BYTES)
            encrypted_password = CryptoUtil.encrypt_password(
                backed_password, aes_iv)
            iv = base64.b64encode(aes_iv).decode()

            UserUtil.add_external_provider_user_info(
                dynamodb=self.dynamodb,
                external_provider_user_id=user_info['user_id'],
                password=encrypted_password,
                iv=iv,
                email=user_info['email'])
            return ResponseBuilder.response(
                status_code=200,
                body={
                    'access_token':
                    response['AuthenticationResult']['AccessToken'],
                    'id_token':
                    response['AuthenticationResult']['IdToken'],
                    'refresh_token':
                    response['AuthenticationResult']['RefreshToken'],
                    'last_auth_user':
                    user_info['user_id'],
                    'has_user_id':
                    False,
                    'status':
                    'sign_up'
                })

        except ClientError as e:
            logging.info(self.event)
            logging.fatal(e)
            traceback.print_exc()
            if e.response['Error']['Code'] == 'UsernameExistsException':
                return ResponseBuilder.response(
                    status_code=400, body={'message': 'EmailExistsException'})
            return ResponseBuilder.response(
                status_code=500, body={'message': 'Internal server error'})
Exemple #7
0
    def exec_main_proc(self):
        params = self.event
        body = json.loads(params.get('body'))
        if UserUtil.check_try_to_register_as_line_user(body['user_id']) or \
           UserUtil.check_try_to_register_as_twitter_user(body['user_id']) or \
           UserUtil.check_try_to_register_as_yahoo_user(body['user_id']) or \
           UserUtil.check_try_to_register_as_facebook_user(body['user_id']):
            raise ValidationError('This username is not allowed')
        users_table = self.dynamodb.Table(os.environ['USERS_TABLE_NAME'])
        external_provider_users_table = self.dynamodb.Table(
            os.environ['EXTERNAL_PROVIDER_USERS_TABLE_NAME'])
        external_provider_user_id = params['requestContext']['authorizer'][
            'claims']['cognito:username']
        exist_check_user = users_table.get_item(Key={
            'user_id': body['user_id']
        }).get('Item')

        external_provider_user = external_provider_users_table.get_item(
            Key={
                'external_provider_user_id': external_provider_user_id
            }).get('Item')

        if (external_provider_user
                is not None) and ('user_id' in external_provider_user):
            raise ValidationError('The user id of this user has been added.')

        elif exist_check_user is None:
            # EXTERNAL_PROVIDERのidで作成したcognitoのユーザーを除去
            if UserUtil.delete_external_provider_id_cognito_user(
                    self.cognito, external_provider_user_id):

                # user_idでのCognitoユーザーの作成し直し
                try:
                    email = external_provider_user['email']
                    hash_data = external_provider_user['password']
                    byte_hash_data = hash_data.encode()
                    decoded_iv = external_provider_user['iv']
                    iv = decoded_iv.encode()
                    backed_password = CryptoUtil.decrypt_password(
                        byte_hash_data, iv)

                    backed_temp_password = os.environ[
                        'EXTERNAL_PROVIDER_LOGIN_COMMON_TEMP_PASSWORD']
                    provider = os.environ['EXTERNAL_PROVIDER_LOGIN_MARK']

                    response = UserUtil.create_external_provider_user(
                        cognito=self.cognito,
                        user_id=body['user_id'],
                        user_pool_id=os.environ['COGNITO_USER_POOL_ID'],
                        user_pool_app_id=os.
                        environ['COGNITO_USER_POOL_APP_ID'],
                        email=email,
                        backed_temp_password=backed_temp_password,
                        backed_password=backed_password,
                        provider=provider)

                    UserUtil.force_non_verified_phone(cognito=self.cognito,
                                                      user_id=body['user_id'])

                    UserUtil.wallet_initialization(
                        self.cognito, os.environ['COGNITO_USER_POOL_ID'],
                        body['user_id'])

                    # ExternalProviderUsersテーブルにuser_idを追加
                    UserUtil.add_user_id_to_external_provider_user(
                        body['user_id'], external_provider_users_table,
                        external_provider_user_id)

                    # Usersテーブルにユーザーを作成
                    UserUtil.add_user_profile(
                        dynamodb=self.dynamodb,
                        user_id=body['user_id'],
                        user_display_name=body['user_id'])

                    has_user_id = UserUtil.has_user_id(
                        self.dynamodb, external_provider_user_id)

                    return {
                        'statusCode':
                        200,
                        'body':
                        json.dumps({
                            'access_token':
                            response['AuthenticationResult']['AccessToken'],
                            'last_auth_user':
                            body['user_id'],
                            'id_token':
                            response['AuthenticationResult']['IdToken'],
                            'refresh_token':
                            response['AuthenticationResult']['RefreshToken'],
                            'status':
                            'login',
                            'has_user_id':
                            has_user_id
                        })
                    }

                except ClientError as e:
                    logging.fatal(e)
                    return {
                        'statusCode': 500,
                        'body':
                        json.dumps({'message': 'Internal server error'})
                    }

            return {
                'statusCode': 500,
                'body': json.dumps({'message': 'Internal server error'})
            }

        else:
            raise ValidationError('This id is already in use.')