def authenticate(self, request): """ Check whether the request provides a valid OAuth2 bearer token. The `user` in `cas_auth_response` is the unique GUID of the user. Please do not use the primary key `id` or the email `username`. :param request: the request :return: the user who owns the bear token and the cas repsonse """ client = cas.get_client() try: auth_header_field = request.META['HTTP_AUTHORIZATION'] auth_token = cas.parse_auth_header(auth_header_field) except (cas.CasTokenError, KeyError): return None try: cas_auth_response = client.profile(auth_token) except cas.CasHTTPError: raise exceptions.NotAuthenticated(_('User provided an invalid OAuth2 access token')) if cas_auth_response.authenticated is False: raise exceptions.NotAuthenticated(_('CAS server failed to authenticate this token')) user = OSFUser.load(cas_auth_response.user) if not user: raise exceptions.AuthenticationFailed(_('Could not find the user associated with this token')) check_user(user) return user, cas_auth_response
def before_request(): from framework.auth import authenticate from framework.auth.core import User from framework.auth import cas # Central Authentication Server Ticket Validation and Authentication ticket = request.args.get('ticket') if ticket: service_url = furl.furl(request.url) service_url.args.pop('ticket') # Attempt autn wih CAS, and return a proper redirect response return cas.make_response_from_ticket(ticket=ticket, service_url=service_url.url) # Central Authentication Server OAuth Bearer Token authorization = request.headers.get('Authorization') if authorization and authorization.startswith('Bearer '): client = cas.get_client() try: access_token = cas.parse_auth_header(authorization) except cas.CasTokenError as err: # NOTE: We assume that the request is an AJAX request return jsonify({'message_short': 'Invalid Bearer token', 'message_long': err.args[0]}), http.UNAUTHORIZED cas_resp = client.profile(access_token) if cas_resp.authenticated: user = User.load(cas_resp.user) return authenticate(user, access_token=access_token, response=None) return make_response('', http.UNAUTHORIZED) if request.authorization: # TODO: Fix circular import from framework.auth.core import get_user user = get_user( email=request.authorization.username, password=request.authorization.password ) # Create empty session # TODO: Shoudn't need to create a session for Basic Auth session = Session() if user: session.data['auth_user_username'] = user.username session.data['auth_user_id'] = user._primary_key session.data['auth_user_fullname'] = user.fullname else: # Invalid key: Not found in database session.data['auth_error_code'] = http.FORBIDDEN set_session(session) return cookie = request.cookies.get(settings.COOKIE_NAME) if cookie: try: session_id = itsdangerous.Signer(settings.SECRET_KEY).unsign(cookie) session = Session.load(session_id) or Session(_id=session_id) set_session(session) return except: pass
def reset_secret(self, save=False): """ Reset the secret of an ApiOAuth2Application Revokes all tokens """ client = cas.get_client() client.revoke_application_tokens(self.client_id, self.client_secret) self.client_secret = generate_client_secret() if save: self.save() return True
def get_auth(auth, **kwargs): cas_resp = None if not auth.user: # Central Authentication Server OAuth Bearer Token authorization = request.headers.get('Authorization') if authorization and authorization.startswith('Bearer '): client = cas.get_client() try: access_token = cas.parse_auth_header(authorization) cas_resp = client.profile(access_token) except cas.CasError as err: sentry.log_exception() # NOTE: We assume that the request is an AJAX request return json_renderer(err) if cas_resp.authenticated: auth.user = User.load(cas_resp.user) if not auth.user: auth.user = User.from_cookie(request.args.get('cookie')) try: action = request.args['action'] node_id = request.args['nid'] provider_name = request.args['provider'] except KeyError: raise HTTPError(httplib.BAD_REQUEST) node = Node.load(node_id) if not node: raise HTTPError(httplib.NOT_FOUND) check_access(node, auth, action, cas_resp) provider_settings = node.get_addon(provider_name) if not provider_settings: raise HTTPError(httplib.BAD_REQUEST) try: credentials = provider_settings.serialize_waterbutler_credentials() settings = provider_settings.serialize_waterbutler_settings() except exceptions.AddonError: log_exception() raise HTTPError(httplib.BAD_REQUEST) return { 'auth': make_auth(auth.user), # A waterbutler auth dict not an Auth object 'credentials': credentials, 'settings': settings, 'callback_url': node.api_url_for( ('create_waterbutler_log' if not node.is_registration else 'registration_callbacks'), _absolute=True, ), }
def deactivate(self, save=False): """ Deactivate an ApiOAuth2Application Does not delete the database record, but revokes all tokens and sets a flag that hides this instance from API """ client = cas.get_client() # Will raise a CasHttpError if deletion fails, which will also stop setting of active=False. resp = client.revoke_application_tokens(self.client_id, self.client_secret) # noqa self.is_active = False if save: self.save() return True
def deactivate(self, save=False): """ Deactivate an ApiOAuth2PersonalToken Does not delete the database record, but hides this instance from API """ client = cas.get_client() # Will raise a CasHttpError if deletion fails for any reason other than the token # not yet being created. This will also stop setting of active=False. try: resp = client.revoke_tokens({"token": self.token_id}) # noqa except cas.CasHTTPError as e: if e.code == 400: pass # Token hasn't been used yet, so not created in cas else: raise e self.is_active = False if save: self.save() return True
def authenticate(self, request): client = cas.get_client() # Returns a CAS server client try: auth_header_field = request.META["HTTP_AUTHORIZATION"] auth_token = cas.parse_auth_header(auth_header_field) except (cas.CasTokenError, KeyError): return None # If no token in header, then this method is not applicable # Found a token; query CAS for the associated user id try: resp = client.profile(auth_token) except cas.CasHTTPError: raise exceptions.NotAuthenticated('User provided an invalid OAuth2 access token') if resp.authenticated is False: raise exceptions.NotAuthenticated('CAS server failed to authenticate this token') user_id = resp.user user = User.load(user_id) if user is None: raise exceptions.AuthenticationFailed("Could not find the user associated with this token") return user, auth_token
def get_auth(auth, **kwargs): cas_resp = None if not auth.user: # Central Authentication Server OAuth Bearer Token authorization = request.headers.get('Authorization') if authorization and authorization.startswith('Bearer '): client = cas.get_client() try: access_token = cas.parse_auth_header(authorization) cas_resp = client.profile(access_token) except cas.CasError as err: sentry.log_exception() # NOTE: We assume that the request is an AJAX request return json_renderer(err) if cas_resp.authenticated: auth.user = User.load(cas_resp.user) try: data = jwt.decode( jwe.decrypt(request.args.get('payload', '').encode('utf-8'), WATERBUTLER_JWE_KEY), settings.WATERBUTLER_JWT_SECRET, options={'require_exp': True}, algorithm=settings.WATERBUTLER_JWT_ALGORITHM )['data'] except (jwt.InvalidTokenError, KeyError): raise HTTPError(httplib.FORBIDDEN) if not auth.user: auth.user = User.from_cookie(data.get('cookie', '')) try: action = data['action'] node_id = data['nid'] provider_name = data['provider'] except KeyError: raise HTTPError(httplib.BAD_REQUEST) node = Node.load(node_id) if not node: raise HTTPError(httplib.NOT_FOUND) check_access(node, auth, action, cas_resp) provider_settings = node.get_addon(provider_name) if not provider_settings: raise HTTPError(httplib.BAD_REQUEST) try: credentials = provider_settings.serialize_waterbutler_credentials() waterbutler_settings = provider_settings.serialize_waterbutler_settings() except exceptions.AddonError: log_exception() raise HTTPError(httplib.BAD_REQUEST) return {'payload': jwe.encrypt(jwt.encode({ 'exp': datetime.datetime.utcnow() + datetime.timedelta(seconds=settings.WATERBUTLER_JWT_EXPIRATION), 'data': { 'auth': make_auth(auth.user), # A waterbutler auth dict not an Auth object 'credentials': credentials, 'settings': waterbutler_settings, 'callback_url': node.api_url_for( ('create_waterbutler_log' if not node.is_registration else 'registration_callbacks'), _absolute=True, ), } }, settings.WATERBUTLER_JWT_SECRET, algorithm=settings.WATERBUTLER_JWT_ALGORITHM), WATERBUTLER_JWE_KEY)}
def get_auth(auth, **kwargs): cas_resp = None if not auth.user: # Central Authentication Server OAuth Bearer Token authorization = request.headers.get('Authorization') if authorization and authorization.startswith('Bearer '): client = cas.get_client() try: access_token = cas.parse_auth_header(authorization) cas_resp = client.profile(access_token) except cas.CasError as err: sentry.log_exception() # NOTE: We assume that the request is an AJAX request return json_renderer(err) if cas_resp.authenticated: auth.user = OSFUser.load(cas_resp.user) try: data = jwt.decode( jwe.decrypt(request.args.get('payload', '').encode('utf-8'), WATERBUTLER_JWE_KEY), settings.WATERBUTLER_JWT_SECRET, options={'require_exp': True}, algorithm=settings.WATERBUTLER_JWT_ALGORITHM )['data'] except (jwt.InvalidTokenError, KeyError) as err: sentry.log_message(str(err)) raise HTTPError(httplib.FORBIDDEN) if not auth.user: auth.user = OSFUser.from_cookie(data.get('cookie', '')) try: action = data['action'] node_id = data['nid'] provider_name = data['provider'] except KeyError: raise HTTPError(httplib.BAD_REQUEST) node = AbstractNode.load(node_id) or Preprint.load(node_id) if not node: raise HTTPError(httplib.NOT_FOUND) check_access(node, auth, action, cas_resp) provider_settings = None if hasattr(node, 'get_addon'): provider_settings = node.get_addon(provider_name) if not provider_settings: raise HTTPError(httplib.BAD_REQUEST) try: path = data.get('path') version = data.get('version') credentials = None waterbutler_settings = None fileversion = None if provider_name == 'osfstorage': if path and version: # check to see if this is a file or a folder filenode = OsfStorageFileNode.load(path.strip('/')) if filenode and filenode.is_file: try: fileversion = FileVersion.objects.filter( basefilenode___id=path.strip('/'), identifier=version ).select_related('region').get() except FileVersion.DoesNotExist: raise HTTPError(httplib.BAD_REQUEST) # path and no version, use most recent version elif path: filenode = OsfStorageFileNode.load(path.strip('/')) if filenode and filenode.is_file: fileversion = FileVersion.objects.filter( basefilenode=filenode ).select_related('region').order_by('-created').first() if fileversion: region = fileversion.region credentials = region.waterbutler_credentials waterbutler_settings = fileversion.serialize_waterbutler_settings( node_id=provider_settings.owner._id if provider_settings else node._id, root_id=provider_settings.root_node._id if provider_settings else node.root_folder._id, ) # If they haven't been set by version region, use the NodeSettings region if not (credentials and waterbutler_settings): credentials = node.serialize_waterbutler_credentials(provider_name) waterbutler_settings = node.serialize_waterbutler_settings(provider_name) except exceptions.AddonError: log_exception() raise HTTPError(httplib.BAD_REQUEST) # TODO: Add a signal here? if waffle.switch_is_active(features.ELASTICSEARCH_METRICS): user = auth.user if isinstance(node, Preprint) and not node.is_contributor(user): metric_class = get_metric_class_for_action(action) if metric_class: try: metric_class.record_for_preprint( preprint=node, user=user, version=fileversion.identifier if fileversion else None, path=path ) except es_exceptions.ConnectionError: log_exception() return {'payload': jwe.encrypt(jwt.encode({ 'exp': timezone.now() + datetime.timedelta(seconds=settings.WATERBUTLER_JWT_EXPIRATION), 'data': { 'auth': make_auth(auth.user), # A waterbutler auth dict not an Auth object 'credentials': credentials, 'settings': waterbutler_settings, 'callback_url': node.api_url_for( ('create_waterbutler_log' if not getattr(node, 'is_registration', False) else 'registration_callbacks'), _absolute=True, _internal=True ) } }, settings.WATERBUTLER_JWT_SECRET, algorithm=settings.WATERBUTLER_JWT_ALGORITHM), WATERBUTLER_JWE_KEY)}
def before_request(): from framework.auth import authenticate from framework.auth.core import User from framework.auth import cas # Central Authentication Server Ticket Validation and Authentication ticket = request.args.get('ticket') if ticket: service_url = furl.furl(request.url) service_url.args.pop('ticket') # Attempt autn wih CAS, and return a proper redirect response return cas.make_response_from_ticket(ticket=ticket, service_url=service_url.url) # Central Authentication Server OAuth Bearer Token authorization = request.headers.get('Authorization') if authorization and authorization.startswith('Bearer '): client = cas.get_client() try: access_token = cas.parse_auth_header(authorization) except cas.CasTokenError as err: # NOTE: We assume that the request is an AJAX request return jsonify({'message_short': 'Invalid Bearer token', 'message_long': err.args[0]}), http.UNAUTHORIZED cas_resp = client.profile(access_token) if cas_resp.authenticated: user = User.load(cas_resp.user) return authenticate(user, access_token=access_token, response=None) return make_response('', http.UNAUTHORIZED) if request.authorization: # Create a session from the API key; if key is # not valid, save the HTTP error code in the # "auth_error_code" field of session.data # Create empty session session = Session() # Hack: Avoid circular import from website.project.model import ApiKey api_label = request.authorization.username api_key_id = request.authorization.password api_key = ApiKey.load(api_key_id) if api_key: user = api_key.user__keyed and api_key.user__keyed[0] node = api_key.node__keyed and api_key.node__keyed[0] session.data['auth_api_label'] = api_label session.data['auth_api_key'] = api_key._primary_key if user: session.data['auth_user_username'] = user.username session.data['auth_user_id'] = user._primary_key session.data['auth_user_fullname'] = user.fullname elif node: session.data['auth_node_id'] = node._primary_key else: # Invalid key: Not attached to user or node session.data['auth_error_code'] = http.FORBIDDEN else: # Invalid key: Not found in database session.data['auth_error_code'] = http.FORBIDDEN set_session(session) return cookie = request.cookies.get(settings.COOKIE_NAME) if cookie: try: session_id = itsdangerous.Signer(settings.SECRET_KEY).unsign(cookie) session = Session.load(session_id) or Session(_id=session_id) set_session(session) return except: pass ## TODO: Create session in before_request, cookie in after_request ## Retry request, preserving status code #response = redirect(request.path, code=307) return create_session(None)
def get_auth(auth, **kwargs): cas_resp = None if not auth.user: # Central Authentication Server OAuth Bearer Token authorization = request.headers.get("Authorization") if authorization and authorization.startswith("Bearer "): client = cas.get_client() try: access_token = cas.parse_auth_header(authorization) cas_resp = client.profile(access_token) except cas.CasError as err: sentry.log_exception() # NOTE: We assume that the request is an AJAX request return json_renderer(err) if cas_resp.authenticated: auth.user = User.load(cas_resp.user) try: data = jwt.decode( jwe.decrypt(request.args.get("payload", "").encode("utf-8"), WATERBUTLER_JWE_KEY), settings.WATERBUTLER_JWT_SECRET, options={"require_exp": True}, algorithm=settings.WATERBUTLER_JWT_ALGORITHM, )["data"] except (jwt.InvalidTokenError, KeyError): raise HTTPError(httplib.FORBIDDEN) if not auth.user: auth.user = User.from_cookie(data.get("cookie", "")) try: action = data["action"] node_id = data["nid"] provider_name = data["provider"] except KeyError: raise HTTPError(httplib.BAD_REQUEST) node = Node.load(node_id) if not node: raise HTTPError(httplib.NOT_FOUND) check_access(node, auth, action, cas_resp) provider_settings = node.get_addon(provider_name) if not provider_settings: raise HTTPError(httplib.BAD_REQUEST) try: credentials = provider_settings.serialize_waterbutler_credentials() waterbutler_settings = provider_settings.serialize_waterbutler_settings() except exceptions.AddonError: log_exception() raise HTTPError(httplib.BAD_REQUEST) return { "payload": jwe.encrypt( jwt.encode( { "exp": datetime.datetime.utcnow() + datetime.timedelta(seconds=settings.WATERBUTLER_JWT_EXPIRATION), "data": { "auth": make_auth(auth.user), # A waterbutler auth dict not an Auth object "credentials": credentials, "settings": waterbutler_settings, "callback_url": node.api_url_for( ("create_waterbutler_log" if not node.is_registration else "registration_callbacks"), _absolute=True, ), }, }, settings.WATERBUTLER_JWT_SECRET, algorithm=settings.WATERBUTLER_JWT_ALGORITHM, ), WATERBUTLER_JWE_KEY, ) }
def get_auth(auth, **kwargs): cas_resp = None if not auth.user: # Central Authentication Server OAuth Bearer Token authorization = request.headers.get('Authorization') if authorization and authorization.startswith('Bearer '): client = cas.get_client() try: access_token = cas.parse_auth_header(authorization) cas_resp = client.profile(access_token) except cas.CasError as err: sentry.log_exception() # NOTE: We assume that the request is an AJAX request return json_renderer(err) if cas_resp.authenticated: auth.user = User.load(cas_resp.user) try: data = jwt.decode(jwe.decrypt( request.args.get('payload', '').encode('utf-8'), WATERBUTLER_JWE_KEY), settings.WATERBUTLER_JWT_SECRET, options={'require_exp': True}, algorithm=settings.WATERBUTLER_JWT_ALGORITHM)['data'] except (jwt.InvalidTokenError, KeyError): raise HTTPError(httplib.FORBIDDEN) if not auth.user: auth.user = User.from_cookie(data.get('cookie', '')) try: action = data['action'] node_id = data['nid'] provider_name = data['provider'] except KeyError: raise HTTPError(httplib.BAD_REQUEST) node = Node.load(node_id) if not node: raise HTTPError(httplib.NOT_FOUND) check_access(node, auth, action, cas_resp) provider_settings = node.get_addon(provider_name) if not provider_settings: raise HTTPError(httplib.BAD_REQUEST) try: credentials = provider_settings.serialize_waterbutler_credentials() waterbutler_settings = provider_settings.serialize_waterbutler_settings( ) except exceptions.AddonError: log_exception() raise HTTPError(httplib.BAD_REQUEST) return { 'payload': jwe.encrypt( jwt.encode( { 'exp': datetime.datetime.utcnow() + datetime.timedelta( seconds=settings.WATERBUTLER_JWT_EXPIRATION), 'data': { 'auth': make_auth( auth.user ), # A waterbutler auth dict not an Auth object 'credentials': credentials, 'settings': waterbutler_settings, 'callback_url': node.api_url_for( ('create_waterbutler_log' if not node.is_registration else 'registration_callbacks'), _absolute=True, ), } }, settings.WATERBUTLER_JWT_SECRET, algorithm=settings.WATERBUTLER_JWT_ALGORITHM), WATERBUTLER_JWE_KEY) }
def get_auth(auth, **kwargs): cas_resp = None if not auth.user: # Central Authentication Server OAuth Bearer Token authorization = request.headers.get('Authorization') if authorization and authorization.startswith('Bearer '): client = cas.get_client() try: access_token = cas.parse_auth_header(authorization) cas_resp = client.profile(access_token) except cas.CasError as err: sentry.log_exception() # NOTE: We assume that the request is an AJAX request return json_renderer(err) if cas_resp.authenticated: auth.user = OSFUser.load(cas_resp.user) try: data = jwt.decode(jwe.decrypt( request.args.get('payload', '').encode('utf-8'), WATERBUTLER_JWE_KEY), settings.WATERBUTLER_JWT_SECRET, options={'require_exp': True}, algorithm=settings.WATERBUTLER_JWT_ALGORITHM)['data'] except (jwt.InvalidTokenError, KeyError) as err: sentry.log_message(str(err)) raise HTTPError(http_status.HTTP_403_FORBIDDEN) if not auth.user: auth.user = OSFUser.from_cookie(data.get('cookie', '')) try: action = data['action'] node_id = data['nid'] provider_name = data['provider'] except KeyError: raise HTTPError(http_status.HTTP_400_BAD_REQUEST) node = AbstractNode.load(node_id) or Preprint.load(node_id) if node and node.is_deleted: raise HTTPError(http_status.HTTP_410_GONE) elif not node: raise HTTPError(http_status.HTTP_404_NOT_FOUND) check_access(node, auth, action, cas_resp) provider_settings = None if hasattr(node, 'get_addon'): provider_settings = node.get_addon(provider_name) if not provider_settings: raise HTTPError(http_status.HTTP_400_BAD_REQUEST) path = data.get('path') credentials = None waterbutler_settings = None fileversion = None if provider_name == 'osfstorage': if path: file_id = path.strip('/') # check to see if this is a file or a folder filenode = OsfStorageFileNode.load(path.strip('/')) if filenode and filenode.is_file: # default to most recent version if none is provided in the response version = int(data['version']) if data.get( 'version') else filenode.versions.count() try: fileversion = FileVersion.objects.filter( basefilenode___id=file_id, identifier=version).select_related('region').get() except FileVersion.DoesNotExist: raise HTTPError(http_status.HTTP_400_BAD_REQUEST) if auth.user: # mark fileversion as seen FileVersionUserMetadata.objects.get_or_create( user=auth.user, file_version=fileversion) if not node.is_contributor_or_group_member(auth.user): from_mfr = download_is_from_mfr(request, payload=data) # version index is 0 based version_index = version - 1 if action == 'render': update_analytics(node, filenode, version_index, 'view') elif action == 'download' and not from_mfr: update_analytics(node, filenode, version_index, 'download') if waffle.switch_is_active(features.ELASTICSEARCH_METRICS): if isinstance(node, Preprint): metric_class = get_metric_class_for_action( action, from_mfr=from_mfr) if metric_class: try: metric_class.record_for_preprint( preprint=node, user=auth.user, version=fileversion.identifier if fileversion else None, path=path) except es_exceptions.ConnectionError: log_exception() if fileversion and provider_settings: region = fileversion.region credentials = region.waterbutler_credentials waterbutler_settings = fileversion.serialize_waterbutler_settings( node_id=provider_settings.owner._id, root_id=provider_settings.root_node._id, ) # If they haven't been set by version region, use the NodeSettings or Preprint directly if not (credentials and waterbutler_settings): credentials = node.serialize_waterbutler_credentials(provider_name) waterbutler_settings = node.serialize_waterbutler_settings( provider_name) return { 'payload': jwe.encrypt( jwt.encode( { 'exp': timezone.now() + datetime.timedelta( seconds=settings.WATERBUTLER_JWT_EXPIRATION), 'data': { 'auth': make_auth( auth.user ), # A waterbutler auth dict not an Auth object 'credentials': credentials, 'settings': waterbutler_settings, 'callback_url': node.api_url_for( ('create_waterbutler_log' if not getattr(node, 'is_registration', False) else 'registration_callbacks'), _absolute=True, _internal=True) } }, settings.WATERBUTLER_JWT_SECRET, algorithm=settings.WATERBUTLER_JWT_ALGORITHM), WATERBUTLER_JWE_KEY) }