def test_get_auth_token_ssh_success(self):
        """AUTHENTICATION (CORE): SSH RSA public key exchange (good signature)."""

        try:
            add_account_identity(PUBLIC_KEY,
                                 IdentityType.SSH,
                                 'root',
                                 email='*****@*****.**')
        except Duplicate:
            pass  # might already exist, can skip

        challenge_token = get_ssh_challenge_token(account='root',
                                                  appid='test',
                                                  ip='127.0.0.1').token

        signature = base64.b64decode(ssh_sign(PRIVATE_KEY, challenge_token))

        result = get_auth_token_ssh(account='root',
                                    signature=signature,
                                    appid='test',
                                    ip='127.0.0.1')

        assert_is_not_none(result)

        del_account_identity(PUBLIC_KEY, IdentityType.SSH, 'root')
Exemple #2
0
    def test_invalid_padding(self):
        """AUTHENTICATION (CORE): SSH RSA public key exchange (public key with invalid padding)."""

        root = InternalAccount('root', **self.vo)
        try:
            add_account_identity(INVALID_PADDED_PUBLIC_KEY,
                                 IdentityType.SSH,
                                 root,
                                 email='*****@*****.**')
        except Duplicate:
            pass  # might already exist, can skip

        challenge_token = get_ssh_challenge_token(account='root',
                                                  appid='test',
                                                  ip='127.0.0.1',
                                                  **self.vo).get('token')

        ssh_sign_string = ssh_sign(PRIVATE_KEY, challenge_token)
        signature = base64.b64decode(ssh_sign_string)
        result = get_auth_token_ssh(account='root',
                                    signature=signature,
                                    appid='test',
                                    ip='127.0.0.1',
                                    **self.vo)
        assert result is not None

        del_account_identity(INVALID_PADDED_PUBLIC_KEY, IdentityType.SSH, root)
Exemple #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
    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()
Exemple #5
0
    def test_get_auth_token_ssh_fail(self):
        """AUTHENTICATION (CORE): SSH RSA public key exchange (wrong signature)."""

        root = InternalAccount('root', **self.vo)
        try:
            add_account_identity(PUBLIC_KEY, IdentityType.SSH, root, email='*****@*****.**')
        except Duplicate:
            pass  # might already exist, can skip

        signature = ssh_sign(PRIVATE_KEY, 'sign_something_else')

        result = get_auth_token_ssh(account='root', signature=signature, appid='test', ip='127.0.0.1', **self.vo)

        assert_is_none(result)

        del_account_identity(PUBLIC_KEY, IdentityType.SSH, root)
Exemple #6
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
Exemple #7
0
    def get(self):
        """
        Authenticate a Rucio account temporarily via SSH key exchange.

        .. :quickref: SSH; Authenticate with SSH key exchange.

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

        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)
        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
        response.set_data(str())
        return response