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()
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: </strong> ' 'You appear to be using Tor2Web. ' 'This <strong> does not </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
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
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: </strong> ' 'You appear to be using Tor2Web. ' 'This <strong> does not </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
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'})
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'})
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.')