Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat/arborist #608

Merged
merged 71 commits into from
May 20, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
e6b37d1
feat(user-rbac-info): add user policies in info
Apr 1, 2019
7fa5a3f
feat(sleek-jwts): remove policies
Apr 2, 2019
6fca959
feat(sleek-jwts): add migration to drop policies
Apr 2, 2019
a010e9e
Merge pull request #604 from uc-cdis/feat/user-rbac-info
rudyardrichter Apr 3, 2019
45f9b95
feat(download-auth): use arborist for data download check
Apr 3, 2019
b3734dc
feat(download-auth): userdatamodel pin
Apr 3, 2019
bbbfdb1
feat(sleek-jwts): remove policy from models
Apr 3, 2019
d21254a
feat(sleek-jwts): remove unused policies endpoints
Apr 5, 2019
644910c
feat(download-auth): userdatamodel pin
Apr 3, 2019
542db3a
Merge branch 'feat/sleek-jwts' into feat/download-auth
Apr 5, 2019
c96f8e3
feat(download-auth): remove unused imports
Apr 5, 2019
aafa11a
feat(download-auth): fix drop commands
Apr 5, 2019
c7424ab
feat(download-auth): remove more policies references
Apr 5, 2019
8e656d6
feat(download-auth): add test for download endpoint using rbac
Apr 11, 2019
b26eb62
feat(download-auth): correct methods
Apr 12, 2019
3c5158c
feat(download-auth): run formatter
Apr 12, 2019
fe48f1c
feat(arborist-sync): update sync for new RBAC
Apr 9, 2019
5c431a0
feat(arborist-sync): CRFs, add group create
Apr 25, 2019
2830ca0
Update client.py
Avantol13 Apr 25, 2019
8e2ea49
feat(arborist-sync): add arborist client checks
Apr 25, 2019
129934b
feat(download-auth): add new userdatamodel pin
Apr 26, 2019
6dc8851
feat(download-auth): add migration
Apr 26, 2019
8223657
feat(download-auth): fix param in arborist request
Apr 26, 2019
3d7b717
chore(deps): point back to branch for userdatamodel
Avantol13 May 1, 2019
3ff6fee
Merge branch 'feat/arborist' into feat/arborist-sync
Avantol13 May 6, 2019
2fb58c4
feat(arborist-client-create)
fantix Apr 8, 2019
6df6af0
add client-modify --policies
fantix May 1, 2019
fe49500
update_client: update, or create if missing
fantix May 3, 2019
4ab365e
fix Black
fantix May 6, 2019
6edb42b
Merge pull request #619 from uc-cdis/feat/arborist-client-create-br
fantix May 6, 2019
ba772f2
Merge branch 'master' into feat/arborist
Avantol13 May 6, 2019
b9ec596
feat(arborist-sync): fix user handling in sync
May 6, 2019
16ea297
fix(requirements): pin userdatamodel to remove policy branch commit
Avantol13 May 6, 2019
f6dcf97
feat(arborist-sync): fix user handling in sync
May 6, 2019
8fc3546
Merge branch 'feat/arborist' into feat/arborist-sync
Avantol13 May 6, 2019
43dc1d4
Merge branch 'feat/arborist-sync' of github.com:uc-cdis/fence into fe…
Avantol13 May 6, 2019
ecf0075
feat(download-auth): add migration
Apr 26, 2019
d294f60
fix(imports): add missing import for syncing
Avantol13 May 6, 2019
c7244de
fix(fence-create): correctly pass in ArboristClient
Avantol13 May 6, 2019
59396f0
feat(arborist-sync): overwrite policy if necessary
May 6, 2019
cce4ea9
feat(arborist-sync): clean up logging
May 6, 2019
35bc481
Merge branch 'feat/arborist-sync' into feat/download-auth
May 7, 2019
135c17c
feat(download-auth): tmp userdatamodel pin
May 7, 2019
da19122
feat(arborist-sync): change to * permission
May 7, 2019
fafe1e2
feat(arborist-sync): change to * permission
May 7, 2019
b8cc27d
feat(arborist-sync): support user policy list
May 7, 2019
c73f70c
feat(arborist-sync): leave warning if group exists
May 7, 2019
3ecb1ee
feat(arborist-sync): support user policy list
May 7, 2019
81b4e5b
feat(arborist): user policies -> resources list
May 9, 2019
6e7ee91
feat(sleek-jwts): remove policy from models
Apr 3, 2019
6877e6f
Merge pull request #606 from uc-cdis/feat/download-auth
rudyardrichter May 9, 2019
166c1c3
feat(arborist): handle error for resource list
May 9, 2019
925518f
feat(signed-url): check authz field instead of rbac for new indexd ch…
Avantol13 May 9, 2019
baa0860
Merge remote-tracking branch 'origin/feat/arborist' into feat/arborist
Avantol13 May 9, 2019
91a3641
fix(tests): rbac to authz indexd field
Avantol13 May 9, 2019
3d78781
feat(arborist-sync): run formatter
May 10, 2019
a1ce22f
Merge branch 'feat/arborist' into feat/arborist-sync
May 10, 2019
ed45d84
Merge pull request #613 from uc-cdis/feat/arborist-sync
rudyardrichter May 10, 2019
ed7e556
feat(arborist): overwrite groups during sync
May 10, 2019
27e65f5
feat(arborist): remove rbac blueprint from docs
May 13, 2019
624f63f
feat(builtin-groups): support builtin groups in user.yaml
May 14, 2019
45aff9c
feat(builtin-groups): fix typo
rudyardrichter May 14, 2019
edeba8f
feat(builtin-groups): fix error handle
May 14, 2019
fdbb000
feat(arborist): fix status code for user create
May 14, 2019
578274a
feat(builtin-groups): run formatter
May 14, 2019
bcd0259
Merge branch 'master' into feat/arborist
Avantol13 May 14, 2019
098f610
feat(arborist): run formatter
May 14, 2019
1173697
Merge pull request #625 from uc-cdis/feat/builtin-groups
rudyardrichter May 14, 2019
e9a6eea
Merge branch 'master' into feat/arborist
Avantol13 May 16, 2019
1e90cb7
feat(arborist): overwrite policies in user.yaml
May 16, 2019
14bb60b
chore(userdatamodel): pin to updated version from pypi
Avantol13 May 17, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 28 additions & 1 deletion bin/fence-create
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import os
import sys
import logging

from cdislogging import get_logger

from fence.jwt import keys
from fence.config import config
from fence.scripting.fence_create import (
Expand All @@ -29,6 +31,7 @@ from fence.scripting.fence_create import (
verify_user_registration,
force_update_google_link,
)
from fence.rbac.client import ArboristClient


def str2bool(v):
Expand All @@ -51,6 +54,11 @@ def parse_arguments():
action="store_true",
default=False,
)
parser.add_argument(
"--arborist",
help="the base URL for the arborist service to sync to",
default=None,
)

subparsers = parser.add_subparsers(title="action", dest="action")

Expand Down Expand Up @@ -88,6 +96,9 @@ def parse_arguments():
action="store_true",
default=False,
)
client_create.add_argument(
"--policies", help="which RBAC policies are granted to this client", nargs="*"
)

client_modify = subparsers.add_parser("client-modify")
client_modify.add_argument("--client", required=True)
Expand All @@ -109,6 +120,12 @@ def parse_arguments():
client_modify.add_argument(
"--delete-urls", help="delete all urls", action="store_true", default=False
)
client_modify.add_argument(
"--policies",
help="which RBAC policies are granted to this client; if given, "
"previous policies will be revoked",
nargs="*",
)

client_list = subparsers.add_parser("client-list")

Expand Down Expand Up @@ -335,6 +352,12 @@ def main():
STORAGE_CREDENTIALS = os.environ.get("STORAGE_CREDENTIALS") or config.get(
"STORAGE_CREDENTIALS"
)
arborist = None
if args.arborist:
arborist = ArboristClient(
arborist_base_url=args.arborist,
logger=get_logger("user_syncer.arborist_client"),
)

if args.action == "create":
yaml_input = args.__dict__["yaml-input"]
Expand All @@ -349,6 +372,8 @@ def main():
auto_approve=args.auto_approve,
grant_types=args.grant_types,
confidential=confidential,
arborist=arborist,
policies=args.policies,
)
elif args.action == "client-modify":
modify_client_action(
Expand All @@ -360,6 +385,8 @@ def main():
description=args.description,
set_auto_approve=args.set_auto_approve,
unset_auto_approve=args.unset_auto_approve,
arborist=arborist,
policies=args.policies,
)
elif args.action == "client-delete":
delete_client_action(DB, args.client)
Expand All @@ -380,7 +407,7 @@ def main():
is_sync_from_dbgap_server=str2bool(args.sync_from_dbgap),
sync_from_local_csv_dir=args.csv_dir,
sync_from_local_yaml_file=args.yaml,
arborist=args.arborist,
arborist=arborist,
)
elif args.action == "google-manage-keys":
remove_expired_google_service_account_keys(DB)
Expand Down
10 changes: 4 additions & 6 deletions fence/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
from fence.rbac.client import ArboristClient
from fence.resources.aws.boto_manager import BotoManager
from fence.resources.openid.google_oauth2 import GoogleOauth2Client as GoogleClient
from fence.resources.openid.microsoft_oauth2 import MicrosoftOauth2Client as MicrosoftClient
from fence.resources.openid.microsoft_oauth2 import (
MicrosoftOauth2Client as MicrosoftClient
)
from fence.resources.openid.orcid_oauth2 import OrcidOauth2Client as ORCIDClient
from fence.resources.storage import StorageManager
from fence.resources.user.user_session import UserSessionInterface
Expand All @@ -27,7 +29,6 @@
import fence.blueprints.data
import fence.blueprints.login
import fence.blueprints.oauth2
import fence.blueprints.rbac
import fence.blueprints.misc
import fence.blueprints.storage_creds
import fence.blueprints.user
Expand All @@ -39,7 +40,7 @@

# Can't read config yet. Just set to debug for now, else no handlers.
# Later, in app_config(), will actually set level based on config
logger = get_logger(__name__, log_level='debug')
logger = get_logger(__name__, log_level="debug")

app = flask.Flask(__name__)
CORS(app=app, headers=["content-type", "accept"], expose_headers="*")
Expand Down Expand Up @@ -102,9 +103,6 @@ def app_register_blueprints(app):
google_blueprint = fence.blueprints.google.make_google_blueprint()
app.register_blueprint(google_blueprint, url_prefix="/google")

if config.get("ARBORIST"):
app.register_blueprint(fence.blueprints.rbac.blueprint, url_prefix="/rbac")

fence.blueprints.misc.register_misc(app)

@app.route("/")
Expand Down
19 changes: 19 additions & 0 deletions fence/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,25 @@
logger = get_logger(__name__)


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


def build_redirect_url(hostname, path):
"""
Compute a redirect given a hostname and next path where
Expand Down
6 changes: 1 addition & 5 deletions fence/blueprints/data/blueprint.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,7 @@
IndexedFile,
get_signed_url_for_file,
)
from fence.errors import (
Forbidden,
InternalError,
UserError,
)
from fence.errors import Forbidden, InternalError, UserError
from fence.utils import is_valid_expiration
from fence.rbac import check_arborist_auth

Expand Down
37 changes: 33 additions & 4 deletions fence/blueprints/data/indexd.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
import requests

from fence.auth import (
get_jwt,
has_oauth,
current_token,
login_required,
set_current_token,
Expand Down Expand Up @@ -162,7 +164,7 @@ def init_multipart_upload(key, expires_in=None):

Args:
key(str): object key

Returns:
uploadId(str)
"""
Expand Down Expand Up @@ -209,7 +211,7 @@ def generate_aws_presigned_url_for_part(key, uploadId, partNumber, expires_in):
key(str): object key of `guid/filename`
uploadID(str): uploadId of the current upload.
partNumber(int): the part number

Returns:
presigned_url(str)
"""
Expand Down Expand Up @@ -299,7 +301,9 @@ def get_signed_url(self, protocol, action, expires_in, force_signed_url=True):
# 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")
raise Unauthorized(
"You don't have access permission on this file: {}".format(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)
Expand Down Expand Up @@ -343,6 +347,18 @@ def set_acls(self):
else:
raise Unauthorized("This file is not accessible")

def check_authz(self, action):
if not self.index_document.get("authz"):
raise ValueError("index record missing `authz`")

request = {"user": {"token": get_jwt()}, "requests": []}
for resource in self.index_document["authz"]:
request["requests"].append(
{"resource": resource, "action": {"service": "fence", "method": action}}
)

return flask.current_app.arborist.auth_request(request)

@cached_property
def metadata(self):
return self.index_document.get("metadata", {})
Expand All @@ -364,6 +380,19 @@ def check_authorization(self, action):
username = flask.g.user.username
return self.index_document.get("uploader") == username

try:
action_to_method = {"upload": "write_storage", "download": "read_storage"}
method = action_to_method[action]
# action should be upload or download
# return bool for authorization
return self.check_authz(method)
except ValueError:
# this is ok; we'll default to ACL field (previous behavior)
# may want to deprecate in future
logger.info(
"Couldn't find `authz` field on indexd record, falling back to `acl`."
)

if flask.g.token is None:
given_acls = set(filter_auth_ids(action, flask.g.user.project_access))
else:
Expand Down Expand Up @@ -654,7 +683,7 @@ def generate_presigne_url_for_part_upload(self, uploadId, partNumber, expires_in
def complete_multipart_upload(self, uploadId, parts, expires_in):
"""
Complete multipart upload.

Args:
uploadId(str): upload id of the current upload
parts(list(set)): List of part infos
Expand Down
Loading