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