Exemplo n.º 1
0
    def get_signed_url(
        self,
        protocol,
        action,
        expires_in,
        force_signed_url=True,
        r_pays_project=None,
        file_name=None,
    ):
        if self.index_document.get("authz"):
            action_to_permission = {
                "upload": "write-storage",
                "download": "read-storage",
            }
            if not self.check_authz(action_to_permission[action]):
                raise Unauthorized(
                    f"Either you weren't logged in or you don't have "
                    f"{action_to_permission[action]} permission "
                    f"on {self.index_document['authz']} for fence")
        else:
            if self.public_acl and action == "upload":
                raise Unauthorized(
                    "Cannot upload on public files while using acl field")
            # don't check the authorization if the file is public
            # (downloading public files with no auth is fine)
            if not self.public_acl and not self.check_authorization(action):
                raise Unauthorized(
                    f"You don't have access permission on this file: {self.file_id}"
                )

        if action is not None and action not in SUPPORTED_ACTIONS:
            raise NotSupported("action {} is not supported".format(action))
        return self._get_signed_url(protocol, action, expires_in,
                                    force_signed_url, r_pays_project,
                                    file_name)
Exemplo n.º 2
0
 def get_signed_url(self,
                    protocol,
                    action,
                    expires_in,
                    force_signed_url=True):
     if self.public and action == "upload":
         raise Unauthorized("Cannot upload on public files")
     # don't check the authorization if the file is public
     # (downloading public files with no auth is fine)
     if not self.public and not self.check_authorization(action):
         raise Unauthorized("You don't have access permission on this file")
     if action is not None and action not in SUPPORTED_ACTIONS:
         raise NotSupported("action {} is not supported".format(action))
     return self._get_signed_url(protocol, action, expires_in,
                                 force_signed_url)
Exemplo n.º 3
0
def any_access():
    """
    Check if the user is in our database

    :note if a user is specified with empty access it still counts

    :query project: (optional) Check for read access to a specific program/project

    """
    project = flask.request.args.get("project")
    projects = None
    if flask.g.token is None:
        flask.g.user = current_session.merge(flask.g.user)
        projects = flask.g.user.project_access
    else:
        projects = flask.g.token["context"]["user"]["projects"]

    success = False

    if not project and len(projects) > 0:
        success = True
    elif project and project in projects:
        access = projects[project]
        if "read" in access:
            success = True

    if success:
        resp = flask.make_response(flask.jsonify({"result": "success"}), 200)
        resp.headers["REMOTE_USER"] = flask.g.user.username
        return resp
    raise Unauthorized("Please login")
Exemplo n.º 4
0
def create_user_access_token(keypair, api_key, expires_in):
    """
    create access token given a user's api key
    Args:
        keypair: RSA keypair for signing jwt
        api_key: user created jwt token, the azp should match with user.id
        expires_in: expiration time in seconds
    Return:
        access token
    """
    try:
        claims = validate_jwt(api_key, scope={"fence"}, purpose="api_key")
        # scopes = claims["scope"]

        ##### begin api key patch block #####
        # TODO: In the next release, remove this block and uncomment line above.
        # Old API keys are not compatible with new validation
        # This is to help transition
        try:
            scopes = claims["scope"]
        except KeyError as e:
            scopes = claims["aud"]
        ##### end api key patch block #####

        user = get_user_from_claims(claims)
    except Exception as e:
        raise Unauthorized(str(e))
    return token.generate_signed_access_token(
        keypair.kid, keypair.private_key, user, expires_in, scopes
    ).token
Exemplo n.º 5
0
def get_jwt():
    """
    Return the user's JWT from authorization header. Requires flask application context.
    Raises:
        - Unauthorized, if header is missing or not in the correct format
    """
    header = flask.request.headers.get("Authorization")
    if not header:
        raise Unauthorized("missing authorization header")
    try:
        bearer, token = header.split(" ")
    except ValueError:
        raise Unauthorized("authorization header not in expected format")
    if bearer.lower() != "bearer":
        raise Unauthorized("expected bearer token in auth header")
    return token
Exemplo n.º 6
0
def handle_login(scope):
    if flask.session.get('username'):
        login_user(
            flask.request,
            flask.session['username'],
            flask.session['provider'],
        )

    eppn = flask.request.headers.get(
        flask.current_app.config['SHIBBOLETH_HEADER']
    )

    if flask.current_app.config.get('MOCK_AUTH') is True:
        eppn = 'test'
    # if there is authorization header for oauth
    if 'Authorization' in flask.request.headers:
        has_oauth(scope=scope)
    # if there is shibboleth session, then create user session and
    # log user in
    elif eppn:
        username = eppn.split('!')[-1]
        flask.session['username'] = username
        flask.session['provider'] = IdentityProvider.itrust
        login_user(flask.request, username, flask.session['provider'])
    else:
        raise Unauthorized("Please login")
Exemplo n.º 7
0
    def get_credential_to_access_bucket(self, aws_creds, expires_in):
        s3_buckets = get_value(
            flask.current_app.config,
            "S3_BUCKETS",
            InternalError("buckets not configured"),
        )
        if len(aws_creds) == 0 and len(s3_buckets) == 0:
            raise InternalError("no bucket is configured")
        if len(aws_creds) == 0 and len(s3_buckets) > 0:
            raise InternalError("credential for buckets is not configured")

        bucket_cred = None
        for pattern in s3_buckets:
            if re.match("^" + pattern + "$", self.parsed_url.netloc):
                bucket_cred = s3_buckets[pattern]
                break
        if bucket_cred is None:
            raise Unauthorized("permission denied for bucket")

        cred_key = get_value(
            bucket_cred, "cred",
            InternalError("credential of that bucket is missing"))
        if cred_key == "*":
            return {"aws_access_key_id": "*"}

        if "role-arn" not in bucket_cred:
            return get_value(
                aws_creds,
                cred_key,
                InternalError("aws credential of that bucket is not found"),
            )
        else:
            return S3IndexedFileLocation.assume_role(aws_creds, bucket_cred,
                                                     cred_key, expires_in)
Exemplo n.º 8
0
    def get(self):
        """
        Complete the shibboleth login.
        """
        shib_header = config.get("SHIBBOLETH_HEADER")
        if not shib_header:
            raise InternalError("Missing shibboleth header configuration")

        # eppn stands for eduPersonPrincipalName
        username = flask.request.headers.get("eppn")
        entityID = flask.session.get("entityID")

        # if eppn not available or logging in through NIH
        if not username or not entityID or entityID == "urn:mace:incommon:nih.gov":
            persistent_id = flask.request.headers.get(shib_header)
            username = persistent_id.split("!")[-1] if persistent_id else None
            if not username:
                # some inCommon providers are not returning eppn
                # or persistent_id. See PXP-4309
                # print("shib_header", shib_header)
                # print("flask.request.headers", flask.request.headers)
                raise Unauthorized("Unable to retrieve username")

        idp = IdentityProvider.itrust
        if entityID:
            idp = entityID
        login_user(flask.request, username, idp)

        if flask.session.get("redirect"):
            return flask.redirect(flask.session.get("redirect"))

        return "logged in"
Exemplo n.º 9
0
    def get(self):
        """Handle ``GET /login/fence/login``."""
        # Check that the state passed back from IDP fence is the same as the
        # one stored previously.
        mismatched_state = (
            'state' not in flask.request.args or 'state' not in flask.session
            or flask.request.args['state'] != flask.session.pop('state'))
        if mismatched_state:
            raise Unauthorized('authorization request failed; state mismatch')
        # Get the token response and log in the user.
        redirect_uri = flask.current_app.fence_client.session.redirect_uri
        tokens = flask.current_app.fence_client.fetch_access_token(
            redirect_uri, **flask.request.args.to_dict())
        id_token_claims = validate_jwt(tokens['id_token'],
                                       aud={'openid'},
                                       purpose='id',
                                       attempt_refresh=True)
        username = id_token_claims['context']['user']['name']
        flask.session['username'] = username
        flask.session['provider'] = IdentityProvider.fence
        login_user(flask.request, username, IdentityProvider.fence)

        if 'redirect' in flask.session:
            return flask.redirect(flask.session.get('redirect'))
        return flask.jsonify({'username': username})
Exemplo n.º 10
0
 def set_acls(self):
     if "acl" in self.index_document:
         return set(self.index_document["acl"])
     elif "acls" in self.metadata:
         return set(self.metadata["acls"].split(","))
     else:
         raise Unauthorized("This file is not accessible")
Exemplo n.º 11
0
def has_oauth(scope=None):
    scope = scope or set()
    scope.update({"openid"})
    try:
        access_token_claims = validate_jwt(aud=scope, purpose="access")
    except JWTError as e:
        raise Unauthorized("failed to validate token: {}".format(e))
    user_id = access_token_claims["sub"]
    user = current_session.query(User).filter_by(id=int(user_id)).first()
    if not user:
        raise Unauthorized("no user found with id: {}".format(user_id))
    # set some application context for current user and client id
    flask.g.user = user
    # client_id should be None if the field doesn't exist or is empty
    flask.g.client_id = access_token_claims.get("azp") or None
    flask.g.token = access_token_claims
Exemplo n.º 12
0
 def check_scope_and_call(*args, **kwargs):
     if "_all" in flask.g.scopes or scope in flask.g.scopes:
         return f(*args, **kwargs)
     else:
         raise Unauthorized(
             "Requested scope {} can't access this endpoint".format(
                 scope))
Exemplo n.º 13
0
def get_current_user():
    username = flask.session.get('username')
    if flask.current_app.config.get('MOCK_AUTH', False) is True:
        username = '******'
    if not username:
        raise Unauthorized("User not logged in")
    return current_session.query(User).filter_by(username=username).first()
Exemplo n.º 14
0
def _get_patched_service_account_error_status(id_, sa):
    """
    Get error status for attempting to patch given service account with access.

    Args:
        id_ (str): Google service account identifier to update
        sa (
            fence.resources.google.service_account.GoogleServiceAccountRegistration
        ): the service account object with its email, project_access, a google project,
           and optionally a user who is attempting to modify/add
    """
    # check if user has permission to update the service account
    authorized = is_user_member_of_all_google_projects(
        sa.user_id, [sa.google_project_id]
    )
    if not authorized:
        msg = (
            'User "{}" does not have permission to update the provided '
            'service account "{}".'.format(sa.user_id, id_)
        )
        raise Unauthorized(msg)

    error_response = _get_service_account_error_status(sa)

    return error_response
Exemplo n.º 15
0
    def get(self):
        """Handle ``GET /login/fence/login``."""
        # Check that the state passed back from IDP fence is the same as the
        # one stored previously.
        mismatched_state = (
            "state" not in flask.request.args or "state" not in flask.session
            or flask.request.args["state"] != flask.session.pop("state", ""))
        if mismatched_state and not config.get("MOCK_AUTH"):
            raise Unauthorized(
                "Login flow was interrupted (state mismatch). Please go back to the"
                " login page for the original application to continue.")
        # Get the token response and log in the user.
        redirect_uri = flask.current_app.fence_client._get_session(
        ).redirect_uri
        tokens = flask.current_app.fence_client.fetch_access_token(
            redirect_uri, **flask.request.args.to_dict())
        id_token_claims = validate_jwt(tokens["id_token"],
                                       aud={"openid"},
                                       purpose="id",
                                       attempt_refresh=True)
        username = id_token_claims["context"]["user"]["name"]
        login_user(
            username,
            IdentityProvider.fence,
            fence_idp=flask.session.get("fence_idp"),
            shib_idp=flask.session.get("shib_idp"),
        )
        self.post_login()

        if "redirect" in flask.session:
            return flask.redirect(flask.session.get("redirect"))
        return flask.jsonify({"username": username})
Exemplo n.º 16
0
        def wrapper(*args, **kwargs):
            if flask.session.get("username"):
                login_user(flask.session["username"],
                           flask.session["provider"])
                return f(*args, **kwargs)

            eppn = None
            if config["LOGIN_OPTIONS"]:
                enable_shib = "shibboleth" in [
                    option["idp"] for option in config["LOGIN_OPTIONS"]
                ]
            else:
                # fall back on "providers"
                enable_shib = "shibboleth" in config.get(
                    "ENABLED_IDENTITY_PROVIDERS", {}).get("providers", {})

            if enable_shib and "SHIBBOLETH_HEADER" in config:
                eppn = flask.request.headers.get(config["SHIBBOLETH_HEADER"])

            if config.get("MOCK_AUTH") is True:
                eppn = "test"
            # if there is authorization header for oauth
            if "Authorization" in flask.request.headers:
                has_oauth(scope=scope)
                return f(*args, **kwargs)
            # if there is shibboleth session, then create user session and
            # log user in
            elif eppn:
                username = eppn.split("!")[-1]
                flask.session["username"] = username
                flask.session["provider"] = IdentityProvider.itrust
                login_user(username, flask.session["provider"])
                return f(*args, **kwargs)
            else:
                raise Unauthorized("Please login")
Exemplo n.º 17
0
        def wrapper(*args, **kwargs):
            if flask.session.get('username'):
                login_user(
                    flask.request,
                    flask.session['username'],
                    flask.session['provider'],
                )
                return f(*args, **kwargs)

            eppn = None
            enable_shib = (
                'shibboleth' in
                flask.current_app.config.get('ENABLED_IDENTITY_PROVIDERS', [])
            )
            if enable_shib and 'SHIBBOLETH_HEADER' in flask.current_app.config:
                eppn = flask.request.headers.get(
                    flask.current_app.config['SHIBBOLETH_HEADER']
                )

            if flask.current_app.config.get('MOCK_AUTH') is True:
                eppn = 'test'
            # if there is authorization header for oauth
            if 'Authorization' in flask.request.headers:
                has_oauth(scope=scope)
                return f(*args, **kwargs)
            # if there is shibboleth session, then create user session and
            # log user in
            elif eppn:
                username = eppn.split('!')[-1]
                flask.session['username'] = username
                flask.session['provider'] = IdentityProvider.itrust
                login_user(flask.request, username, flask.session['provider'])
                return f(*args, **kwargs)
            else:
                raise Unauthorized("Please login")
Exemplo n.º 18
0
def force_update_google_link(DB, username, google_email):
    """
    WARNING: This function circumvents Google Auth flow, and should only be
    used for internal testing!
    WARNING: This function assumes that a user already has a proxy group!

    Adds user's google account to proxy group and/or updates expiration for
    that google account's access.
    WARNING: This assumes that provided arguments represent valid information.
             This BLINDLY adds without verification. Do verification
             before this.
    Specifically, this ASSUMES that the proxy group provided belongs to the
    given user and that the user has ALREADY authenticated to prove the
    provided google_email is also their's.

    Args:
        DB
        username (str): Username to link with
        google_email (str): Google email to link to

    Raises:
        NotFound: Linked Google account not found
        Unauthorized: Couldn't determine user

    Returns:
        Expiration time of the newly updated google account's access
    """
    import fence.settings

    cirrus_config.update(**fence.settings.CIRRUS_CFG)

    db = SQLAlchemyDriver(DB)
    with db.session as session:
        user_account = session.query(User).filter(
            User.username == username).first()
        if user_account:
            user_id = user_account.id
            proxy_group_id = user_account.google_proxy_group_id
        else:
            raise Unauthorized("Could not determine authed user "
                               "from session. Unable to link Google account.")

        user_google_account = (session.query(UserGoogleAccount).filter(
            UserGoogleAccount.email == google_email).first())
        if not user_google_account:
            user_google_account = add_new_user_google_account(
                user_id, google_email, session)

        now = int(time.time())
        expiration = now + GOOGLE_ACCOUNT_ACCESS_EXPIRES_IN

        force_update_user_google_account_expiration(user_google_account,
                                                    proxy_group_id,
                                                    google_email, expiration,
                                                    session)

        session.commit()

        return expiration
Exemplo n.º 19
0
def get_current_user(flask_session=None):
    flask_session = flask_session or flask.session
    username = flask_session.get("username")
    if config.get("MOCK_AUTH", False) is True:
        username = "******"
    if not username:
        raise Unauthorized("User not logged in")
    return query_for_user(session=current_session, username=username)
Exemplo n.º 20
0
    def get_signed_url(self, protocol, action, expires_in):
        if not self.public and not self.check_authorization(action):
            raise Unauthorized("You don't have access permission on this file")

        if action is not None and action not in SUPPORTED_ACTIONS:
            raise NotSupported("action {} is not supported".format(action))

        return self._get_signed_url(protocol, action, expires_in)
Exemplo n.º 21
0
def get_current_user(flask_session=None):
    flask_session = flask_session or flask.session
    username = flask_session.get("username")
    if flask.current_app.config.get("MOCK_AUTH", False) is True:
        username = "******"
    if not username:
        raise Unauthorized("User not logged in")
    return (current_session.query(User).filter(
        func.lower(User.username) == username.lower()).first())
Exemplo n.º 22
0
def get_jwt_header():
    """
    Get the user's JWT from the Authorization header, or raise Unauthorized on failure.

    Return just the entire JWT as a string, without further validation or processing.
    """
    try:
        header = flask.request.headers["Authorization"]
    except KeyError:
        raise Unauthorized("missing authorization header")
    if not header.lower().startswith("bearer"):
        raise Unauthorized(
            "unexpected Authorization header format (expected `Bearer`")
    try:
        jwt = header.split(" ")[1]
    except IndexError:
        raise Unauthorized("authorization header missing token")
    return jwt
Exemplo n.º 23
0
    def get(self):
        """Handle ``GET /login/fence/login``."""
        # Check that the state passed back from IDP fence is the same as the
        # one stored previously.
        mismatched_state = (
            "state" not in flask.request.args or "state" not in flask.session
            or flask.request.args["state"] != flask.session.pop("state", ""))
        if mismatched_state and not config.get("MOCK_AUTH"):
            raise Unauthorized(
                "Login flow was interrupted (state mismatch). Please go back to the"
                " login page for the original application to continue.")
        # Get the token response and log in the user.
        redirect_uri = flask.current_app.fence_client._get_session(
        ).redirect_uri
        tokens = flask.current_app.fence_client.fetch_access_token(
            redirect_uri, **flask.request.args.to_dict())

        try:
            # For multi-Fence setup with two Fences >=5.0.0
            id_token_claims = validate_jwt(
                tokens["id_token"],
                aud=self.client.client_id,
                scope={"openid"},
                purpose="id",
                attempt_refresh=True,
            )
        except JWTError:
            # Since fenceshib cannot be updated to issue "new-style" ID tokens
            # (where scopes are in the scope claim and aud is in the aud claim),
            # allow also "old-style" Fence ID tokens.
            id_token_claims = validate_jwt(
                tokens["id_token"],
                aud="openid",
                scope=None,
                purpose="id",
                attempt_refresh=True,
            )
        username = id_token_claims["context"]["user"]["name"]
        email = id_token_claims["context"]["user"].get("email")
        login_user(
            username,
            IdentityProvider.fence,
            fence_idp=flask.session.get("fence_idp"),
            shib_idp=flask.session.get("shib_idp"),
            email=email,
        )
        self.post_login()

        if config["REGISTER_USERS_ON"]:
            if not flask.g.user.additional_info.get("registration_info"):
                return flask.redirect(config["BASE_URL"] +
                                      flask.url_for("register.register_user"))

        if "redirect" in flask.session:
            return flask.redirect(flask.session.get("redirect"))
        return flask.jsonify({"username": username})
Exemplo n.º 24
0
def logout(next_url=None):
    # Call get_current_user (but ignore the result) just to check that either
    # the user is logged in or that authorization is mocked.
    user = get_current_user()
    if not user:
        raise Unauthorized("You are not logged in")
    if flask.session['provider'] == IdentityProvider.itrust:
        next_url = flask.current_app.config['ITRUST_GLOBAL_LOGOUT'] + next_url
    flask.session.clear()
    return next_url
Exemplo n.º 25
0
def _is_valid_service_account(sa_email, google_project_id):
    """
    Validate the given registered service account and remove if invalid.

    Args:
        sa_email(str): service account email
        google_project_id(str): google project id
    """
    with GoogleCloudManager(google_project_id) as gcm:
        google_project_number = get_google_project_number(google_project_id, gcm)

    has_access = bool(google_project_number)
    if not has_access:
        # if our monitor doesn't have access at this point, just don't return any
        # information. When the project check runs, it will catch the monitor missing
        # error and add it to the removal reasons
        raise Unauthorized(
            "Google Monitoring SA doesn't have access to Google Project: {}".format(
                google_project_id
            )
        )

    try:
        sa_validity = GoogleServiceAccountValidity(
            sa_email, google_project_id, google_project_number=google_project_number
        )

        if is_google_managed_service_account(sa_email):
            sa_validity.check_validity(
                early_return=True,
                check_type=True,
                check_policy_accessible=True,
                check_external_access=False,
            )
        else:
            sa_validity.check_validity(
                early_return=True,
                check_type=True,
                check_policy_accessible=True,
                check_external_access=True,
            )

    except Exception as exc:
        # any issues, assume invalid
        # TODO not sure if this is the right way to handle this...
        logger.warning(
            "Service Account {} determined invalid due to unhandled exception: {}. "
            "Assuming service account is invalid.".format(sa_email, str(exc))
        )
        traceback.print_exc()
        sa_validity = None

    return sa_validity
Exemplo n.º 26
0
def check_authorization(action, doc):
    metadata = doc['metadata']
    if 'acls' not in metadata:
        raise Unauthorized("You don't have access permission on this file")
    set_acls = set(metadata['acls'].split(','))
    if flask.g.token is None:
        given_acls = set(filter_auth_ids(action, flask.g.user.project_access))
    else:
        given_acls = set(
            filter_auth_ids(action,
                            flask.g.token['context']['user']['projects']))
    return len(set_acls & given_acls) > 0
Exemplo n.º 27
0
def resolve_url(url, location, expires, action):
    protocol = location.scheme
    if protocol == 's3':
        aws_creds = current_app.config['AWS_CREDENTIALS']
        if 'AWS_CREDENTIALS' in current_app.config and len(aws_creds) > 0:
            buckets = flask.current_app.config['S3_BUCKETS']
            if location.netloc not in buckets.keys():
                raise Unauthorized('permission denied for bucket')
            if buckets[location.netloc] not in aws_creds:
                raise Unauthorized('permission denied for bucket')
        credential_key = buckets[location.netloc]
        url = current_app.boto.presigned_url(
            location.netloc,
            location.path.strip('/'),
            expires,
            aws_creds[credential_key],
            ACTION_DICT[protocol][action],
        )
    elif protocol not in ['http', 'https']:
        raise NotSupported('protocol {} in URL {} is not supported'.format(
            protocol, url))
    return flask.jsonify(dict(url=url))
Exemplo n.º 28
0
 def check_auth(self, provider, user):
     """
     check if the user should be authorized to storage resources
     """
     storage_access = any(
         ["read-storage" in item for item in user.project_access.values()])
     backend_access = any([
         sa.provider.name == provider for p in user.projects.values()
         for sa in p.storage_access
     ])
     if storage_access and backend_access:
         return True
     else:
         raise Unauthorized("Your are not authorized")
Exemplo n.º 29
0
def test_prompt_login_no_authn(client, oauth_client):
    """
    Test ``prompt=login`` when unable to re-AuthN.
    """
    data = {'prompt': 'login'}

    with patch('fence.blueprints.oauth2.handle_login') as handle_login_mock:
        handle_login_mock.side_effect = Unauthorized('couldnt authN')
        auth_response = oauth2.get_authorize(client, oauth_client, data=data)

        assert auth_response.status_code == 302
        assert 'Location' in auth_response.headers
        query_params = parse_qs(
            urlparse(auth_response.headers['Location']).query)
        assert 'error' in query_params
        assert query_params['error'][0] == 'access_denied'
Exemplo n.º 30
0
    def get(self):
        """
        Complete the shibboleth login.
        """
        if "SHIBBOLETH_HEADER" in config:
            eppn = flask.request.headers.get(config["SHIBBOLETH_HEADER"])

        else:
            raise InternalError("Missing shibboleth header configuration")
        username = eppn.split("!")[-1] if eppn else None
        if username:
            login_user(flask.request, username, IdentityProvider.itrust)
            if flask.session.get("redirect"):
                return flask.redirect(flask.session.get("redirect"))
            return "logged in"
        else:
            raise Unauthorized("Please login")