def get(self): """ Authenticate a Rucio account temporarily via a GSS token. .. :quickref: GSS; Authenticate with GSS token :reqheader Rucio-VO: VO name as a string (Multi-VO only). :reqheader Rucio-Account: Account identifier as a string. :reqheader Rucio-AppID: Application identifier as a string. :reqheader SavedCredentials: Apache mod_auth_kerb SavedCredentials. :resheader Access-Control-Allow-Origin: :resheader Access-Control-Allow-Headers: :resheader Access-Control-Allow-Methods: :resheader Access-Control-Allow-Credentials: :resheader Access-Control-Expose-Headers: :resheader X-Rucio-Auth-Token: The authentication token :status 200: Successfully authenticated :status 404: Invalid credentials """ headers = self.get_headers() headers['Content-Type'] = 'application/octet-stream' headers[ 'Cache-Control'] = 'no-cache, no-store, max-age=0, must-revalidate' headers.add('Cache-Control', 'post-check=0, pre-check=0') headers['Pragma'] = 'no-cache' vo = extract_vo(request.headers) account = request.headers.get('X-Rucio-Account', default=None) gsscred = request.environ.get('REMOTE_USER') appid = request.headers.get('X-Rucio-AppID', default='unknown') ip = request.headers.get('X-Forwarded-For', default=request.remote_addr) try: result = get_auth_token_gss(account, gsscred, appid, ip, vo=vo) except AccessDenied: return generate_http_error_flask( status_code=401, exc=CannotAuthenticate.__name__, exc_msg= f'Cannot authenticate to account {account} with given credentials', headers=headers) if result is None: return generate_http_error_flask( status_code=401, exc=CannotAuthenticate.__name__, exc_msg= f'Cannot authenticate to account {account} with given credentials', headers=headers) headers['X-Rucio-Auth-Token'] = result['token'] headers['X-Rucio-Auth-Token-Expires'] = date_to_str( result['expires_at']) return '', 200, headers
def get(self): """ Request a challenge token for SSH authentication .. :quickref: SSH; Request SSH Challenge Token :reqheader Rucio-VO: VO name as a string (Multi-VO only). :reqheader Rucio-Account: Account identifier as a string. :reqheader Rucio-AppID: Application identifier as a string. :resheader Access-Control-Allow-Origin: :resheader Access-Control-Allow-Headers: :resheader Access-Control-Allow-Methods: :resheader Access-Control-Allow-Credentials: :resheader Access-Control-Expose-Headers: :resheader X-Rucio-SSH-Challenge-Token: The SSH challenge token :resheader X-Rucio-SSH-Challenge-Token-Expires: The expiry time of the token :status 200: Successfully authenticated :status 404: Invalid credentials """ headers = self.get_headers() headers['Content-Type'] = 'application/octet-stream' headers[ 'Cache-Control'] = 'no-cache, no-store, max-age=0, must-revalidate' headers.add('Cache-Control', 'post-check=0, pre-check=0') headers['Pragma'] = 'no-cache' vo = extract_vo(request.headers) account = request.headers.get('X-Rucio-Account', default=None) appid = request.headers.get('X-Rucio-AppID', default='unknown') ip = request.headers.get('X-Forwarded-For', default=request.remote_addr) result = get_ssh_challenge_token(account, appid, ip, vo=vo) if not result: return generate_http_error_flask( status_code=401, exc=CannotAuthenticate.__name__, exc_msg=f'Cannot generate challenge for account {account}', headers=headers) headers['X-Rucio-SSH-Challenge-Token'] = result['token'] headers['X-Rucio-SSH-Challenge-Token-Expires'] = date_to_str( result['expires_at']) return '', 200, headers
def get(self): """ .. :quickref: OIDC; :status 200: OK :status 401: Unauthorized :resheader X-Rucio-Auth-Token: The authentication token """ headers = self.get_headers() headers.set('Content-Type', 'application/octet-stream') headers.set('Cache-Control', 'no-cache, no-store, max-age=0, must-revalidate') headers.add('Cache-Control', 'post-check=0, pre-check=0') headers.set('Pragma', 'no-cache') vo = extract_vo(request.headers) account = request.headers.get('X-Rucio-Account', default=None) token = request.headers.get('X-Rucio-Auth-Token', default=None) if token is None or account is None: return generate_http_error_flask(401, CannotAuthorize.__name__, 'Cannot authorize token request.', headers=headers) try: result = refresh_cli_auth_token(token, account, vo=vo) except AccessDenied: return generate_http_error_flask(401, CannotAuthorize.__name__, 'Cannot authorize token request.', headers=headers) if result is not None and len(result) > 1: headers.set('X-Rucio-Auth-Token', str(result[0])) headers.set('X-Rucio-Auth-Token-Expires', str(result[1])) else: headers.set('X-Rucio-Auth-Token', '') headers.set('X-Rucio-Auth-Token-Expires', '') return '', 200, headers
def get(self, scope_name): """ --- summary: Metalink redirect description: Get Metalink redirect. tags: - Redirect parameters: - name: scope_name in: path description: The data identifier (scope)/(name). schema: type: string style: simple - name: ip in: query description: The client ip. schema: type: string style: simple required: false - name: fqdn in: query schema: type: string style: simple required: false - name: site in: query schema: type: string style: simple required: false - name: schemes in: query schema: type: array style: simple required: false - name: select in: query schema: type: string style: simple required: false - name: sort in: query schema: type: string style: simple required: false responses: 200: description: OK content: application/metalink4+xml: schema: description: The metalink file. type: string 401: description: Invalid Auth Token 404: description: Rse or did not found 406: description: Not acceptable """ headers = self.get_headers() try: scope, name = parse_scope_name(scope_name, extract_vo(request.headers)) except ValueError as error: return generate_http_error_flask(400, error, headers=headers) # set the correct client IP client_ip = request.headers.get('X-Forwarded-For', default=request.remote_addr) client_location = { 'ip': request.args.get('ip', default=client_ip), 'fqdn': request.args.get('fqdn', default=None), 'site': request.args.get('site', default=None), } dids = [{'scope': scope, 'name': name}] schemes = request.args.getlist('schemes') or [ 'http', 'https', 'root', 'gsiftp', 'srm', 'davs' ] sortby = request.args.get('select', default=None) sortby = request.args.get('sort', default=sortby) # get vo if given vo = extract_vo(request.headers) try: replicas_iter = list_replicas(dids=dids, schemes=schemes, client_location=client_location, vo=vo) try: first = next(replicas_iter) except StopIteration: return 'no redirection possible - cannot find the DID', 404 def generate(): # first, set the appropriate content type, and stream the header yield '<?xml version="1.0" encoding="UTF-8"?>\n<metalink xmlns="urn:ietf:params:xml:ns:metalink">\n' # iteratively stream the XML per file for rfile in itertools.chain((first, ), replicas_iter): replicas = [] dictreplica = {} for rse in rfile['rses']: for replica in rfile['rses'][rse]: replicas.append(replica) dictreplica[replica] = rse # stream metadata yield ' <file name="' + rfile['name'] + '">\n' yield ' <identity>' + rfile['scope'] + ':' + rfile[ 'name'] + '</identity>\n' if rfile['adler32'] is not None: yield ' <hash type="adler32">' + rfile[ 'adler32'] + '</hash>\n' if rfile['md5'] is not None: yield ' <hash type="md5">' + rfile['md5'] + '</hash>\n' yield ' <size>' + str(rfile['bytes']) + '</size>\n' yield f' <glfn name="/atlas/rucio/{rfile["scope"]}:{rfile["name"]}">' yield '</glfn>\n' replicas = sort_replicas(dictreplica, client_location, selection=sortby) # stream URLs idx = 1 for replica in replicas: yield ' <url location="' + str( dictreplica[replica]) + '" priority="' + str( idx) + '">' + replica + '</url>\n' idx += 1 yield ' </file>\n' # don't forget to send the metalink footer yield '</metalink>\n' return try_stream(generate(), content_type='application/metalink4+xml') except (DataIdentifierNotFound, ReplicaNotFound) as error: return generate_http_error_flask(404, error, headers=headers)
def get(self, scope_name): """ --- summary: Header redirect description: Get the header redirect. tags: - Redirect parameters: - name: scope_name in: path description: The data identifier (scope)/(name). schema: type: string style: simple - name: ip in: query description: The client ip. schema: type: string style: simple required: false - name: fqdn in: query schema: type: string style: simple required: false - name: site in: query schema: type: string style: simple required: false - name: schemes in: query schema: type: array style: simple required: false - name: select in: query schema: type: string style: simple required: false - name: sort in: query schema: type: string style: simple required: false - name: rse in: query schema: type: string style: simple required: false responses: 303: description: OK content: application/json: schema: description: The redirect url. type: string 401: description: Invalid Auth Token 404: description: Rse or did not found """ headers = self.get_headers() try: scope, name = parse_scope_name(scope_name, extract_vo(request.headers)) except ValueError as error: return generate_http_error_flask(400, error, headers=headers) try: client_ip = request.headers.get('X-Forwarded-For', default=request.remote_addr) client_location = { 'ip': request.args.get('ip', default=client_ip), 'fqdn': request.args.get('fqdn', default=None), 'site': request.args.get('site', default=None), } # use the default HTTP protocols if no scheme is given schemes = request.args.getlist('schemes') or [ 'davs', 'https', 's3' ] sortby = request.args.get('select', default='random') sortby = request.args.get('sort', default=sortby) rse = request.args.get('rse', default=None) site = request.args.get('site', default=None) # correctly forward the schemes and select to potential metalink followups cleaned_url = request.environ.get('REQUEST_URI').split('?')[0] headers.set( 'Link', f'<{cleaned_url}/metalink?schemes={",".join(schemes)}&select={sortby}>; rel=describedby; type="application/metalink+xml"' ) # get vo if given vo = extract_vo(request.headers) replicas = list( list_replicas(dids=[{ 'scope': scope, 'name': name, 'type': 'FILE' }], schemes=schemes, client_location=client_location, vo=vo)) selected_url = None for r in replicas: if r['rses']: dictreplica = {} if rse: if rse in r['rses'] and r['rses'][rse]: selected_url = r['rses'][rse][0] else: return 'no redirection possible - no valid RSE for HTTP redirection found', 404, headers else: for rep in r['rses']: for replica in r['rses'][rep]: # since this is HTTP-only redirection, and to ensure compatibility with as many http clients as possible # forcibly replacement davs and s3 URLs to https replica = replica.replace( 'davs://', 'https://').replace('s3://', 'https://') dictreplica[replica] = rep if not dictreplica: return 'no redirection possible - no valid RSE for HTTP redirection found', 404, headers elif site: rep = site_selector(dictreplica, site, vo) if rep: selected_url = rep[0] else: return 'no redirection possible - no valid RSE for HTTP redirection found', 404, headers else: rep = sort_replicas(dictreplica, client_location, selection=sortby) selected_url = rep[0] if selected_url: response = redirect(selected_url, code=303) response.headers.extend(headers) return response return 'no redirection possible - file does not exist', 404, headers except ReplicaNotFound as error: return generate_http_error_flask(404, error, headers=headers)
def get(self): """ --- summary: Sign URL description: Sign a url for a limited lifetime for a particular srevice. tags: - Credentials parameters: - name: rse in: query description: The RSE to authenticate against. schema: type: string required: true - name: lifetime in: query description: The lifetime, default 600s. schema: type: string required: false - name: svc in: query description: The service, default gcs. schema: type: string required: false - name: op in: query description: The operation. schema: type: string required: false - name: url in: query description: The Url of the authentification. schema: type: string required: true requestBody: content: 'application/octet-stream': schema: type: object properties: X-Rucio-Account: description: Account identifier. type: string X-Rucio-VO: description: VO name (Multi-VO only). type: string X-Rucio-AppID: description: Application identifier. type: string responses: 200: description: OK content: application/json: schema: type: array items: type: object description: An account attribute. properties: key: description: The key of the account attribute. type: string value: description: The value of the account attribute. type: string 401: description: Invalid Auth Token 400: description: bad request, no rse or url found. 406: description: Not acceptable. """ headers = self.get_headers() vo = extract_vo(request.headers) account = request.headers.get('X-Rucio-Account', default=None) appid = request.headers.get('X-Rucio-AppID', default='unknown') ip = request.headers.get('X-Forwarded-For', default=request.remote_addr) if 'rse' not in request.args: return generate_http_error_flask(400, ValueError.__name__, 'Parameter "rse" not found', headers=headers) rse = request.args.get('rse') lifetime = request.args.get('lifetime', type=int, default=600) service = request.args.get('svc', default='gcs') operation = request.args.get('op', default='read') if 'url' not in request.args: return generate_http_error_flask(400, ValueError.__name__, 'Parameter "url" not found', headers=headers) url = request.args.get('url') if service not in ['gcs', 's3', 'swift']: return generate_http_error_flask( 400, ValueError.__name__, 'Parameter "svc" must be either empty(=gcs), gcs, s3 or swift', headers=headers) if operation not in ['read', 'write', 'delete']: return generate_http_error_flask( 400, ValueError.__name__, 'Parameter "op" must be either empty(=read), read, write, or delete.', headers=headers) result = get_signed_url(account, appid, ip, rse=rse, service=service, operation=operation, url=url, lifetime=lifetime, vo=vo) if not result: return generate_http_error_flask( 401, CannotAuthenticate.__name__, f'Cannot generate signed URL for account {account}', headers=headers) return str(result), 200, headers
def get(self): """ .. :quickref: SAML; :status 200: OK :status 401: Unauthorized :reqheader Rucio-VO: VO name as a string (Multi-VO only) :reqheader Rucio-Account: Account identifier as a string. :reqheader Rucio-Username: Username as a string. :reqheader Rucio-Password: Password as a string. :reqheader Rucio-AppID: Application identifier as a string. :resheader X-Rucio-SAML-Auth-URL: as a variable-length string header. """ headers = self.get_headers() headers.set('Content-Type', 'application/octet-stream') headers.set('Cache-Control', 'no-cache, no-store, max-age=0, must-revalidate') headers.add('Cache-Control', 'post-check=0, pre-check=0') headers.set('Pragma', 'no-cache') if not EXTRA_MODULES['onelogin']: return "SAML not configured on the server side.", 400, headers saml_nameid = request.cookies.get('saml-nameid', default=None) vo = extract_vo(request.headers) account = request.headers.get('X-Rucio-Account', default=None) appid = request.headers.get('X-Rucio-AppID', default='unknown') ip = request.headers.get('X-Forwarded-For', default=request.remote_addr) if saml_nameid: try: result = get_auth_token_saml(account, saml_nameid, appid, ip, vo=vo) except AccessDenied: return generate_http_error_flask( status_code=401, exc=CannotAuthenticate.__name__, exc_msg= f'Cannot authenticate to account {account} with given credentials', headers=headers) if not result: return generate_http_error_flask( status_code=401, exc=CannotAuthenticate.__name__, exc_msg= f'Cannot authenticate to account {account} with given credentials', headers=headers) headers.set('X-Rucio-Auth-Token', result['token']) headers.set('X-Rucio-Auth-Token-Expires', date_to_str(result['expires_at'])) return '', 200, headers # Path to the SAML config folder SAML_PATH = config_get('saml', 'config_path') req = prepare_saml_request(request.environ, dict(request.args.items(multi=False))) auth = OneLogin_Saml2_Auth(req, custom_base_path=SAML_PATH) headers.set('X-Rucio-SAML-Auth-URL', auth.login()) return '', 200, headers
def get(self): """ Authenticate a Rucio account temporarily via SSH key exchange. .. :quickref: SSH; Authenticate with SSH key exchange. :reqheader Rucio-VO: VO name as a string (Multi-VO only). :reqheader Rucio-Account: Account identifier as a string. :reqheader Rucio-SSH-Signature: Response to server challenge signed with SSH private key as a base64 encoded string. :reqheader Rucio-AppID: Application identifier as a string. :resheader Access-Control-Allow-Origin: :resheader Access-Control-Allow-Headers: :resheader Access-Control-Allow-Methods: :resheader Access-Control-Allow-Credentials: :resheader Access-Control-Expose-Headers: :resheader X-Rucio-Auth-Token: The authentication token :status 200: Successfully authenticated :status 404: Invalid credentials """ headers = self.get_headers() headers['Content-Type'] = 'application/octet-stream' headers[ 'Cache-Control'] = 'no-cache, no-store, max-age=0, must-revalidate' headers.add('Cache-Control', 'post-check=0, pre-check=0') headers['Pragma'] = 'no-cache' vo = extract_vo(request.headers) account = request.headers.get('X-Rucio-Account', default=None) signature = request.headers.get('X-Rucio-SSH-Signature', default=None) appid = request.headers.get('X-Rucio-AppID', default='unknown') ip = request.headers.get('X-Forwarded-For', default=request.remote_addr) # decode the signature which must come in base64 encoded try: signature += '=' * ( (4 - len(signature) % 4) % 4) # adding required padding signature = base64.b64decode(signature) except TypeError: return generate_http_error_flask( status_code=401, exc=CannotAuthenticate.__name__, exc_msg= f'Cannot authenticate to account {account} with malformed signature', headers=headers) try: result = get_auth_token_ssh(account, signature, appid, ip, vo=vo) except AccessDenied: return generate_http_error_flask( status_code=401, exc=CannotAuthenticate.__name__, exc_msg= f'Cannot authenticate to account {account} with given credentials', headers=headers) if not result: return generate_http_error_flask( status_code=401, exc=CannotAuthenticate.__name__, exc_msg= f'Cannot authenticate to account {account} with given credentials', headers=headers) headers['X-Rucio-Auth-Token'] = result['token'] headers['X-Rucio-Auth-Token-Expires'] = date_to_str( result['expires_at']) return '', 200, headers
def get(self): """ Authenticate a Rucio account temporarily via username and password. .. :quickref: UserPass; Authenticate with username/password :reqheader X-Rucio-VO: VO name as a string (Multi-VO Only) :reqheader X-Rucio-Account: Account identifier as a string. :reqheader X-Rucio-Username: Username as a string. :reqheader X-Rucio-Password: password as a text-plain string. :reqheader X-Rucio-AppID: Application identifier as a string. :resheader Access-Control-Allow-Origin: :resheader Access-Control-Allow-Headers: :resheader Access-Control-Allow-Methods: :resheader Access-Control-Allow-Credentials: :resheader Access-Control-Expose-Headers: :resheader X-Rucio-Auth-Token: The authentication token :status 200: Successfully authenticated :status 404: Invalid credentials """ headers = self.get_headers() headers['Content-Type'] = 'application/octet-stream' headers[ 'Cache-Control'] = 'no-cache, no-store, max-age=0, must-revalidate' headers.add('Cache-Control', 'post-check=0, pre-check=0') headers['Pragma'] = 'no-cache' vo = extract_vo(request.headers) account = request.headers.get('X-Rucio-Account', default=None) username = request.headers.get('X-Rucio-Username', default=None) password = request.headers.get('X-Rucio-Password', default=None) appid = request.headers.get('X-Rucio-AppID', default='unknown') ip = request.headers.get('X-Forwarded-For', default=request.remote_addr) if not account or not username or not password: return generate_http_error_flask( 401, CannotAuthenticate.__name__, 'Cannot authenticate without passing all required arguments', headers=headers) try: result = get_auth_token_user_pass(account, username, password, appid, ip, vo=vo) except AccessDenied: return generate_http_error_flask( 401, CannotAuthenticate.__name__, f'Cannot authenticate to account {account} with given credentials', headers=headers) if not result: return generate_http_error_flask( 401, CannotAuthenticate.__name__, f'Cannot authenticate to account {account} with given credentials', headers=headers) headers['X-Rucio-Auth-Token'] = result['token'] headers['X-Rucio-Auth-Token-Expires'] = date_to_str( result['expires_at']) return '', 200, headers
def get(self): """ Authenticate a Rucio account temporarily via an x509 certificate. .. :quickref: x509; Authenticate with x509 certificate. :reqheader Rucio-VO: VO name as a string (Multi-VO only). :reqheader Rucio-Account: Account identifier as a string. :reqheader Rucio-AppID: Application identifier as a string. :reqheader SSLStdEnv: Apache mod_ssl SSL Standard Env Variables. :resheader Access-Control-Allow-Origin: :resheader Access-Control-Allow-Headers: :resheader Access-Control-Allow-Methods: :resheader Access-Control-Allow-Credentials: :resheader Access-Control-Expose-Headers: :resheader X-Rucio-Auth-Token: The authentication token :status 200: Successfully authenticated :status 404: Invalid credentials """ headers = self.get_headers() headers['Content-Type'] = 'application/octet-stream' headers[ 'Cache-Control'] = 'no-cache, no-store, max-age=0, must-revalidate' headers.add('Cache-Control', 'post-check=0, pre-check=0') headers['Pragma'] = 'no-cache' vo = extract_vo(request.headers) account = request.headers.get('X-Rucio-Account', default=None) dn = request.environ.get('SSL_CLIENT_S_DN') if not dn: return generate_http_error_flask(401, CannotAuthenticate.__name__, 'Cannot get DN', headers=headers) if not dn.startswith('/'): dn = '/' + '/'.join(dn.split(',')[::-1]) appid = request.headers.get('X-Rucio-AppID', default='unknown') ip = request.headers.get('X-Forwarded-For', default=request.remote_addr) # If we get a valid proxy certificate we have to strip this postfix, # otherwise we would have to store the proxy DN in the database as well. # Alternative: use the SSL_CLIENT_I_DN, but that would require a separate # endpoint as you cannot programmatically decide, by examining the SSL variables, # if you got a proxy or regular certificate while True: if dn.endswith('/CN=limited proxy'): dn = dn[:-17] elif dn.endswith('/CN=proxy'): dn = dn[:-9] elif search('/CN=[0-9]*$', dn): dn = dn.rpartition('/')[0] else: break try: result = get_auth_token_x509(account, dn, appid, ip, vo=vo) except AccessDenied: return generate_http_error_flask( status_code=401, exc=CannotAuthenticate.__name__, exc_msg= f'Cannot authenticate to account {account} with given credentials', headers=headers) except IdentityError: return generate_http_error_flask( status_code=401, exc=CannotAuthenticate.__name__, exc_msg=f'No default account set for {dn}', headers=headers) if not result: return generate_http_error_flask( status_code=401, exc=CannotAuthenticate.__name__, exc_msg= f'Cannot authenticate to account {account} with given credentials', headers=headers) headers['X-Rucio-Auth-Token'] = result['token'] headers['X-Rucio-Auth-Token-Expires'] = date_to_str( result['expires_at']) return '', 200, headers
def get(self): """ .. :quickref: OIDC; Authenticate with OIDC :status 200: OK :status 401: Unauthorized :resheader X-Rucio-OIDC-Auth-URL: User & Rucio OIDC Client specific Authorization URL """ headers = self.get_headers() headers.set('Content-Type', 'application/octet-stream') headers.set('Cache-Control', 'no-cache, no-store, max-age=0, must-revalidate') headers.add('Cache-Control', 'post-check=0, pre-check=0') headers.set('Pragma', 'no-cache') vo = extract_vo(request.headers) account = request.environ.get('HTTP_X_RUCIO_ACCOUNT', 'webui') auth_scope = request.environ.get('HTTP_X_RUCIO_CLIENT_AUTHORIZE_SCOPE', "") audience = request.environ.get( 'HTTP_X_RUCIO_CLIENT_AUTHORIZE_AUDIENCE', "") auto = request.environ.get('HTTP_X_RUCIO_CLIENT_AUTHORIZE_AUTO', False) issuer = request.environ.get('HTTP_X_RUCIO_CLIENT_AUTHORIZE_ISSUER', None) polling = request.environ.get('HTTP_X_RUCIO_CLIENT_AUTHORIZE_POLLING', False) refresh_lifetime = request.environ.get( 'HTTP_X_RUCIO_CLIENT_AUTHORIZE_REFRESH_LIFETIME', None) auto = (auto == 'True' or auto == 'true') polling = (polling == 'True' or polling == 'true') if refresh_lifetime == 'None': refresh_lifetime = None ip = request.headers.get('X-Forwarded-For', default=request.remote_addr) try: kwargs = { 'auth_scope': auth_scope, 'audience': audience, 'issuer': issuer, 'auto': auto, 'polling': polling, 'refresh_lifetime': refresh_lifetime, 'ip': ip } result = get_auth_oidc(account, vo=vo, **kwargs) except AccessDenied: return generate_http_error_flask( status_code=401, exc=CannotAuthenticate.__name__, exc_msg= f'Cannot get authentication URL from Rucio Authentication Server for account {account}', headers=headers) if not result: return generate_http_error_flask( status_code=401, exc=CannotAuthenticate.__name__, exc_msg= f'Cannot get authentication URL from Rucio Authentication Server for account {account}', headers=headers) headers.set('X-Rucio-OIDC-Auth-URL', result) return '', 200, headers
def get(self): """ Sign a URL for a limited lifetime for a particular service. :reqheader X-Rucio-VO: VO name as a string (Multi-VO only). :reqheader X-Rucio-Account: Account identifier as a string. :reqheader X-Rucio-AppID: Application identifier as a string. :status 200: Successfully signed URL :status 400: Bad Request :status 401: Unauthorized :status 406: Not Acceptable """ headers = self.get_headers() vo = extract_vo(request.headers) account = request.headers.get('X-Rucio-Account', default=None) appid = request.headers.get('X-Rucio-AppID', default='unknown') ip = request.headers.get('X-Forwarded-For', default=request.remote_addr) if 'rse' not in request.args: return generate_http_error_flask(400, ValueError.__name__, 'Parameter "rse" not found', headers=headers) rse = request.args.get('rse') lifetime = request.args.get('lifetime', type=int, default=600) service = request.args.get('svc', default='gcs') operation = request.args.get('op', default='read') if 'url' not in request.args: return generate_http_error_flask(400, ValueError.__name__, 'Parameter "url" not found', headers=headers) url = request.args.get('url') if service not in ['gcs', 's3', 'swift']: return generate_http_error_flask( 400, ValueError.__name__, 'Parameter "svc" must be either empty(=gcs), gcs, s3 or swift', headers=headers) if operation not in ['read', 'write', 'delete']: return generate_http_error_flask( 400, ValueError.__name__, 'Parameter "op" must be either empty(=read), read, write, or delete.', headers=headers) result = get_signed_url(account, appid, ip, rse=rse, service=service, operation=operation, url=url, lifetime=lifetime, vo=vo) if not result: return generate_http_error_flask( 401, CannotAuthenticate.__name__, f'Cannot generate signed URL for account {account}', headers=headers) return str(result), 200, headers