def file_update_bucket(context, request): new_bucket = request.json_body.get('new_bucket') if not new_bucket: raise ValidationFailure('body', ['bucket'], 'New bucket not specified') force = asbool(request.params.get('force')) known_buckets = [ request.registry.settings['file_upload_bucket'], request.registry.settings['pds_public_bucket'], request.registry.settings['pds_private_bucket'], ] # Try to validate input to a known bucket. if new_bucket not in known_buckets and not force: raise ValidationFailure('body', ['bucket'], 'Unknown bucket and force not specified') current_bucket = context._get_external_sheet().get('bucket') # Don't bother setting if already the same. if current_bucket != new_bucket: request.registry.notify(BeforeModified(context, request)) context._set_external_sheet({'bucket': new_bucket}) request.registry.notify(AfterModified(context, request)) return { 'status': 'success', '@type': ['result'], 'old_bucket': current_bucket, 'new_bucket': new_bucket }
def impersonate_user(context, request): """As an admin, impersonate a different user.""" userid = request.validated['userid'] users = request.registry[COLLECTIONS]['user'] try: user = users[userid] except KeyError: raise ValidationFailure('body', ['userid'], 'User not found.') if user.properties.get('status') != 'current': raise ValidationFailure('body', ['userid'], 'User is not enabled.') user_properties = get_basic_properties_for_user(request, userid) # pop off impersonate user action if not admin user_properties['user_actions'] = [ x for x in user_properties['user_actions'] if (x['id'] and x['id'] != 'impersonate') ] # make a key registry = request.registry auth0_client = registry.settings.get('auth0.client') auth0_secret = registry.settings.get('auth0.secret') if not (auth0_client and auth0_secret): raise HTTPForbidden(title="No keys to impersonate user") jwt_contents = { 'email': userid, 'email_verified': True, 'aud': auth0_client, } id_token = jwt.encode(jwt_contents, auth0_secret, algorithm=JWT_ENCODING_ALGORITHM) is_https = request.scheme == "https" request.response.set_cookie("jwtToken", value=id_token.decode('utf-8'), domain=request.domain, path="/", httponly=True, samesite="strict", overwrite=True, secure=is_https) return user_properties
def set_status(self, new_status, request, parent=True): root = find_root(self) schema = self.type_info.schema properties = self.upgrade_properties() item_id = '{}/'.format(resource_path(self)) current_status = properties.get('status') if not current_status: raise ValidationFailure('body', ['status'], 'No property status') if not self._valid_status(new_status, schema, parent): return False force_transition = asbool(request.params.get('force_transition')) if not self._valid_transition(current_status, new_status, parent, force_transition): return False force_audit = asbool(request.params.get('force_audit')) self._block_on_audits(item_id, force_audit, request, parent, new_status) update = asbool(request.params.get('update')) self._update_status(new_status, current_status, properties, schema, request, item_id, update) request._set_status_considered_paths.add( (item_id, current_status, new_status)) logging.warn('Considering {} from status {} to status {}'.format( item_id, current_status, new_status)) block_children = self._calculate_block_children( request, force_transition) child_paths = self._get_child_paths(current_status, new_status, block_children) embedded_properties = request.embed(item_id, '@@embedded') related_objects = self._get_related_object(child_paths, embedded_properties, request) self._set_status_on_related_objects(new_status, related_objects, root, request) return True
def _valid_status(new_status, schema, parent): valid_statuses = schema.get('properties', {}).get('status', {}).get('enum', []) if new_status not in valid_statuses: if parent: msg = '{} not one of {}'.format(new_status, valid_statuses) raise ValidationFailure('body', ['status'], msg) else: return False return True
def _valid_transition(current_status, new_status, parent, force_transition): if current_status not in STATUS_TRANSITION_TABLE[ new_status] and not force_transition: if parent: msg = 'Status transition {} to {} not allowed'.format( current_status, new_status) raise ValidationFailure('body', ['status'], msg) else: return False return True
def item_set_status(context, request): new_status = request.json_body.get('status') if not new_status: raise ValidationFailure('body', ['status'], 'Status not specified') context.set_status(new_status, request) # Returns changed and considered lists of tuples: (item, current_status, new_status). return { 'status': 'success', '@type': ['result'], 'changed': request._set_status_changed_paths, 'considered': request._set_status_considered_paths }
def impersonate_user(request): """As an admin, impersonate a different user.""" userid = request.validated['userid'] users = request.registry[COLLECTIONS]['user'] try: user = users[userid] except KeyError: raise ValidationFailure('body', ['userid'], 'User not found.') if user.properties.get('status') != 'current': raise ValidationFailure('body', ['userid'], 'User is not enabled.') request.session.invalidate() request.session.get_csrf_token() request.response.headerlist.extend(remember(request, 'mailto.' + userid)) user_properties = request.embed('/session-properties', as_user=userid) if 'auth.userid' in request.session: user_properties['auth.userid'] = request.session['auth.userid'] return user_properties
def disease_term_name(self, request, registry, disease_term_id=None): if disease_term_id is not None: term_name = list() for term_id in disease_term_id: if term_id in registry['ontology']: term_name.append(registry['ontology'][term_id]['name']) else: msg = 'Disease term ID {} is not a valid ID'.format( term_id ) raise ValidationFailure('body', ['disease_term_id'], msg) return term_name
def impersonate_user(request): """As an admin, impersonate a different user.""" user = request.validated['user'] try: user = find_resource(request.root, user) except KeyError: raise ValidationFailure('body', ['user'], 'User not found.') if user.item_type != 'user': raise ValidationFailure('body', ['user'], 'User not found.') if user.properties.get('status') != 'current': raise ValidationFailure('body', ['user'], 'User is not enabled.') request.session.invalidate() request.session.get_csrf_token() request.response.headerlist.extend( remember(request, 'mailto.%s' % user.uuid)) user_properties = request.embed( '/session-properties', as_user=str(user.uuid)) if 'auth.userid' in request.session: user_properties['auth.userid'] = request.session['auth.userid'] return user_properties
def _block_on_audits(item_id, force_audit, request, parent, new_status): if new_status not in ['released', 'submitted']: return if not parent or force_audit: return audits = request.embed(item_id, '@@audit') errors = audits.get('audit', {}).get('ERROR', []) not_compliants = audits.get('audit', {}).get('NOT_COMPLIANT', []) details = { detail.get('detail') for detail in itertools.chain(errors, not_compliants) if detail.get('detail') } if audits and any([errors, not_compliants]): raise ValidationFailure( 'body', ['status'], 'Audit on parent object. Must use ?force_audit=true to change status. {}' .format(list(details)))
def organism(self, properties=None, return_uuid=False): if properties is None: properties = self.upgrade_properties() root = find_root(self) if 'target_organism' in properties: organism_uuid = properties['target_organism'] else: organism_uuids = set( root.get_by_uuid(gene).upgrade_properties()['organism'] for gene in properties['genes']) if len(organism_uuids) != 1: msg = 'Target genes are from different organisms: {}'.format( repr(organism_uuids)) raise ValidationFailure('body', ['genes'], msg) organism_uuid = next(iter(organism_uuids)) if return_uuid: return organism_uuid return resource_path(root.get_by_uuid(organism_uuid), '')
def create_and_commit(cls, request, properties, clean_headers=False): """ Create a TrackingItem with a given request and properties, committing it directly to the DB. This works by manually committing the transaction, which may cause issues if this function is called as part of another POST. For this reason, this function should be used to track GET requests -- otherwise, use the standard POST method. If validator issues are hit, will not create the item but log to error Args: request: current request object properties (dict): TrackingItem properties to post clean_headers(bool): If True, remove 'Location' header created by POST Returns: dict response from snovault.crud_views.collection_add Raises: ValidationFailure if TrackingItem cannot be validated """ collection = request.registry[COLLECTIONS]['TrackingItem'] # set remote_user to standarize permissions prior_remote = request.remote_user request.remote_user = '******' # remove any missing attributes from DownloadTracking properties['download_tracking'] = { k: v for k, v in properties.get('download_tracking', {}).items() if v is not None } validate_request(collection.type_info.schema, request, properties) if request.errors: # added from validate_request request.remote_user = prior_remote raise ValidationFailure('body', 'TrackingItem: create_and_commit', 'Cannot validate request') ti_res = sno_collection_add(collection, request, False) # render=False transaction.get().commit() if clean_headers and 'Location' in request.response.headers: del request.response.headers['Location'] request.remote_user = prior_remote return ti_res
def _validate_set_status_patch(request, schema, new_properties, current_properties): validate_request(schema, request, new_properties, current_properties) if any(request.errors): raise ValidationFailure(request.errors)
def create_unauthorized_user(context, request): """ Endpoint to create an unauthorized user, which will have no institution/project. Requires a reCAPTCHA response, which is propogated from the front end registration form. This is so the endpoint cannot be abused. Given a user properties in the request body, will validate those and also validate the reCAPTCHA response using the reCAPTCHA server. If all checks are succesful, POST a new user Args: request: Request object Returns: dictionary User creation response from collection_add Raises: LoginDenied, HTTPForbidden, or ValidationFailure """ recaptcha_resp = request.json.get('g-recaptcha-response') if not recaptcha_resp: raise LoginDenied() email = request._auth0_authenticated # equal to: jwt_info['email'].lower() user_props = request.json user_props_email = user_props.get("email", "<no e-mail supplied>").lower() if user_props_email != email: raise HTTPUnauthorized( title= "Provided email {} not validated with Auth0. Try logging in again." .format(user_props_email), headers={ 'WWW-Authenticate': "Bearer realm=\"{}\"; Basic realm=\"{}\"".format( request.domain, request.domain) }) del user_props['g-recaptcha-response'] user_props['was_unauthorized'] = True user_props['email'] = user_props_email # lowercased user_coll = request.registry[COLLECTIONS]['User'] request.remote_user = '******' # permission = restricted_fields # validate the User json validate_request(user_coll.type_info.schema, request, user_props) if request.errors: raise ValidationFailure('body', 'create_unauthorized_user', 'Cannot validate request') # validate recaptcha_resp # https://developers.google.com/recaptcha/docs/verify recap_url = 'https://www.google.com/recaptcha/api/siteverify' recap_values = { 'secret': request.registry.settings['g.recaptcha.secret'], 'response': recaptcha_resp } data = urlencode(recap_values).encode() headers = { "Content-Type": "application/x-www-form-urlencoded; charset=utf-8" } recap_res = requests.get(recap_url, params=data, headers=headers).json() if recap_res['success']: sno_res = sno_collection_add(user_coll, request, False) # POST User if sno_res.get('status') == 'success': return sno_res else: raise HTTPForbidden( title="Could not create user. Try logging in again.") else: # error with re-captcha raise HTTPUnauthorized( title="Invalid reCAPTCHA. Try logging in again.", headers={ 'WWW-Authenticate': "Bearer realm=\"{}\"; Basic realm=\"{}\"".format( request.domain, request.domain) })