async def auth_via_next(user, password, env): """Authorization via NEXT stored credentials to access NEXT""" auth_info = await get_auth_by_next_id(user["next_id"]) # check if a password is set on the account. if auth_info["hashed_password"] is None: raise ApiUnauthorized("No password is set on this account.") salt = auth_info.get("salt") hashed_password = auth_info.get("hashed_password") # compare the hashes. check_password = compare_hash( hashed_password, hashlib.pbkdf2_hmac("sha256", password.encode("utf-8"), salt, 100000).hex(), ) if not check_password: raise ApiUnauthorized("Incorrect username or password.") token = generate_api_key(env("SECRET_KEY"), user["next_id"]) return utils.create_authorization_response( token, {"message": "Authorization successful", "next_id": auth_info.get("next_id")}, )
async def authorize(request): """ User login (authorization) """ required_fields = ["id", "password"] utils.validate_fields(required_fields, request.json) password = request.json.get("password") hashed_pwd = hashlib.sha256(password.encode("utf-8")).hexdigest() auth_info = await auth_query.fetch_info_by_username(request) email = auth_info.get("email") if auth_info.get("hashed_password") is None: if request.app.config.DEMO_MODE: token = generate_api_key( request.app.config.SECRET_KEY, auth_info.get("user_id") ) return utils.create_authorization_response( token, { "message": "Authorization (demo mode) successful", "user_id": auth_info.get("user_id"), }, ) if not email: raise ApiUnauthorized( "Unauthorized: No password nor email is set on this account" ) # TODO: send email confirmation with password set link raise ApiUnauthorized("Unauthorized: No password is set") if auth_info.get("hashed_password") != hashed_pwd: # TODO: rate limit password attempts raise ApiUnauthorized("Unauthorized: Incorrect password") token = generate_api_key(request.app.config.SECRET_KEY, auth_info.get("user_id")) return utils.create_authorization_response( token, {"message": "Authorization successful", "user_id": auth_info.get("user_id")}, )
async def decorated_function(request, *args, **kwargs): if request.token is None: raise ApiUnauthorized("Unauthorized: No bearer token provided") try: id_dict = deserialize_apikey(request.app.config.SECRET_KEY, request.token) await auth_query.fetch_info_by_user_id( request.app.config.DB_CONN, id_dict.get("id")) except (ApiNotFound, BadSignature): raise ApiUnauthorized("Unauthorized: Invalid bearer token") response = await func(request, *args, **kwargs) return response
async def auth_via_next(user, password, env): """Authorization via NEXT stored credentials to access NEXT""" hashed_pwd = hashlib.sha256(password.encode("utf-8")).hexdigest() auth_info = await get_auth_by_next_id(user["next_id"]) if auth_info["hashed_password"] is None: raise ApiUnauthorized("No password is set on this account.") if auth_info["hashed_password"] != hashed_pwd: raise ApiUnauthorized("The password you entered is incorrect.") token = generate_api_key(env("SECRET_KEY"), user["next_id"]) return utils.create_authorization_response( token, {"message": "Authorization successful", "next_id": auth_info.get("next_id")}, )
def extract_request_token(request): """Given a request was initiated by the chatbot engine, retrieve the auth token directly from the slot field, otherwise construct the token by concatenating the the supplied credentials (cookies)""" try: return request.json["tracker"]["slots"]["token"] except (KeyError, TypeError): pass if request.cookies.get(SIGNATURE_KEY) is None: raise ApiUnauthorized("Unauthorized: No token signature provided") if request.cookies.get(PAYLOAD_KEY) is None: raise ApiUnauthorized("Unauthorized: No token payload provided") return ".".join( [request.cookies.get(SIGNATURE_KEY), request.cookies.get(PAYLOAD_KEY)])
async def update_proposal(request, proposal_id): """Update proposal.""" log_request(request) LOGGER.debug("update proposal %s\n%s", proposal_id, request.json) required_fields = ["reason", "status"] validate_fields(required_fields, request.json) if request.json["status"] not in ("REJECTED", "APPROVED"): raise ApiBadRequest( "Bad Request: status must be either 'REJECTED' or 'APPROVED'" ) txn_key, txn_user_id = await get_transactor_key(request=request) conn = await create_connection() proposal_resource = await proposals_query.fetch_proposal_resource( conn, proposal_id=proposal_id ) approvers_list = await compile_proposal_resource(conn, proposal_resource) conn.close() if txn_user_id not in approvers_list["approvers"]: raise ApiUnauthorized( "Bad Request: You don't have the authorization to APPROVE or REJECT the proposal" ) batch_list = PROPOSAL_TRANSACTION[proposal_resource.get("type")][ request.json["status"] ].batch_list( signer_keypair=txn_key, signer_user_id=txn_user_id, proposal_id=proposal_id, object_id=proposal_resource.get("object"), related_id=proposal_resource.get("target"), reason=request.json.get("reason"), ) await send(request.app.config.VAL_CONN, batch_list, request.app.config.TIMEOUT) await send_notification(proposal_resource.get("target"), proposal_id) return json({"proposal_id": proposal_id})
def auth_via_ldap(user_map, password, env): """Authorize via LDAP credentials to access NEXT.""" ldap_server = env("LDAP_SERVER") if ldap_server: server = Server(ldap_server) conn = Connection( server, user=user_map["remote_id"], password=password, read_only=True ) if not conn.bind(): ldap_login_msg = re.search( "data ([0-9a-fA-F]*), v[0-9a-fA-F]*", conn.result["message"] ) if ldap_login_msg and ldap_login_msg.group(1): ldap_err_code = ldap_login_msg.group(1) login_error = LDAP_ERR_MESSAGES.get( ldap_err_code, LDAP_ERR_MESSAGES["default"] ) else: login_error = LDAP_ERR_MESSAGES["default"] raise ApiUnauthorized(login_error) conn.unbind() token = generate_api_key(env("SECRET_KEY"), user_map["next_id"]) return utils.create_authorization_response( token, {"message": "Authorization successful", "next_id": user_map["next_id"]}, ) raise ApiBadRequest("Missing LDAP_SERVER env variable.")
async def decorated_function(request, *args, **kwargs): try: id_dict = deserialize_api_key( request.app.config.SECRET_KEY, utils.extract_request_token(request) ) await get_auth_by_next_id(id_dict.get("id")) except (ApiNotFound, BadSignature): raise ApiUnauthorized("Unauthorized: Invalid bearer token") response = await func(request, *args, **kwargs) return response
def extract_request_token(request): """If a request was initiated by the chatbot engine, retrieve the auth token directly from the tracker slot. Otherwise return the 'Authorization' header token.""" if request.token is not None: return request.token try: return request.json["tracker"]["slots"]["token"] except (KeyError, TypeError): pass raise ApiUnauthorized("Unauthorized: No authentication token provided")
async def authorize(request): required_fields = ["id", "password"] utils.validate_fields(required_fields, request.json) password = request.json.get("password") hashed_pwd = hashlib.sha256(password.encode("utf-8")).hexdigest() auth_info = await auth_query.fetch_info_by_user_name( request.app.config.DB_CONN, request.json.get("id") ) if auth_info is None or auth_info.get("hashed_password") != hashed_pwd: raise ApiUnauthorized("Unauthorized: Incorrect user id or password") token = generate_api_key(request.app.config.SECRET_KEY, auth_info.get("user_id")) return json({"data": {"authorization": token, "user_id": auth_info.get("user_id")}})
async def next_admin_creation(request): """Creating the admin user. Used exclusively for the creation of the NEXT admin Args: request: obj: a request object """ try: txn_key, txn_user_id = await get_transactor_key(request) is_admin = await check_admin_status(txn_user_id) if not is_admin: raise ApiUnauthorized( "You do not have the authorization to create an account.") except ApiUnauthorized: txn_key = Key() txn_user_id = str(uuid4()) key_pair = txn_key next_id = txn_user_id return txn_key, txn_user_id, next_id, key_pair
async def decorated_function(request, *args, **kwargs): try: id_dict = deserialize_api_key( request.app.config.SECRET_KEY, utils.extract_request_token(request) ) conn = await db_utils.create_connection( request.app.config.DB_HOST, request.app.config.DB_PORT, request.app.config.DB_NAME, ) await auth_query.fetch_info_by_user_id(conn, id_dict.get("id")) conn.close() except (ApiNotFound, BadSignature): raise ApiUnauthorized("Unauthorized: Invalid bearer token") response = await func(request, *args, **kwargs) return response
def extract_request_token(request): """If a request was initiated by the chatbot engine, retrieve the auth token directly from the slot field, otherwise return the Authorization header value or cookie token values.""" if "Authorization" in request.headers: return request.headers["Authorization"] token_signature = request.cookies.get(SIGNATURE_KEY) token_payload = request.cookies.get(PAYLOAD_KEY) if token_signature and token_payload: return ".".join([token_signature, token_payload]) try: return request.json["tracker"]["slots"]["token"] except (KeyError, TypeError): pass raise ApiUnauthorized("Unauthorized: No authentication token provided")
async def authorize(request): """ API Endpoint to authenticate and login to the NEXT platform. """ required_fields = ["id", "password", "auth_source"] utils.validate_fields(required_fields, request.json) username = request.json.get("id") password = request.json.get("password") auth_source = request.json.get("auth_source") if auth_source == "next": hashed_pwd = hashlib.sha256(password.encode("utf-8")).hexdigest() auth_info = await auth_query.fetch_info_by_username(request) email = auth_info.get("email") if auth_info.get("hashed_password") is None: if request.app.config.DEMO_MODE: token = generate_api_key( request.app.config.SECRET_KEY, auth_info.get("user_id") ) return utils.create_authorization_response( token, { "message": "Authorization (demo mode) successful", "user_id": auth_info.get("user_id"), }, ) if not email: raise ApiUnauthorized("No password or email is set on this account.") # TODO: send email confirmation with password set link raise ApiUnauthorized("No password is set on this account.") if auth_info.get("hashed_password") != hashed_pwd: # TODO: rate limit password attempts raise ApiUnauthorized("The password you entered is incorrect.") token = generate_api_key( request.app.config.SECRET_KEY, auth_info.get("user_id") ) return utils.create_authorization_response( token, { "message": "Authorization successful", "user_id": auth_info.get("user_id"), }, ) if auth_source == "ldap": if LDAP_SERVER: if username != "" and password != "": ldap_user_dn = await auth_query.fetch_dn_by_username(request) server = Server(LDAP_SERVER) conn = Connection( server, user=ldap_user_dn, password=password, read_only=True ) if not conn.bind(): ldap_login_msg = re.search( "data ([0-9a-fA-F]*), v[0-9a-fA-F]*", conn.result["message"] ) if ldap_login_msg and ldap_login_msg.group(1): ldap_err_code = ldap_login_msg.group(1) login_error = LDAP_ERR_MESSAGES.get( ldap_err_code, LDAP_ERR_MESSAGES["default"] ) else: login_error = LDAP_ERR_MESSAGES["default"] raise ApiUnauthorized(login_error) auth_info = await auth_query.fetch_info_by_username(request) conn.unbind() token = generate_api_key( request.app.config.SECRET_KEY, auth_info.get("user_id") ) return utils.create_authorization_response( token, { "message": "Authorization successful", "user_id": auth_info.get("user_id"), }, ) raise ApiBadRequest(LDAP_ERR_MESSAGES["default"]) raise ApiBadRequest("Missing LDAP_SERVER env variable.") raise ApiBadRequest("Invalid authentication source.")