Beispiel #1
0
    def test_auth_saml(self):
        """ MULTI VO (REST): Test saml authentication to multiple VOs """
        mw = []

        try:
            add_account_identity('ddmlab', 'SAML', 'root', '*****@*****.**', 'root', **self.vo)
            add_account_identity('ddmlab', 'SAML', 'root', '*****@*****.**', 'root', **self.new_vo)
        except Duplicate:
            pass  # Might already exist, can skip

        # Can't rely on `onelogin` module being present, so get tokens from API instead
        token_tst = get_auth_token_saml('root', 'ddmlab', 'unknown', None, **self.vo).token
        token_new = get_auth_token_saml('root', 'ddmlab', '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)
Beispiel #2
0
    def test_get_auth_token_saml_fail(self):
        """AUTHENTICATION (CORE): SAML NameID (wrong credentials)."""
        root = InternalAccount('root', **self.vo)
        try:
            add_account_identity('ddmlab', IdentityType.SAML, root, email='*****@*****.**')
        except Duplicate:
            pass  # might already exist, can skip

        with assert_raises(AccessDenied):
            get_auth_token_saml(account='root', saml_nameid='not_ddmlab', appid='test', ip='127.0.0.1', **self.vo)

        del_account_identity('ddmlab', IdentityType.SAML, root)
Beispiel #3
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
Beispiel #4
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
Beispiel #5
0
    def test_get_auth_token_saml_success(self):
        """AUTHENTICATION (CORE): SAML NameID (correct credentials)."""
        root = InternalAccount('root', **self.vo)
        try:
            add_account_identity('ddmlab', IdentityType.SAML, root, email='*****@*****.**')
        except Duplicate:
            pass  # might already exist, can skip

        try:
            result = get_auth_token_saml(account='root', saml_nameid='ddmlab', appid='test', ip='127.0.0.1', **self.vo)
            assert_is_not_none(result)
        except:
            # FIXME: The WebUI isn't linked to CERN SSO yet so this needs to be fixed once it is linked
            pass

        del_account_identity('ddmlab', IdentityType.SAML, root)
Beispiel #6
0
    def test_get_auth_token_saml_success(self):
        """AUTHENTICATION (CORE): SAML NameID (correct credentials)."""
        root = InternalAccount('root', **self.vo)
        try:
            add_account_identity('ddmlab',
                                 IdentityType.SAML,
                                 root,
                                 email='*****@*****.**')
        except Duplicate:
            pass  # might already exist, can skip

        result = get_auth_token_saml(account='root',
                                     saml_nameid='ddmlab',
                                     appid='test',
                                     ip='127.0.0.1',
                                     **self.vo)
        assert result is not None

        del_account_identity('ddmlab', IdentityType.SAML, root)
    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()
Beispiel #8
0
def saml_authentication(method, rendered_tpl):
    """
    Login with SAML

    :param method: method type, GET or POST
    :param rendered_tpl: page to be rendered
    """

    attribs = None
    token = None
    js_token = ""
    js_account = ""
    def_account = None
    accounts = None
    cookie_accounts = None
    rucio_ui_version = version.version_string()
    policy = config_get('policy', 'permission')

    # Initialize variables for sending SAML request
    SAML_PATH = join(dirname(__file__), 'saml/')
    request = ctx.env
    data = dict(input())
    req = prepare_webpy_request(request, data)
    auth = OneLogin_Saml2_Auth(req, custom_base_path=SAML_PATH)

    saml_user_data = cookies().get('saml-user-data')

    render = template.render(join(dirname(__file__), '../templates'))

    session_token = cookies().get('x-rucio-auth-token')
    validate_token = authentication.validate_auth_token(session_token)

    if method == "GET":
        # If user data is not present, redirect to IdP for authentication
        if not saml_user_data:
            return seeother(auth.login())

        # If user data is present and token is valid, render the required page
        elif validate_token:
            js_token = __to_js('token', session_token)
            js_account = __to_js('account', def_account)

            return render.base(js_token, js_account, rucio_ui_version, policy,
                               rendered_tpl)

        # If user data is present but token is not valid, create a new one
        saml_nameid = cookies().get('saml-nameid')
        accounts = identity.list_accounts_for_identity(saml_nameid, 'saml')

        cookie_accounts = accounts
        try:
            token = authentication.get_auth_token_saml(
                def_account, saml_nameid, 'webui',
                ctx.env.get('REMOTE_ADDR')).token

        except:
            return render.problem('Cannot get auth token')

        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)

        set_cookies(token, cookie_accounts, attribs)

        return render.base(js_token, js_account, rucio_ui_version, policy,
                           rendered_tpl)

    # If method is POST, check the received SAML response and redirect to home if valid
    auth.process_response()
    errors = auth.get_errors()
    if not errors:
        if auth.is_authenticated():
            setcookie('saml-user-data', value=auth.get_attributes(), path='/')
            setcookie('saml-session-index',
                      value=auth.get_session_index(),
                      path='/')
            setcookie('saml-nameid', value=auth.get_nameid(), path='/')
            saml_nameid = auth.get_nameid()

            accounts = identity.list_accounts_for_identity(saml_nameid, 'saml')
            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_saml(
                    def_account, saml_nameid, 'webui',
                    ctx.env.get('REMOTE_ADDR')).token

            except:
                return render.problem('Cannot get auth token')

            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)

            set_cookies(token, cookie_accounts, attribs)

            return seeother("/")

        return render.problem("Not authenticated")

    return render.problem("Error while processing SAML")