Example #1
0
    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 = Headers()
        headers.set('Access-Control-Allow-Origin', request.environ.get('HTTP_ORIGIN'))
        headers.set('Access-Control-Allow-Headers', request.environ.get('HTTP_ACCESS_CONTROL_REQUEST_HEADERS'))
        headers.set('Access-Control-Allow-Methods', '*')
        headers.set('Access-Control-Allow-Credentials', 'true')
        headers.set('Access-Control-Expose-Headers', 'X-Rucio-Auth-Token')

        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 = cookies().get('saml-nameid')
        vo = request.headers.get('X-Rucio-VO', default='def')
        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(401, 'CannotAuthenticate', 'Cannot authenticate to account %(account)s with given credentials' % locals(), headers=headers)
            except RucioException as error:
                return generate_http_error_flask(500, error.__class__.__name__, error.args[0], headers=headers)
            except Exception as error:
                logging.exception("Internal Error")
                return str(error), 500, headers

            if not result:
                return generate_http_error_flask(401, 'CannotAuthenticate', 'Cannot authenticate to account %(account)s with given credentials' % locals(), headers=headers)

            headers.set('X-Rucio-Auth-Token', result.token)
            headers.set('X-Rucio-Auth-Token-Expires', date_to_str(result.expired_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
Example #2
0
    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 = request.headers.get('X-Rucio-VO', default='def')
        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.expired_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
Example #3
0
    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 = Headers()
        headers['Access-Control-Allow-Origin'] = request.environ.get('HTTP_ORIGIN')
        headers['Access-Control-Allow-Headers'] = request.environ.get('HTTP_ACCESS_CONTROL_REQUEST_HEADERS')
        headers['Access-Control-Allow-Methods'] = '*'
        headers['Access-Control-Allow-Credentials'] = 'true'
        headers['Access-Control-Expose-Headers'] = 'X-Rucio-Auth-Token'

        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 = request.headers.get('X-Rucio-VO', default='def')
        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 = base64.b64decode(signature)
        except TypeError:
            return generate_http_error_flask(401, 'CannotAuthenticate', 'Cannot authenticate to account %(account)s with malformed signature' % locals(), headers=headers)

        try:
            result = get_auth_token_ssh(account, signature, appid, ip, vo=vo)
        except AccessDenied:
            return generate_http_error_flask(401, 'CannotAuthenticate', 'Cannot authenticate to account %(account)s with given credentials' % locals(), headers=headers)
        except RucioException as error:
            return generate_http_error_flask(500, error.__class__.__name__, error.args[0], headers=headers)
        except Exception as error:
            logging.exception("Internal Error")
            return str(error), 500

        if not result:
            return generate_http_error_flask(401, 'CannotAuthenticate', 'Cannot authenticate to account %(account)s with given credentials' % locals(), headers=headers)

        headers['X-Rucio-Auth-Token'] = result.token
        headers['X-Rucio-Auth-Token-Expires'] = date_to_str(result.expired_at)
        return '', 200, headers
Example #4
0
    def list_dids(self, scope, filters, type='collection', long=False):
        """
        List all data identifiers in a scope which match a given pattern.

        :param scope: The scope name.
        :param filters: A dictionary of key/value pairs like {'name': 'file_name','rse-expression': 'tier0'}.
        :param type: The type of the did: 'all'(container, dataset or file)|'collection'(dataset or container)|'dataset'|'container'|'file'
        :param long: Long format option to display more information for each DID.
        """
        path = '/'.join(
            [self.DIDS_BASEURL,
             quote_plus(scope), 'dids', 'search'])
        payload = {}
        if long:
            payload['long'] = 1

        for k, v in filters.items():
            if k in ('created_before', 'created_after'):
                payload[k] = date_to_str(v)
            else:
                payload[k] = v
        payload['type'] = type

        url = build_url(choice(self.list_hosts), path=path, params=payload)

        r = self._send_request(url, type='GET')
        if r.status_code == codes.ok:
            dids = self._load_json_data(r)
            return dids
        else:
            exc_cls, exc_msg = self._get_exception(headers=r.headers,
                                                   status_code=r.status_code,
                                                   data=r.content)
            raise exc_cls(exc_msg)
Example #5
0
    def list_dids_extended(self, scope, filters, did_type='collection', long=False, recursive=False):
        """
        List all data identifiers in a scope which match a given pattern. Extended version that goes through plugin mechanism.

        :param scope: The scope name.
        :param filters: A dictionary of key/value pairs like {'type': 'dataset', 'scope': 'test'}.
        :param did_type: The type of the did: 'all'(container, dataset or file)|'collection'(dataset or container)|'dataset'|'container'|'file'
        :param long: Long format option to display more information for each DID.
        :param recursive: Recursively list DIDs content.
        """
        path = '/'.join([self.DIDS_BASEURL, quote_plus(scope), 'dids', 'search_extended'])
        payload = {}

        for k, v in list(filters.items()):
            if k in ('created_before', 'created_after'):
                payload[k] = date_to_str(v)
            else:
                payload[k] = v
        payload['long'] = long
        payload['type'] = did_type
        payload['recursive'] = recursive

        url = build_url(choice(self.list_hosts), path=path, params=payload)
        r = self._send_request(url, type_='GET')
        if r.status_code == codes.ok:
            dids = self._load_json_data(r)
            return dids
        else:
            exc_cls, exc_msg = self._get_exception(headers=r.headers, status_code=r.status_code, data=r.content)
            raise exc_cls(exc_msg)
Example #6
0
    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 = Headers()
        headers['Access-Control-Allow-Origin'] = request.environ.get('HTTP_ORIGIN')
        headers['Access-Control-Allow-Headers'] = request.environ.get('HTTP_ACCESS_CONTROL_REQUEST_HEADERS')
        headers['Access-Control-Allow-Methods'] = '*'
        headers['Access-Control-Allow-Credentials'] = 'true'
        headers['Access-Control-Expose-Headers'] = 'X-Rucio-Auth-Token'

        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 = request.headers.get('X-Rucio-VO', default='def')
        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', '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', 'Cannot authenticate to account %(account)s with given credentials' % locals(), headers=headers)
        except RucioException as error:
            return generate_http_error_flask(500, error.__class__.__name__, error.args[0], headers=headers)
        except Exception as error:
            logging.exception("Internal Error")
            return str(error), 500, headers

        if not result:
            return generate_http_error_flask(401, 'CannotAuthenticate', 'Cannot authenticate to account %(account)s with given credentials' % locals(), headers=headers)

        headers['X-Rucio-Auth-Token'] = result.token
        headers['X-Rucio-Auth-Token-Expires'] = date_to_str(result.expired_at)
        return '', 200, headers
Example #7
0
    def GET(self):
        """
        HTTP Success:
            200 OK

        HTTP Error:
            401 Unauthorized

        :param QUERY_STRING: the URL query string itself

        :returns: "Rucio-Auth-Token" as a variable-length string header.
        """

        header('Access-Control-Allow-Origin', ctx.env.get('HTTP_ORIGIN'))
        header('Access-Control-Allow-Headers', ctx.env.get('HTTP_ACCESS_CONTROL_REQUEST_HEADERS'))
        header('Access-Control-Allow-Methods', '*')
        header('Access-Control-Allow-Credentials', 'true')

        header('Content-Type', 'application/octet-stream')
        header('Cache-Control', 'no-cache, no-store, max-age=0, must-revalidate')
        header('Cache-Control', 'post-check=0, pre-check=0', False)
        header('Pragma', 'no-cache')

        query_string = ctx.env.get('QUERY_STRING')
        ip = ctx.env.get('HTTP_X_FORWARDED_FOR')
        if ip is None:
            ip = ctx.ip

        try:
            result = get_token_oidc(query_string, ip)

        except AccessDenied:
            raise generate_http_error(401, 'CannotAuthorize', 'Cannot authorize token request.')
        except RucioException as error:
            raise generate_http_error(500, error.__class__.__name__, error.args[0])
        except Exception as error:
            print(format_exc())
            raise InternalError(error)

        if not result:
            raise generate_http_error(401, 'CannotAuthorize', 'Cannot authorize token request.')
        if 'token' in result and 'webhome' not in result:
            header('X-Rucio-Auth-Token', result['token'].token)  # pylint: disable=no-member
            header('X-Rucio-Auth-Token-Expires', date_to_str(result['token'].expired_at))  # pylint: disable=no-member
            return str()
        elif 'webhome' in result:
            webhome = result['webhome']
            if webhome is None:
                header('Content-Type', 'text/html')
                render = template.render(join(dirname(__file__), '../auth_templates/'))
                return render.auth_crash('unknown_identity')
            # domain setting is necessary so that the token gets distributed also to the webui server
            domain = '.'.join(urlparse.urlparse(webhome).netloc.split('.')[1:])
            setcookie('x-rucio-auth-token', value=result['token'].token, domain=domain, path='/')
            setcookie('rucio-auth-token-created-at', value=int(time.time()), domain=domain, path='/')
            return seeother(webhome)
        else:
            raise BadRequest()
Example #8
0
    def GET(self):
        """
        HTTP Success:
            200 OK

        HTTP Error:
            401 Unauthorized

        :param Rucio-VO: VO name as a string (Multi-VO only).
        :param Rucio-Account: Account identifier as a string.
        :param Rucio-AppID: Application identifier as a string.
        :param SavedCredentials: Apache mod_auth_kerb SavedCredentials.
        :returns: "Rucio-Auth-Token" as a variable-length string header.
        """

        header('Access-Control-Allow-Origin', ctx.env.get('HTTP_ORIGIN'))
        header('Access-Control-Allow-Headers',
               ctx.env.get('HTTP_ACCESS_CONTROL_REQUEST_HEADERS'))
        header('Access-Control-Allow-Methods', '*')
        header('Access-Control-Allow-Credentials', 'true')
        header('Access-Control-Expose-Headers', 'X-Rucio-Auth-Token')

        header('Content-Type', 'application/octet-stream')
        header('Cache-Control',
               'no-cache, no-store, max-age=0, must-revalidate')
        header('Cache-Control', 'post-check=0, pre-check=0', False)
        header('Pragma', 'no-cache')

        vo = ctx.env.get('HTTP_X_RUCIO_VO', '')
        account = ctx.env.get('HTTP_X_RUCIO_ACCOUNT')
        gsscred = ctx.env.get('REMOTE_USER')
        appid = ctx.env.get('HTTP_X_RUCIO_APPID')
        if appid is None:
            appid = 'unknown'
        ip = ctx.env.get('HTTP_X_FORWARDED_FOR')
        if ip is None:
            ip = ctx.ip

        try:
            result = get_auth_token_gss(account, gsscred, appid, ip, vo=vo)
        except AccessDenied:
            raise generate_http_error(
                401, 'CannotAuthenticate',
                'Cannot authenticate to account %(account)s with given credentials'
                % locals())

        if result is None:
            raise generate_http_error(
                401, 'CannotAuthenticate',
                'Cannot authenticate to account %(account)s with given credentials'
                % locals())
        else:
            header('X-Rucio-Auth-Token', result.token)
            header('X-Rucio-Auth-Token-Expires',
                   date_to_str(result.expired_at))
            return str()

        raise BadRequest()
Example #9
0
    def GET(self):
        """
        HTTP Success:
            200 OK

        HTTP Error:
            401 Unauthorized

        :param Rucio-VO: VO name as a string (Multi-VO only).
        :param Rucio-Account: Account identifier as a string.
        :param Rucio-SSH-Signature: Response to server challenge signed with SSH private key as a base64 encoded string.
        :param Rucio-AppID: Application identifier as a string.
        :returns: "Rucio-Auth-Token" as a variable-length string header.
        """

        header('Access-Control-Allow-Origin', ctx.env.get('HTTP_ORIGIN'))
        header('Access-Control-Allow-Headers', ctx.env.get('HTTP_ACCESS_CONTROL_REQUEST_HEADERS'))
        header('Access-Control-Allow-Methods', '*')
        header('Access-Control-Allow-Credentials', 'true')
        header('Access-Control-Expose-Headers', 'X-Rucio-Auth-Token')

        header('Content-Type', 'application/octet-stream')
        header('Cache-Control', 'no-cache, no-store, max-age=0, must-revalidate')
        header('Cache-Control', 'post-check=0, pre-check=0', False)
        header('Pragma', 'no-cache')

        vo = ctx.env.get('HTTP_X_RUCIO_VO', 'def')
        account = ctx.env.get('HTTP_X_RUCIO_ACCOUNT')
        signature = ctx.env.get('HTTP_X_RUCIO_SSH_SIGNATURE')
        appid = ctx.env.get('HTTP_X_RUCIO_APPID')
        if appid is None:
            appid = 'unknown'
        ip = ctx.env.get('HTTP_X_FORWARDED_FOR')
        if ip is None:
            ip = ctx.ip

        # decode the signature which must come in base64 encoded
        try:
            signature = base64.b64decode(signature)
        except:
            raise generate_http_error(401, 'CannotAuthenticate', 'Cannot authenticate to account %(account)s with malformed signature' % locals())

        try:
            result = get_auth_token_ssh(account, signature, appid, ip, vo=vo)
        except AccessDenied:
            raise generate_http_error(401, 'CannotAuthenticate', 'Cannot authenticate to account %(account)s with given credentials' % locals())
        except RucioException as error:
            raise generate_http_error(500, error.__class__.__name__, error.args[0])
        except Exception as error:
            print(format_exc())
            raise InternalError(error)

        if not result:
            raise generate_http_error(401, 'CannotAuthenticate', 'Cannot authenticate to account %(account)s with given credentials' % locals())

        header('X-Rucio-Auth-Token', result.token)
        header('X-Rucio-Auth-Token-Expires', date_to_str(result.expired_at))
        return str()
Example #10
0
    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
Example #11
0
    def get(self):
        """
        .. :quickref: OIDC;

        :status 200: OK
        :status 401: Unauthorized
        :resheader X-Rucio-Auth-Token: The authentication token
        :resheader X-Rucio-Auth-Token-Expires: The time when the token expires
        """
        headers = Headers()
        headers.set('Access-Control-Allow-Origin', request.environ.get('HTTP_ORIGIN'))
        headers.set('Access-Control-Allow-Headers', request.environ.get('HTTP_ACCESS_CONTROL_REQUEST_HEADERS'))
        headers.set('Access-Control-Allow-Methods', '*')
        headers.set('Access-Control-Allow-Credentials', 'true')

        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')

        query_string = request.query_string.decode(encoding='utf-8')
        ip = request.headers.get('X-Forwarded-For', default=request.remote_addr)

        try:
            result = get_token_oidc(query_string, ip)
        except AccessDenied:
            return generate_http_error_flask(401, 'CannotAuthorize', 'Cannot authorize token request.', headers=headers)
        except RucioException as error:
            return generate_http_error_flask(500, error.__class__.__name__, error.args[0], headers=headers)
        except Exception as error:
            logging.exception("Internal Error")
            return str(error), 500, headers

        if not result:
            return generate_http_error_flask(401, 'CannotAuthorize', 'Cannot authorize token request.', headers=headers)
        if 'token' in result and 'webhome' not in result:
            headers.set('X-Rucio-Auth-Token', result['token'].token)
            headers.set('X-Rucio-Auth-Token-Expires', date_to_str(result['token'].expired_at))
            return '', 200, headers
        elif 'webhome' in result:
            webhome = result['webhome']
            if webhome is None:
                headers.extend(error_headers('CannotAuthenticate', 'Cannot find your OIDC identity linked to any Rucio account'))
                headers.set('Content-Type', 'text/html')
                return render_template('auth_crash.html', crashtype='unknown_identity'), 401, headers
            # domain setting is necessary so that the token gets distributed also to the webui server
            domain = '.'.join(urlparse.urlparse(webhome).netloc.split('.')[1:])
            response = redirect(webhome, code=303)
            response.headers.extend(headers)
            response.set_cookie('x-rucio-auth-token', value=result['token'].token, domain=domain, path='/')
            response.set_cookie('rucio-auth-token-created-at', value=str(time.time()), domain=domain, path='/')
            return response
        else:
            return '', 400, headers
    def GET(self):
        """
        HTTP Success:
            200 OK

        HTTP Error:
            401 Unauthorized

        :param Rucio-VO: VO name as a string (Multi-VO only).
        :param Rucio-Account: Account identifier as a string.
        :param Rucio-AppID: Application identifier as a string.
        :returns: "Rucio-Auth-Token" as a variable-length string header.
        """

        header('Access-Control-Allow-Origin', ctx.env.get('HTTP_ORIGIN'))
        header('Access-Control-Allow-Headers',
               ctx.env.get('HTTP_ACCESS_CONTROL_REQUEST_HEADERS'))
        header('Access-Control-Allow-Methods', '*')
        header('Access-Control-Allow-Credentials', 'true')
        header('Access-Control-Expose-Headers', 'X-Rucio-Auth-Token')

        header('Content-Type', 'application/octet-stream')
        header('Cache-Control',
               'no-cache, no-store, max-age=0, must-revalidate')
        header('Cache-Control', 'post-check=0, pre-check=0', False)
        header('Pragma', 'no-cache')

        vo = ctx.env.get('HTTP_X_RUCIO_VO', 'def')
        account = ctx.env.get('HTTP_X_RUCIO_ACCOUNT')
        appid = ctx.env.get('HTTP_X_RUCIO_APPID')
        if appid is None:
            appid = 'unknown'
        ip = ctx.env.get('HTTP_X_FORWARDED_FOR')
        if ip is None:
            ip = ctx.ip

        try:
            result = get_ssh_challenge_token(account, appid, ip, vo=vo)
        except RucioException as error:
            raise generate_http_error(500, error.__class__.__name__,
                                      error.args[0])
        except Exception as error:
            print(format_exc())
            raise InternalError(error)

        if not result:
            raise generate_http_error(
                401, 'CannotAuthenticate',
                'Cannot generate challenge for account %(account)s' % locals())

        header('X-Rucio-SSH-Challenge-Token', result.token)
        header('X-Rucio-SSH-Challenge-Token-Expires',
               date_to_str(result.expired_at))
        return str()
Example #13
0
    def GET(self):
        """
        HTTP Success:
            200 OK

        HTTP Error:
            401 Unauthorized

        :param Rucio-Account: Account identifier as a string.
        :param Rucio-Username: Username as a string.
        :param Rucio-Password: SHA1 hash of the password as a string.
        :param Rucio-AppID: Application identifier as a string.
        :returns: "Rucio-Auth-Token" as a variable-length string header.
        """

        header('Access-Control-Allow-Origin', ctx.env.get('HTTP_ORIGIN'))
        header('Access-Control-Allow-Headers', ctx.env.get('HTTP_ACCESS_CONTROL_REQUEST_HEADERS'))
        header('Access-Control-Allow-Methods', '*')
        header('Access-Control-Allow-Credentials', 'true')
        header('Access-Control-Expose-Headers', 'X-Rucio-Auth-Token')

        header('Content-Type', 'application/octet-stream')
        header('Cache-Control', 'no-cache, no-store, max-age=0, must-revalidate')
        header('Cache-Control', 'post-check=0, pre-check=0', False)
        header('Pragma', 'no-cache')

        account = ctx.env.get('HTTP_X_RUCIO_ACCOUNT')
        username = ctx.env.get('HTTP_X_RUCIO_USERNAME')
        password = ctx.env.get('HTTP_X_RUCIO_PASSWORD')
        appid = ctx.env.get('HTTP_X_RUCIO_APPID')
        if appid is None:
            appid = 'unknown'
        ip = ctx.env.get('HTTP_X_FORWARDED_FOR')
        if ip is None:
            ip = ctx.ip

        try:
            result = get_auth_token_user_pass(account, username, password, appid, ip)
        except AccessDenied:
            raise generate_http_error(401, 'CannotAuthenticate', 'Cannot authenticate to account %(account)s with given credentials' % locals())
        except RucioException as error:
            raise generate_http_error(500, error.__class__.__name__, error.args[0])
        except Exception as error:
            print(format_exc())
            raise InternalError(error)

        if not result:
            raise generate_http_error(401, 'CannotAuthenticate', 'Cannot authenticate to account %(account)s with given credentials' % locals())

        header('X-Rucio-Auth-Token', result.token)
        header('X-Rucio-Auth-Token-Expires', date_to_str(result.expired_at))
        return str()
Example #14
0
    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 = Headers()
        headers['Access-Control-Allow-Origin'] = request.environ.get('HTTP_ORIGIN')
        headers['Access-Control-Allow-Headers'] = request.environ.get('HTTP_ACCESS_CONTROL_REQUEST_HEADERS')
        headers['Access-Control-Allow-Methods'] = '*'
        headers['Access-Control-Allow-Credentials'] = 'true'
        headers['Access-Control-Expose-Headers'] = 'X-Rucio-Auth-Token'

        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 = request.headers.get('X-Rucio-VO', default='def')
        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)

        try:
            result = get_ssh_challenge_token(account, appid, ip, vo=vo)
        except RucioException as error:
            return generate_http_error_flask(500, error.__class__.__name__, error.args[0], headers=headers)
        except Exception as error:
            logging.exception("Internal Error")
            return str(error), 500, headers

        if not result:
            return generate_http_error_flask(401, 'CannotAuthenticate', 'Cannot generate challenge for account %(account)s' % locals(), headers=headers)

        headers['X-Rucio-SSH-Challenge-Token'] = result.token
        headers['X-Rucio-SSH-Challenge-Token-Expires'] = date_to_str(result.expired_at)
        return '', 200, headers
Example #15
0
    def list_dids_extended(self,
                           scope,
                           filters,
                           did_type='collection',
                           long=False,
                           recursive=False):
        """
        List all data identifiers in a scope which match a given pattern.

        :param scope: The scope name.
        :param filters: A nested dictionary of key/value pairs like [{'key1': 'value1', 'key2.lte': 'value2'}, {'key3.gte, 'value3'}].
                        Keypairs in the same dictionary are AND'ed together, dictionaries are OR'ed together. Keys should be suffixed
                        like <key>.<operation>, e.g. key1 >= value1 is equivalent to {'key1.gte': value}, where <operation> belongs to one
                        of the set {'lte', 'gte', 'gt', 'lt', 'ne' or ''}. Equivalence doesn't require an operator.
        :param did_type: The type of the did: 'all'(container, dataset or file)|'collection'(dataset or container)|'dataset'|'container'|'file'
        :param long: Long format option to display more information for each DID.
        :param recursive: Recursively list DIDs content.
        """
        path = '/'.join(
            [self.DIDS_BASEURL,
             quote_plus(scope), 'dids', 'search_extended'])

        # stringify dates.
        if isinstance(
                filters,
                dict):  # backwards compatability for filters as single {}
            filters = [filters]
        for or_group in filters:
            for key, value in or_group.items():
                if isinstance(value, datetime):
                    or_group[key] = date_to_str(value)

        payload = {
            'type': did_type,
            'filters': filters,
            'long': long,
            'recursive': recursive
        }

        url = build_url(choice(self.list_hosts), path=path, params=payload)

        r = self._send_request(url, type_='GET')

        if r.status_code == codes.ok:
            dids = self._load_json_data(r)
            return dids
        else:
            exc_cls, exc_msg = self._get_exception(headers=r.headers,
                                                   status_code=r.status_code,
                                                   data=r.content)
            raise exc_cls(exc_msg)
Example #16
0
    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 = Headers()
        headers['Access-Control-Allow-Origin'] = request.environ.get('HTTP_ORIGIN')
        headers['Access-Control-Allow-Headers'] = request.environ.get('HTTP_ACCESS_CONTROL_REQUEST_HEADERS')
        headers['Access-Control-Allow-Methods'] = '*'
        headers['Access-Control-Allow-Credentials'] = 'true'
        headers['Access-Control-Expose-Headers'] = 'X-Rucio-Auth-Token'

        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 = request.headers.get('X-Rucio-VO', default='def')
        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(401, 'CannotAuthenticate', 'Cannot authenticate to account %(account)s with given credentials' % locals(), headers=headers)

        if result is None:
            return generate_http_error_flask(401, 'CannotAuthenticate', 'Cannot authenticate to account %(account)s with given credentials' % locals(), headers=headers)

        headers['X-Rucio-Auth-Token'] = result.token
        headers['X-Rucio-Auth-Token-Expires'] = date_to_str(result.expired_at)
        return '', 200, headers
Example #17
0
    def list_dids(self,
                  scope,
                  filters,
                  did_type='collection',
                  long=False,
                  recursive=False):
        """
        List all data identifiers in a scope which match a given pattern.

        :param scope: The scope name.
        :param filters: A dictionary of key/value pairs like {'type': 'dataset', 'scope': 'test'}.
        :param did_type: The type of the did: 'all'(container, dataset or file)|'collection'(dataset or container)|'dataset'|'container'|'file'
        :param long: Long format option to display more information for each DID.
        :param recursive: Recursively list DIDs content.
        """
        path = '/'.join(
            [self.DIDS_BASEURL,
             quote_plus(scope), 'dids', 'search'])

        # stringify dates.
        if isinstance(
                filters,
                dict):  # backwards compatability for filters as single {}
            filters = [filters]
        for or_group in filters:
            for key, value in or_group.items():
                if isinstance(value, datetime):
                    or_group[key] = date_to_str(value)

        payload = {
            'type': did_type,
            'filters': filters,
            'long': long,
            'recursive': recursive
        }

        url = build_url(choice(self.list_hosts), path=path, params=payload)

        r = self._send_request(url, type_='GET')

        if r.status_code == codes.ok:
            dids = self._load_json_data(r)
            return dids
        else:
            exc_cls, exc_msg = self._get_exception(headers=r.headers,
                                                   status_code=r.status_code,
                                                   data=r.content)
            raise exc_cls(exc_msg)
Example #18
0
    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 = request.headers.get('X-Rucio-VO', default='def')
        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.expired_at)
        return '', 200, headers
Example #19
0
    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
        """

        response = Response()
        response.headers['Access-Control-Allow-Origin'] = request.environ.get(
            'HTTP_ORIGIN')
        response.headers['Access-Control-Allow-Headers'] = request.environ.get(
            'HTTP_ACCESS_CONTROL_REQUEST_HEADERS')
        response.headers['Access-Control-Allow-Methods'] = '*'
        response.headers['Access-Control-Allow-Credentials'] = 'true'
        response.headers[
            'Access-Control-Expose-Headers'] = 'X-Rucio-Auth-Token'

        response.headers['Content-Type'] = 'application/octet-stream'
        response.headers[
            'Cache-Control'] = 'no-cache, no-store, max-age=0, must-revalidate'
        response.headers['Cache-Control'] = 'post-check=0, pre-check=0'
        response.headers['Pragma'] = 'no-cache'

        vo = request.environ.get('HTTP_X_RUCIO_VO', 'def')
        account = request.environ.get('HTTP_X_RUCIO_ACCOUNT')
        dn = request.environ.get('SSL_CLIENT_S_DN')
        if not dn:
            return generate_http_error_flask(401, 'CannotAuthenticate',
                                             'Cannot get DN')
        if not dn.startswith('/'):
            dn = '/%s' % '/'.join(dn.split(',')[::-1])

        appid = request.environ.get('HTTP_X_RUCIO_APPID')
        if appid is None:
            appid = 'unknown'
        ip = request.environ.get('HTTP_X_FORWARDED_FOR')
        if ip is None:
            ip = 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:
            print('Cannot Authenticate', account, dn, appid, ip, vo)
            return generate_http_error_flask(
                401, 'CannotAuthenticate',
                'Cannot authenticate to account %(account)s with given credentials'
                % locals())
        except IdentityError:
            print('Cannot Authenticate', account, dn, appid, ip, vo)
            return generate_http_error_flask(
                401, 'CannotAuthenticate',
                'No default account set for %(dn)s' % locals())

        if not result:
            print('Cannot Authenticate', account, dn, appid, ip, vo)
            return generate_http_error_flask(
                401, 'CannotAuthenticate',
                'Cannot authenticate to account %(account)s with given credentials'
                % locals())

        response.headers['X-Rucio-Auth-Token'] = result.token
        response.headers['X-Rucio-Auth-Token-Expires'] = date_to_str(
            result.expired_at)
        return response
Example #20
0
    def get(self):
        """
        Request a challenge token for SSH authentication

        .. :quickref: SSHChallengeToken; 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-Auth-Token: The authentication token
        :status 200: Successfully authenticated
        :status 404: Invalid credentials
        """

        response = Response()
        response.headers['Access-Control-Allow-Origin'] = request.environ.get(
            'HTTP_ORIGIN')
        response.headers['Access-Control-Allow-Headers'] = request.environ.get(
            'HTTP_ACCESS_CONTROL_REQUEST_HEADERS')
        response.headers['Access-Control-Allow-Methods'] = '*'
        response.headers['Access-Control-Allow-Credentials'] = 'true'
        response.headers[
            'Access-Control-Expose-Headers'] = 'X-Rucio-Auth-Token'

        response.headers['Content-Type'] = 'application/octet-stream'
        response.headers[
            'Cache-Control'] = 'no-cache, no-store, max-age=0, must-revalidate'
        response.headers['Cache-Control'] = 'post-check=0, pre-check=0'
        response.headers['Pragma'] = 'no-cache'

        vo = request.environ.get('HTTP_X_RUCIO_VO', 'def')
        account = request.environ.get('HTTP_X_RUCIO_ACCOUNT')
        appid = request.environ.get('HTTP_X_RUCIO_APPID')
        if appid is None:
            appid = 'unknown'
        ip = request.environ.get('HTTP_X_FORWARDED_FOR')
        if ip is None:
            ip = request.remote_addr

        try:
            result = get_ssh_challenge_token(account, appid, ip, vo=vo)
        except RucioException as error:
            return generate_http_error_flask(500, error.__class__.__name__,
                                             error.args[0])
        except Exception as error:
            print(format_exc())
            return error, 500

        if not result:
            return generate_http_error_flask(
                401, 'CannotAuthenticate',
                'Cannot generate challenge for account %(account)s' % locals())

        response.headers['X-Rucio-Auth-Token'] = result.token
        response.headers['X-Rucio-Auth-Token-Expires'] = date_to_str(
            result.expired_at)
        return response
Example #21
0
    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
        """

        response = Response()
        response.headers['Access-Control-Allow-Origin'] = request.environ.get(
            'HTTP_ORIGIN')
        response.headers['Access-Control-Allow-Headers'] = request.environ.get(
            'HTTP_ACCESS_CONTROL_REQUEST_HEADERS')
        response.headers['Access-Control-Allow-Methods'] = '*'
        response.headers['Access-Control-Allow-Credentials'] = 'true'
        response.headers[
            'Access-Control-Expose-Headers'] = 'X-Rucio-Auth-Token'

        response.headers['Content-Type'] = 'application/octet-stream'
        response.headers[
            'Cache-Control'] = 'no-cache, no-store, max-age=0, must-revalidate'
        response.headers['Cache-Control'] = 'post-check=0, pre-check=0'
        response.headers['Pragma'] = 'no-cache'

        vo = request.environ.get('HTTP_X_RUCIO_VO', 'def')
        account = request.environ.get('HTTP_X_RUCIO_ACCOUNT')
        username = request.environ.get('HTTP_X_RUCIO_USERNAME')
        password = request.environ.get('HTTP_X_RUCIO_PASSWORD')
        appid = request.environ.get('HTTP_X_RUCIO_APPID')
        if appid is None:
            appid = 'unknown'
        ip = request.environ.get('HTTP_X_FORWARDED_FOR')
        if ip is None:
            ip = request.remote_addr

        print(account, username, password, appid)
        try:
            result = get_auth_token_user_pass(account,
                                              username,
                                              password,
                                              appid,
                                              ip,
                                              vo=vo)
        except AccessDenied:
            return generate_http_error_flask(
                401, 'CannotAuthenticate',
                'Cannot authenticate to account %(account)s with given credentials'
                % locals())
        except RucioException as error:
            return generate_http_error_flask(500, error.__class__.__name__,
                                             error.args[0])
        except Exception as error:
            print(format_exc())
            return error, 500

        if not result:
            return generate_http_error_flask(
                401, 'CannotAuthenticate',
                'Cannot authenticate to account %(account)s with given credentials'
                % locals())

        response.headers['X-Rucio-Auth-Token'] = result.token
        response.headers['X-Rucio-Auth-Token-Expires'] = date_to_str(
            result.expired_at)
        return response
    def GET(self):
        """
        HTTP Success:
            200 OK

        HTTP Error:
            401 Unauthorized

        :param Rucio-VO: VO name as a string (Multi-VO only).
        :param Rucio-Account: Account identifier as a string.
        :param Rucio-AppID: Application identifier as a string.
        :param SSLStdEnv: Apache mod_ssl SSL Standard Env Variables.
        :returns: "Rucio-Auth-Token" as a variable-length string header.
        """

        header('Access-Control-Allow-Origin', ctx.env.get('HTTP_ORIGIN'))
        header('Access-Control-Allow-Headers',
               ctx.env.get('HTTP_ACCESS_CONTROL_REQUEST_HEADERS'))
        header('Access-Control-Allow-Methods', '*')
        header('Access-Control-Allow-Credentials', 'true')
        header('Access-Control-Expose-Headers', 'X-Rucio-Auth-Token')

        header('Content-Type', 'application/octet-stream')
        header('Cache-Control',
               'no-cache, no-store, max-age=0, must-revalidate')
        header('Cache-Control', 'post-check=0, pre-check=0', False)
        header('Pragma', 'no-cache')

        vo = ctx.env.get('HTTP_X_RUCIO_VO', 'def')
        account = ctx.env.get('HTTP_X_RUCIO_ACCOUNT')
        dn = ctx.env.get('SSL_CLIENT_S_DN')
        if not dn:
            raise generate_http_error(401, 'CannotAuthenticate',
                                      'Cannot get DN')
        if not dn.startswith('/'):
            dn = '/%s' % '/'.join(dn.split(',')[::-1])

        appid = ctx.env.get('HTTP_X_RUCIO_APPID')
        if appid is None:
            appid = 'unknown'
        ip = ctx.env.get('HTTP_X_FORWARDED_FOR')
        if ip is None:
            ip = ctx.ip

        # 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:
            print('Cannot Authenticate', account, dn, appid, ip, vo)
            raise generate_http_error(
                401, 'CannotAuthenticate',
                'Cannot authenticate to account %(account)s with given credentials'
                % locals())
        except IdentityError:
            print('Cannot Authenticate', account, dn, appid, ip, vo)
            raise generate_http_error(
                401, 'CannotAuthenticate',
                'No default account set for %(dn)s' % locals())

        if not result:
            print('Cannot Authenticate', account, dn, appid, ip, vo)
            raise generate_http_error(
                401, 'CannotAuthenticate',
                'Cannot authenticate to account %(account)s with given credentials'
                % locals())

        header('X-Rucio-Auth-Token', result.token)
        header('X-Rucio-Auth-Token-Expires', date_to_str(result.expired_at))
        return str()
    def GET(self):
        """
        HTTP Success:
            200 OK

        HTTP Error:
            401 Unauthorized

        :param Rucio-VO: VO name as a string (Multi-VO only)
        :param Rucio-Account: Account identifier as a string.
        :param Rucio-Username: Username as a string.
        :param Rucio-Password: Password as a string.
        :param Rucio-AppID: Application identifier as a string.
        :returns: "X-Rucio-SAML-Auth-URL" as a variable-length string header.
        """

        header('Access-Control-Allow-Origin', ctx.env.get('HTTP_ORIGIN'))
        header('Access-Control-Allow-Headers',
               ctx.env.get('HTTP_ACCESS_CONTROL_REQUEST_HEADERS'))
        header('Access-Control-Allow-Methods', '*')
        header('Access-Control-Allow-Credentials', 'true')
        header('Access-Control-Expose-Headers', 'X-Rucio-Auth-Token')

        header('Content-Type', 'application/octet-stream')
        header('Cache-Control',
               'no-cache, no-store, max-age=0, must-revalidate')
        header('Cache-Control', 'post-check=0, pre-check=0', False)
        header('Pragma', 'no-cache')

        if not EXTRA_MODULES['onelogin']:
            header('X-Rucio-Auth-Token', None)
            return "SAML not configured on the server side."

        saml_nameid = cookies().get('saml-nameid')
        vo = ctx.env.get('HTTP_X_RUCIO_VO', 'def')
        account = ctx.env.get('HTTP_X_RUCIO_ACCOUNT')
        appid = ctx.env.get('HTTP_X_RUCIO_APPID')
        if appid is None:
            appid = 'unknown'
        ip = ctx.env.get('HTTP_X_FORWARDED_FOR')
        if ip is None:
            ip = ctx.ip

        if saml_nameid:
            try:
                result = get_auth_token_saml(account,
                                             saml_nameid,
                                             appid,
                                             ip,
                                             vo=vo)
            except AccessDenied:
                raise generate_http_error(
                    401, 'CannotAuthenticate',
                    'Cannot authenticate to account %(account)s with given credentials'
                    % locals())
            except RucioException as error:
                raise generate_http_error(500, error.__class__.__name__,
                                          error.args[0])
            except Exception as error:
                print(format_exc())
                raise InternalError(error)

            if not result:
                raise generate_http_error(
                    401, 'CannotAuthenticate',
                    'Cannot authenticate to account %(account)s with given credentials'
                    % locals())

            header('X-Rucio-Auth-Token', result.token)
            header('X-Rucio-Auth-Token-Expires',
                   date_to_str(result.expired_at))
            return str()

        # Path to the SAML config folder
        SAML_PATH = config_get('saml', 'config_path')

        request = ctx.env
        data = dict(param_input())
        req = prepare_saml_request(request, data)
        auth = OneLogin_Saml2_Auth(req, custom_base_path=SAML_PATH)

        header('X-Rucio-SAML-Auth-URL', auth.login())
        return str()
Example #24
0
    def get(self):
        """
        .. :quickref: OIDC;

        :status 200: OK
        :status 401: Unauthorized
        :resheader X-Rucio-Auth-Token: The authentication token
        :resheader X-Rucio-Auth-Token-Expires: The time when the token expires
        """
        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')

        query_string = request.query_string.decode(encoding='utf-8')
        ip = request.headers.get('X-Forwarded-For',
                                 default=request.remote_addr)

        try:
            result = get_token_oidc(query_string, ip)
        except AccessDenied:
            return generate_http_error_flask(401,
                                             CannotAuthorize.__name__,
                                             'Cannot authorize token request.',
                                             headers=headers)

        if not result:
            return generate_http_error_flask(401,
                                             CannotAuthorize.__name__,
                                             'Cannot authorize token request.',
                                             headers=headers)
        if 'token' in result and 'webhome' not in result:
            headers.set('X-Rucio-Auth-Token', result['token']['token'])
            headers.set('X-Rucio-Auth-Token-Expires',
                        date_to_str(result['token']['expires_at']))
            return '', 200, headers
        elif 'webhome' in result:
            webhome = result['webhome']
            if webhome is None:
                headers.extend(
                    error_headers(
                        CannotAuthenticate.__name__,
                        'Cannot find your OIDC identity linked to any Rucio account'
                    ))
                headers.set('Content-Type', 'text/html')
                return render_template(
                    'auth_crash.html',
                    crashtype='unknown_identity'), 401, headers
            # domain setting is necessary so that the token gets distributed also to the webui server
            domain = '.'.join(urlparse(webhome).netloc.split('.')[1:])
            response = redirect(webhome, code=303)
            response.headers.extend(headers)
            response.set_cookie('x-rucio-auth-token',
                                value=result['token']['token'],
                                domain=domain,
                                path='/')
            response.set_cookie('rucio-auth-token-created-at',
                                value=str(time.time()),
                                domain=domain,
                                path='/')
            return response
        else:
            return '', 400, headers
Example #25
0
    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 = request.headers.get('X-Rucio-VO', default='def')
        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 = 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.expired_at)
        return '', 200, headers
Example #26
0
    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 = request.headers.get('X-Rucio-VO', default='def')
        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.expired_at)
        return '', 200, headers
Example #27
0
    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
Example #28
0
    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
        """

        response = Response()
        response.headers['Access-Control-Allow-Origin'] = request.environ.get(
            'HTTP_ORIGIN')
        response.headers['Access-Control-Allow-Headers'] = request.environ.get(
            'HTTP_ACCESS_CONTROL_REQUEST_HEADERS')
        response.headers['Access-Control-Allow-Methods'] = '*'
        response.headers['Access-Control-Allow-Credentials'] = 'true'
        response.headers[
            'Access-Control-Expose-Headers'] = 'X-Rucio-Auth-Token'

        response.headers['Content-Type'] = 'application/octet-stream'
        response.headers[
            'Cache-Control'] = 'no-cache, no-store, max-age=0, must-revalidate'
        response.headers['Cache-Control'] = 'post-check=0, pre-check=0'
        response.headers['Pragma'] = 'no-cache'

        vo = request.environ.get('HTTP_X_RUCIO_VO', 'def')
        account = request.environ.get('HTTP_X_RUCIO_ACCOUNT')
        signature = request.environ.get('HTTP_X_RUCIO_SSH_SIGNATURE')
        appid = request.environ.get('HTTP_X_RUCIO_APPID')
        if appid is None:
            appid = 'unknown'
        ip = request.environ.get('HTTP_X_FORWARDED_FOR')
        if ip is None:
            ip = request.remote_addr

        # decode the signature which must come in base64 encoded
        try:
            signature = base64.b64decode(signature)
        except Exception as error:  # noqa: F841
            return generate_http_error_flask(
                401, 'CannotAuthenticate',
                'Cannot authenticate to account %(account)s with malformed signature'
                % locals())

        try:
            result = get_auth_token_ssh(account, signature, appid, ip, vo=vo)
        except AccessDenied:
            return generate_http_error_flask(
                401, 'CannotAuthenticate',
                'Cannot authenticate to account %(account)s with given credentials'
                % locals())
        except RucioException as error:
            return generate_http_error_flask(500, error.__class__.__name__,
                                             error.args[0])
        except Exception as error:
            print(format_exc())
            return error, 500

        if not result:
            return generate_http_error_flask(
                401, 'CannotAuthenticate',
                'Cannot authenticate to account %(account)s with given credentials'
                % locals())

        response.headers['X-Rucio-Auth-Token'] = result.token
        response.headers['X-Rucio-Auth-Token-Expires'] = date_to_str(
            result.expired_at)
        return response