Пример #1
0
    def test_disconnect_when_more_than_one_associated_token_for_provider(self):
        self.trans.user = User(email=self.test_email, username=self.test_username)
        custos_authnz_token1 = CustosAuthnzToken(
            user=self.trans.user,
            external_user_id=self.test_user_id + "1",
            provider=self.custos_authnz.config['provider'],
            access_token=self.test_access_token,
            id_token=self.test_id_token,
            refresh_token=self.test_refresh_token,
            expiration_time=datetime.now() + timedelta(seconds=self.test_refresh_expires_in),
            refresh_expiration_time=datetime.now() + timedelta(seconds=self.test_refresh_expires_in),
        )
        custos_authnz_token2 = CustosAuthnzToken(
            user=self.trans.user,
            external_user_id=self.test_user_id + "2",
            provider=self.custos_authnz.config['provider'],
            access_token=self.test_access_token,
            id_token=self.test_id_token,
            refresh_token=self.test_refresh_token,
            expiration_time=datetime.now() + timedelta(seconds=self.test_refresh_expires_in),
            refresh_expiration_time=datetime.now() + timedelta(seconds=self.test_refresh_expires_in),
        )
        self.trans.user.custos_auth = [custos_authnz_token1, custos_authnz_token2]

        success, message, redirect_uri = self.custos_authnz.disconnect("Custos", self.trans, "/")

        self.assertEqual(0, len(self.trans.sa_session.deleted))
        self.assertFalse(self.trans.sa_session.flush_called)
        self.assertFalse(success)
        self.assertNotEqual("", message)
        self.assertIsNone(redirect_uri)
Пример #2
0
    def test_disconnect(self):
        custos_authnz_token = CustosAuthnzToken(
            user=User(email=self.test_email, username=self.test_username),
            external_user_id=self.test_user_id,
            provider=self.custos_authnz.config['provider'],
            access_token=self.test_access_token,
            id_token=self.test_id_token,
            refresh_token=self.test_refresh_token,
            expiration_time=datetime.now() +
            timedelta(seconds=self.test_refresh_expires_in),
            refresh_expiration_time=datetime.now() +
            timedelta(seconds=self.test_refresh_expires_in),
        )
        self.trans.user = custos_authnz_token.user
        self.trans.user.custos_auth = [custos_authnz_token]

        success, message, redirect_uri = self.custos_authnz.disconnect(
            "Custos", self.trans, "/")

        self.assertEqual(1, len(self.trans.sa_session.deleted))
        deleted_token = self.trans.sa_session.deleted[0]
        self.assertIs(custos_authnz_token, deleted_token)
        self.assertTrue(self.trans.sa_session.flush_called)
        self.assertTrue(success)
        self.assertEqual("", message)
        self.assertEqual("/", redirect_uri)
Пример #3
0
    def test_callback_galaxy_user_not_created_when_custos_authnz_token_exists(self):
        self.trans.set_cookie(value=self.test_state, name=custos_authnz.STATE_COOKIE_NAME)
        self.trans.set_cookie(value=self.test_nonce, name=custos_authnz.NONCE_COOKIE_NAME)
        old_access_token = "old-access-token"
        old_id_token = "old-id-token"
        old_refresh_token = "old-refresh-token"
        old_expiration_time = datetime.now() - timedelta(days=1)
        old_refresh_expiration_time = datetime.now() - timedelta(hours=3)
        existing_custos_authnz_token = CustosAuthnzToken(
            user=User(email=self.test_email, username=self.test_username),
            external_user_id=self.test_user_id,
            provider=self.custos_authnz.config['provider'],
            access_token=old_access_token,
            id_token=old_id_token,
            refresh_token=old_refresh_token,
            expiration_time=old_expiration_time,
            refresh_expiration_time=old_refresh_expiration_time,
        )

        self.trans.sa_session._query.custos_authnz_token = existing_custos_authnz_token

        self.assertIsNotNone(
            self.trans.sa_session.query(CustosAuthnzToken)
                .filter_by(external_user_id=self.test_user_id,
                           provider=self.custos_authnz.config['provider'])
                .one_or_none()
        ),
        self.assertEqual(0, len(self.trans.sa_session.items))
        login_redirect_url, user = self.custos_authnz.callback(
            state_token="xxx",
            authz_code=self.test_code, trans=self.trans,
            login_redirect_url="http://localhost:8000/")
        self.assertTrue(self._fetch_token_called)
        self.assertTrue(self._get_userinfo_called)
        # Make sure query was called with correct parameters
        self.assertEqual(self.test_user_id, self.trans.sa_session._query.external_user_id)
        self.assertEqual(self.custos_authnz.config['provider'], self.trans.sa_session._query.provider)
        self.assertEqual(1, len(self.trans.sa_session.items), "Session has updated CustosAuthnzToken")
        session_custos_authnz_token = self.trans.sa_session.items[0]
        self.assertIsInstance(session_custos_authnz_token, CustosAuthnzToken)
        self.assertIs(existing_custos_authnz_token, session_custos_authnz_token, "existing CustosAuthnzToken should be updated")
        # Verify both that existing CustosAuthnzToken has the correct values and different values than before
        self.assertEqual(self.test_access_token, session_custos_authnz_token.access_token)
        self.assertNotEqual(old_access_token, session_custos_authnz_token.access_token)
        self.assertEqual(self.test_id_token, session_custos_authnz_token.id_token)
        self.assertNotEqual(old_id_token, session_custos_authnz_token.id_token)
        self.assertEqual(self.test_refresh_token, session_custos_authnz_token.refresh_token)
        self.assertNotEqual(old_refresh_token, session_custos_authnz_token.refresh_token)
        expected_expiration_time = datetime.now() + timedelta(seconds=self.test_expires_in)
        expiration_timedelta = expected_expiration_time - session_custos_authnz_token.expiration_time
        self.assertTrue(expiration_timedelta.total_seconds() < 1)
        self.assertNotEqual(old_expiration_time, session_custos_authnz_token.expiration_time)
        expected_refresh_expiration_time = datetime.now() + timedelta(seconds=self.test_refresh_expires_in)
        refresh_expiration_timedelta = expected_refresh_expiration_time - session_custos_authnz_token.refresh_expiration_time
        self.assertTrue(refresh_expiration_timedelta.total_seconds() < 1)
        self.assertNotEqual(old_refresh_expiration_time, session_custos_authnz_token.refresh_expiration_time)
        self.assertTrue(self.trans.sa_session.flush_called)
Пример #4
0
    def callback(self, state_token, authz_code, trans, login_redirect_url):
        # Take state value to validate from token. OAuth2Session.fetch_token
        # will validate that the state query parameter value on the URL matches
        # this value.
        state_cookie = trans.get_cookie(name=STATE_COOKIE_NAME)
        oauth2_session = self._create_oauth2_session(state=state_cookie)
        token = self._fetch_token(oauth2_session, trans)
        log.debug("token={}".format(json.dumps(token, indent=True)))
        access_token = token['access_token']
        id_token = token['id_token']
        refresh_token = token[
            'refresh_token'] if 'refresh_token' in token else None
        expiration_time = datetime.now() + timedelta(
            seconds=token['expires_in'])
        refresh_expiration_time = (
            datetime.now() + timedelta(seconds=token['refresh_expires_in'])
        ) if 'refresh_expires_in' in token else None

        # Get nonce from token['id_token'] and validate. 'nonce' in the
        # id_token is a hash of the nonce stored in the NONCE_COOKIE_NAME
        # cookie.
        id_token_decoded = jwt.decode(id_token, verify=False)
        nonce_hash = id_token_decoded['nonce']
        self._validate_nonce(trans, nonce_hash)

        # Get userinfo and lookup/create Galaxy user record
        userinfo = self._get_userinfo(oauth2_session)
        log.debug("userinfo={}".format(json.dumps(userinfo, indent=True)))
        username = userinfo['preferred_username']
        email = userinfo['email']
        user_id = userinfo['sub']

        # Create or update custos_authnz_token record
        custos_authnz_token = self._get_custos_authnz_token(
            trans.sa_session, user_id, self.config['provider'])
        if custos_authnz_token is None:
            user = self._get_current_user(trans)
            if not user:
                user = self._create_user(trans.sa_session, username, email)
            custos_authnz_token = CustosAuthnzToken(
                user=user,
                external_user_id=user_id,
                provider=self.config['provider'],
                access_token=access_token,
                id_token=id_token,
                refresh_token=refresh_token,
                expiration_time=expiration_time,
                refresh_expiration_time=refresh_expiration_time)
        else:
            custos_authnz_token.access_token = access_token
            custos_authnz_token.id_token = id_token
            custos_authnz_token.refresh_token = refresh_token
            custos_authnz_token.expiration_time = expiration_time
            custos_authnz_token.refresh_expiration_time = refresh_expiration_time
        trans.sa_session.add(custos_authnz_token)
        trans.sa_session.flush()
        return login_redirect_url, custos_authnz_token.user
Пример #5
0
    def create_user(self, token, trans, login_redirect_url):
        token_dict = json.loads(token)

        access_token = token_dict['access_token']
        id_token = token_dict['id_token']
        refresh_token = token_dict[
            'refresh_token'] if 'refresh_token' in token_dict else None
        expiration_time = datetime.now() + timedelta(seconds=token_dict.get(
            'expires_in',
            3600))  # might be a problem cause times no long valid
        refresh_expiration_time = (
            datetime.now() +
            timedelta(seconds=token_dict['refresh_expires_in'])
        ) if 'refresh_expires_in' in token_dict else None

        # Get nonce from token['id_token'] and validate. 'nonce' in the
        # id_token is a hash of the nonce stored in the NONCE_COOKIE_NAME
        # cookie.
        userinfo = self._decode_token_no_signature(id_token)

        # Get userinfo and create Galaxy user record
        email = userinfo['email']
        # Check if username if already taken
        username = userinfo.get('preferred_username',
                                self._generate_username(trans, email))
        user_id = userinfo['sub']

        user = trans.app.user_manager.create(email=email, username=username)
        if trans.app.config.user_activation_on:
            trans.app.user_manager.send_activation_email(
                trans, email, username)

        custos_authnz_token = CustosAuthnzToken(
            user=user,
            external_user_id=user_id,
            provider=self.config['provider'],
            access_token=access_token,
            id_token=id_token,
            refresh_token=refresh_token,
            expiration_time=expiration_time,
            refresh_expiration_time=refresh_expiration_time)

        trans.sa_session.add(user)
        trans.sa_session.add(custos_authnz_token)
        trans.sa_session.flush()
        return login_redirect_url, user
Пример #6
0
    def test_callback_verify_with_state_cookie(self):
        """Verify that state from cookie is passed to OAuth2Session constructor."""
        self.trans.set_cookie(value=self.test_state,
                              name=custos_authnz.STATE_COOKIE_NAME)
        self.trans.set_cookie(value=self.test_nonce,
                              name=custos_authnz.NONCE_COOKIE_NAME)
        old_access_token = "old-access-token"
        old_id_token = "old-id-token"
        old_refresh_token = "old-refresh-token"
        old_expiration_time = datetime.now() - timedelta(days=1)
        old_refresh_expiration_time = datetime.now() - timedelta(hours=3)
        existing_custos_authnz_token = CustosAuthnzToken(
            user=User(email=self.test_email, username=self.test_username),
            external_user_id=self.test_user_id,
            provider=self.custos_authnz.config['provider'],
            access_token=old_access_token,
            id_token=old_id_token,
            refresh_token=old_refresh_token,
            expiration_time=old_expiration_time,
            refresh_expiration_time=old_refresh_expiration_time,
        )

        self.trans.sa_session._query.custos_authnz_token = existing_custos_authnz_token
        self.assertIsNotNone(
            self.trans.sa_session.query(CustosAuthnzToken).filter_by(
                external_user_id=self.test_user_id,
                provider=self.custos_authnz.config['provider']).one_or_none())
        self.trans.sa_session._query.user = User(email=self.test_email,
                                                 username=self.test_username)

        # Mock _create_oauth2_session to make sure it is created with cookie state token
        self.mock_create_oauth2_session(self.custos_authnz)

        # Intentionally passing a bad state_token to make sure that code under
        # test uses the state cookie instead when creating the OAuth2Session
        login_redirect_url, user = self.custos_authnz.callback(
            state_token="xxx",
            authz_code=self.test_code,
            trans=self.trans,
            login_redirect_url="http://localhost:8000/")
        self.assertTrue(self._create_oauth2_session_called)
        self.assertTrue(self._fetch_token_called)
        self.assertTrue(self._get_userinfo_called)
        self.assertEqual(login_redirect_url, "http://localhost:8000/")
        self.assertIsNotNone(user)
Пример #7
0
    def callback(self, state_token, authz_code, trans, login_redirect_url):
        # Take state value to validate from token. OAuth2Session.fetch_token
        # will validate that the state query parameter value on the URL matches
        # this value.
        state_cookie = trans.get_cookie(name=STATE_COOKIE_NAME)
        oauth2_session = self._create_oauth2_session(state=state_cookie)
        token = self._fetch_token(oauth2_session, trans)
        log.debug("token={}".format(json.dumps(token, indent=True)))
        access_token = token['access_token']
        id_token = token['id_token']
        refresh_token = token[
            'refresh_token'] if 'refresh_token' in token else None
        expiration_time = datetime.now() + timedelta(
            seconds=token.get('expires_in', 3600))
        refresh_expiration_time = (
            datetime.now() + timedelta(seconds=token['refresh_expires_in'])
        ) if 'refresh_expires_in' in token else None

        # Get nonce from token['id_token'] and validate. 'nonce' in the
        # id_token is a hash of the nonce stored in the NONCE_COOKIE_NAME
        # cookie.
        id_token_decoded = jwt.decode(id_token, verify=False)
        nonce_hash = id_token_decoded['nonce']
        self._validate_nonce(trans, nonce_hash)

        # Get userinfo and lookup/create Galaxy user record
        if id_token_decoded.get('email', None):
            userinfo = id_token_decoded
        else:
            userinfo = self._get_userinfo(oauth2_session)
        log.debug("userinfo={}".format(json.dumps(userinfo, indent=True)))
        email = userinfo['email']
        # Check if username if already taken
        username = userinfo.get('preferred_username',
                                self._generate_username(trans, email))
        user_id = userinfo['sub']

        # Create or update custos_authnz_token record
        custos_authnz_token = self._get_custos_authnz_token(
            trans.sa_session, user_id, self.config['provider'])
        if custos_authnz_token is None:
            user = trans.user
            if not user:
                existing_user = trans.sa_session.query(User).filter_by(
                    email=email).first()
                if existing_user:
                    # If there is only a single external authentication
                    # provider in use, trust the user provided and
                    # automatically associate.
                    # TODO: Future work will expand on this and provide an
                    # interface for when there are multiple auth providers
                    # allowing explicit authenticated association.
                    if (trans.app.config.enable_oidc
                            and len(trans.app.config.oidc) == 1 and len(
                                trans.app.auth_manager.authenticators) == 0):
                        user = existing_user
                    else:
                        message = 'There already exists a user this email.  To associate this external login, you must first be logged in as that existing account.'
                        log.exception(message)
                        raise exceptions.AuthenticationFailed(message)
                else:
                    user = trans.app.user_manager.create(email=email,
                                                         username=username)
                    trans.sa_session.add(user)
                    trans.sa_session.flush()
            custos_authnz_token = CustosAuthnzToken(
                user=user,
                external_user_id=user_id,
                provider=self.config['provider'],
                access_token=access_token,
                id_token=id_token,
                refresh_token=refresh_token,
                expiration_time=expiration_time,
                refresh_expiration_time=refresh_expiration_time)
        else:
            custos_authnz_token.access_token = access_token
            custos_authnz_token.id_token = id_token
            custos_authnz_token.refresh_token = refresh_token
            custos_authnz_token.expiration_time = expiration_time
            custos_authnz_token.refresh_expiration_time = refresh_expiration_time
        trans.sa_session.add(custos_authnz_token)
        trans.sa_session.flush()
        return login_redirect_url, custos_authnz_token.user