def get_token(): account = ctx.env.get('HTTP_X_RUCIO_ACCOUNT') dn = ctx.env.get('SSL_CLIENT_S_DN') try: token = authentication.get_auth_token_x509(account, dn, 'webui', ctx.env.get('REMOTE_ADDR')) return token except: return False
def check_token(rendered_tpl): token = None js_token = "" js_account = "" def_account = None accounts = None cookie_accounts = None rucio_ui_version = version.version_string() render = template.render(join(dirname(__file__), '../templates')) if ctx.env.get('SSL_CLIENT_VERIFY') != 'SUCCESS': return render.problem("No certificate provided. Please authenticate with a cerficate registered in Rucio.") dn = ctx.env.get('SSL_CLIENT_S_DN') # try to get and check the rucio session token from cookie session_token = cookies().get('x-rucio-auth-token') validate_token = authentication.validate_auth_token(session_token) # if there is no session token or if invalid: get a new one. if validate_token is None: # get all accounts for an identity. Needed for account switcher in UI. accounts = identity.list_accounts_for_identity(dn, 'x509') cookie_accounts = accounts try: try: # try to get the default account for the identity. def_account = identity.get_default_account(dn, 'x509') except IdentityError: # if there is no default account used the first from the list off accounts. def_account = accounts[0] token = authentication.get_auth_token_x509(def_account, dn, 'webui', ctx.env.get('REMOTE_ADDR')) except: return render.problem("Your certificate (%s) is not registered in Rucio. Please contact <a href=\"mailto:[email protected]\">Rucio Support</a>" % dn) # write the token and account to javascript variables, that will be used in the HTML templates. js_token = __to_js('token', token) js_account = __to_js('account', def_account) # if there was no valid session token write the new token to a cookie. if token: setcookie('x-rucio-auth-token', value=token, expires=3600, path='/') if cookie_accounts: values = "" for acc in cookie_accounts: values += acc + " " setcookie('rucio-available-accounts', value=values[:-1], path='/') return render.base(js_token, js_account, rucio_ui_version, rendered_tpl)
def test_auth_x509(self): """ MULTI VO (REST): Test X509 authentication to multiple VOs """ mw = [] # TestApp doesn't support `cert` argument, so get tokens from API instead token_tst = get_auth_token_x509('root', '/CN=Rucio User', 'unknown', None, **self.vo).token token_new = get_auth_token_x509('root', '/CN=Rucio User', 'unknown', None, **self.new_vo).token headers_tst = {'X-Rucio-Auth-Token': str(token_tst)} res_tst = TestApp(account_app.wsgifunc(*mw)).get('/', headers=headers_tst, expect_errors=True) assert_equal(res_tst.status, 200) accounts_tst = [parse_response(a)['account'] for a in res_tst.body.decode().split('\n')[:-1]] assert_not_equal(len(accounts_tst), 0) assert_in(self.account_tst, accounts_tst) assert_not_in(self.account_new, accounts_tst) headers_new = {'X-Rucio-Auth-Token': str(token_new)} res_new = TestApp(account_app.wsgifunc(*mw)).get('/', headers=headers_new, expect_errors=True) assert_equal(res_new.status, 200) accounts_new = [parse_response(a)['account'] for a in res_new.body.decode().split('\n')[:-1]] assert_not_equal(len(accounts_new), 0) assert_in(self.account_new, accounts_new) assert_not_in(self.account_tst, accounts_new)
def GET(self): """ HTTP Success: 200 OK HTTP Error: 401 Unauthorized :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') 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) except AccessDenied: print 'Cannot Authenticate', account, dn, appid, ip 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 raise generate_http_error( 401, 'CannotAuthenticate', 'No default account set for %(dn)s' % locals()) if not result: print 'Cannot Authenticate', account, dn, appid, ip raise generate_http_error( 401, 'CannotAuthenticate', 'Cannot authenticate to account %(account)s with given credentials' % locals()) header('X-Rucio-Auth-Token', result) return str()
def check_token(rendered_tpl): attribs = None token = None js_token = "" js_account = "" def_account = None accounts = None cookie_accounts = None rucio_ui_version = version.version_string() ui_account = None if 'ui_account' in input(): ui_account = input()['ui_account'] render = template.render(join(dirname(__file__), '../templates')) if ctx.env.get('SSL_CLIENT_VERIFY') != 'SUCCESS': return render.problem( "No certificate provided. Please authenticate with a certificate registered in Rucio." ) dn = ctx.env.get('SSL_CLIENT_S_DN') msg = "Your certificate (%s) is not mapped to any rucio account." % dn msg += "<br><br><font color=\"red\">First, please make sure it is correctly registered in <a href=\"https://voms2.cern.ch:8443/voms/atlas\">VOMS</a> and be patient until it has been fully propagated through the system.</font>" msg += "<br><br>Then, if it is still not working please contact <a href=\"mailto:[email protected]\">DDM Support</a>." # try to get and check the rucio session token from cookie session_token = cookies().get('x-rucio-auth-token') validate_token = authentication.validate_auth_token(session_token) # check if ui_account param is set and if yes, force new token if ui_account: accounts = identity.list_accounts_for_identity(dn, 'x509') if len(accounts) == 0: return render.problem(msg) if ui_account not in accounts: return render.problem( "The rucio account (%s) you selected is not mapped to your certificate (%s). Please select another account or none at all to automatically use your default account." % (ui_account, dn)) cookie_accounts = accounts if (validate_token is None) or (validate_token['account'] != ui_account): try: token = authentication.get_auth_token_x509( ui_account, dn, 'webui', ctx.env.get('REMOTE_ADDR')) except: return render.problem(msg) attribs = list_account_attributes(ui_account) js_token = __to_js('token', token) js_account = __to_js('account', def_account) else: # if there is no session token or if invalid: get a new one. if validate_token is None: # get all accounts for an identity. Needed for account switcher in UI. accounts = identity.list_accounts_for_identity(dn, 'x509') if len(accounts) == 0: return render.problem(msg) cookie_accounts = accounts # try to set the default account to the user account, if not available take the first account. def_account = accounts[0] for account in accounts: account_info = get_account_info(account) if account_info.account_type == AccountType.USER: def_account = account break selected_account = cookies().get('rucio-selected-account') if (selected_account): def_account = selected_account try: token = authentication.get_auth_token_x509( def_account, dn, 'webui', ctx.env.get('REMOTE_ADDR')) except: return render.problem(msg) attribs = list_account_attributes(def_account) # write the token and account to javascript variables, that will be used in the HTML templates. js_token = __to_js('token', token) js_account = __to_js('account', def_account) # if there was no valid session token write the new token to a cookie. if token: setcookie('x-rucio-auth-token', value=token, path='/') setcookie('rucio-auth-token-created-at', value=int(time()), path='/') if cookie_accounts: values = "" for acc in cookie_accounts: values += acc + " " setcookie('rucio-available-accounts', value=values[:-1], path='/') if attribs: setcookie('rucio-account-attr', value=dumps(attribs), path='/') if ui_account: setcookie('rucio-selected-account', value=ui_account, path='/') return render.base(js_token, js_account, rucio_ui_version, rendered_tpl)
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
def get(self): """ Authenticate a Rucio account temporarily via an x509 certificate. .. :quickref: x509; Authenticate with x509 certificate. :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' 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) except AccessDenied: print('Cannot Authenticate', account, dn, appid, ip) 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) 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) 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
def GET(self): """ HTTP Success: 200 OK HTTP Error: 401 Unauthorized :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') 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) except AccessDenied: print 'Cannot Authenticate', account, dn, appid, ip 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 raise generate_http_error(401, 'CannotAuthenticate', 'No default account set for %(dn)s' % locals()) if not result: print 'Cannot Authenticate', account, dn, appid, ip raise generate_http_error(401, 'CannotAuthenticate', 'Cannot authenticate to account %(account)s with given credentials' % locals()) header('X-Rucio-Auth-Token', result) return str()