示例#1
0
class TestTokenValidation(AuthMiddlewareTest):

    @mock.patch.object(
        Token, 'get',
        mock.Mock(return_value=TokenDB(id=OBJ_ID, user=USER, token=TOKEN, expiry=FUTURE)))
    def test_token_validation_token_in_headers(self):
        response = self.app.get('/v1/actions', headers={'X-Auth-Token': TOKEN},
                                expect_errors=False)
        self.assertEqual(response.status_int, 200)

    @mock.patch.object(
        Token, 'get',
        mock.Mock(return_value=TokenDB(id=OBJ_ID, user=USER, token=TOKEN, expiry=FUTURE)))
    def test_token_validation_token_in_query_params(self):
        response = self.app.get('/v1/actions?x-auth-token=%s' % (TOKEN), expect_errors=False)
        self.assertEqual(response.status_int, 200)

    @mock.patch.object(
        Token, 'get',
        mock.Mock(return_value=TokenDB(id=OBJ_ID, user=USER, token=TOKEN, expiry=PAST)))
    def test_token_expired(self):
        response = self.app.get('/v1/actions', headers={'X-Auth-Token': TOKEN},
                                expect_errors=True)
        self.assertEqual(response.status_int, 401)

    @mock.patch.object(
        Token, 'get', mock.MagicMock(side_effect=TokenNotFoundError()))
    def test_token_not_found(self):
        response = self.app.get('/v1/actions', headers={'X-Auth-Token': TOKEN},
                                expect_errors=True)
        self.assertEqual(response.status_int, 401)

    def test_token_not_provided(self):
        response = self.app.get('/v1/actions', expect_errors=True)
        self.assertEqual(response.status_int, 401)
示例#2
0
class TestTokenBasedAuth(FunctionalTest):

    enable_auth = True

    @mock.patch.object(Token, 'get',
                       mock.Mock(return_value=TokenDB(
                           id=OBJ_ID, user=USER, token=TOKEN, expiry=FUTURE)))
    def test_token_validation_token_in_headers(self):
        response = self.app.get('/v1/actions',
                                headers={'X-Auth-Token': TOKEN},
                                expect_errors=False)
        self.assertTrue('application/json' in response.headers['content-type'])
        self.assertEqual(response.status_int, 200)

    @mock.patch.object(Token, 'get',
                       mock.Mock(return_value=TokenDB(
                           id=OBJ_ID, user=USER, token=TOKEN, expiry=FUTURE)))
    def test_token_validation_token_in_query_params(self):
        response = self.app.get('/v1/actions?x-auth-token=%s' % (TOKEN),
                                expect_errors=False)
        self.assertTrue('application/json' in response.headers['content-type'])
        self.assertEqual(response.status_int, 200)

    @mock.patch.object(Token, 'get',
                       mock.Mock(return_value=TokenDB(
                           id=OBJ_ID, user=USER, token=TOKEN, expiry=PAST)))
    def test_token_expired(self):
        response = self.app.get('/v1/actions',
                                headers={'X-Auth-Token': TOKEN},
                                expect_errors=True)
        self.assertTrue('application/json' in response.headers['content-type'])
        self.assertEqual(response.status_int, 401)

    @mock.patch.object(Token, 'get',
                       mock.MagicMock(side_effect=TokenNotFoundError()))
    def test_token_not_found(self):
        response = self.app.get('/v1/actions',
                                headers={'X-Auth-Token': TOKEN},
                                expect_errors=True)
        self.assertTrue('application/json' in response.headers['content-type'])
        self.assertEqual(response.status_int, 401)

    def test_token_not_provided(self):
        response = self.app.get('/v1/actions', expect_errors=True)
        self.assertTrue('application/json' in response.headers['content-type'])
        self.assertEqual(response.status_int, 401)
示例#3
0
class TestApiKeyBasedAuth(FunctionalTest):

    enable_auth = True

    apikey1 = None
    apikey_disabled = None

    @classmethod
    def setUpClass(cls):
        super(TestApiKeyBasedAuth, cls).setUpClass()
        models = FixturesLoader().save_fixtures_to_db(fixtures_pack=FIXTURES_PACK,
                                                      fixtures_dict=TEST_MODELS)
        cls.apikey1 = models['apikeys']['apikey1.yaml']
        cls.apikey_disabled = models['apikeys']['apikey_disabled.yaml']

    @mock.patch.object(User, 'get_by_name', mock.Mock(return_value=UserDB(name='bill')))
    def test_apikey_validation_apikey_in_headers(self):
        response = self.app.get('/v1/actions', headers={'St2-Api-key': KEY1_KEY},
                                expect_errors=False)
        self.assertTrue('application/json' in response.headers['content-type'])
        self.assertEqual(response.status_int, 200)

    @mock.patch.object(User, 'get_by_name', mock.Mock(return_value=UserDB(name='bill')))
    def test_apikey_validation_apikey_in_query_params(self):
        response = self.app.get('/v1/actions?st2-api-key=%s' % (KEY1_KEY), expect_errors=False)
        self.assertTrue('application/json' in response.headers['content-type'])
        self.assertEqual(response.status_int, 200)

    def test_apikey_disabled(self):
        response = self.app.get('/v1/actions', headers={'St2-Api-key': DISABLED_KEY},
                                expect_errors=True)
        self.assertTrue('application/json' in response.headers['content-type'])
        self.assertEqual(response.status_int, 401)
        self.assertEqual(response.json_body['faultstring'], 'Unauthorized - API key is disabled.')

    def test_apikey_not_found(self):
        response = self.app.get('/v1/actions', headers={'St2-Api-key': 'UNKNOWN'},
                                expect_errors=True)
        self.assertTrue('application/json' in response.headers['content-type'])
        self.assertEqual(response.status_int, 401)
        self.assertRegexpMatches(response.json_body['faultstring'],
                                 '^Unauthorized - ApiKey with key_hash=([a-zA-Z0-9]+) not found.$')

    @mock.patch.object(
        Token, 'get',
        mock.Mock(return_value=TokenDB(id=OBJ_ID, user=USER, token=TOKEN, expiry=FUTURE)))
    @mock.patch.object(
        ApiKey, 'get',
        mock.Mock(return_value=ApiKeyDB(user=USER, key_hash=KEY1_KEY, enabled=True)))
    @mock.patch.object(User, 'get_by_name', mock.Mock(return_value=USER_DB))
    def test_multiple_auth_sources(self):
        response = self.app.get('/v1/actions',
                                headers={'X-Auth-Token': TOKEN, 'St2-Api-key': KEY1_KEY},
                                expect_errors=True)
        self.assertTrue('application/json' in response.headers['content-type'])
        self.assertEqual(response.status_int, 401)
        self.assertEqual(response.json_body['faultstring'],
                         'Unauthorized - Only one of Token or API key expected.')
示例#4
0
def create_token(username, ttl=None, metadata=None, add_missing_user=True):
    """
    :param username: Username of the user to create the token for. If the account for this user
                     doesn't exist yet it will be created.
    :type username: ``str``

    :param ttl: Token TTL (in seconds).
    :type ttl: ``int``

    :param metadata: Optional metadata to associate with the token.
    :type metadata: ``dict``

    :param add_missing_user: Add the user given by `username` if they don't exist
    :type  add_missing_user: ``bool``
    """

    if ttl:
        if ttl > cfg.CONF.auth.token_ttl:
            msg = 'TTL specified %s is greater than max allowed %s.' % (
                ttl, cfg.CONF.auth.token_ttl)
            raise TTLTooLargeException(msg)
    else:
        ttl = cfg.CONF.auth.token_ttl

    if username:
        try:
            User.get_by_name(username)
        except:
            if add_missing_user:
                user_db = UserDB(name=username)
                User.add_or_update(user_db)

                extra = {'username': username, 'user': user_db}
                LOG.audit('Registered new user "%s".' % (username),
                          extra=extra)
            else:
                raise UserNotFoundError()

    token = uuid.uuid4().hex
    expiry = date_utils.get_datetime_utc_now() + datetime.timedelta(
        seconds=ttl)
    token = TokenDB(user=username,
                    token=token,
                    expiry=expiry,
                    metadata=metadata)
    Token.add_or_update(token)

    username_string = username if username else 'an anonymous user'
    token_expire_string = isotime.format(expiry, offset=False)
    extra = {'username': username, 'token_expiration': token_expire_string}

    LOG.audit('Access granted to "%s" with the token set to expire at "%s".' %
              (username_string, token_expire_string),
              extra=extra)

    return token
示例#5
0
    def test_cache_auth_token_invalid_permissions(self):
        shell = Shell()
        username = '******'

        cached_token_path = shell._get_cached_token_path_for_user(
            username=username)
        expiry = datetime.datetime.utcnow() + datetime.timedelta(seconds=30)

        token_db = TokenDB(user=username, token='fyeah', expiry=expiry)

        cached_token_path = shell._get_cached_token_path_for_user(
            username=username)
        data = {
            'token': 'yayvalid',
            'expire_timestamp': (int(time.time()) + 20)
        }
        with open(cached_token_path, 'w') as fp:
            fp.write(json.dumps(data))

        # 1. Current user has no write access to the parent directory
        os.chmod(self._mock_config_directory_path, 0o000)

        shell.LOG = mock.Mock()
        shell._cache_auth_token(token_obj=token_db)

        self.assertEqual(shell.LOG.warn.call_count, 1)
        log_message = shell.LOG.warn.call_args[0][0]

        expected_msg = (
            'Unable to write token to .*? doesn\'t have write access to the parent '
            'directory')
        self.assertRegexpMatches(log_message, expected_msg)

        # 2. Current user has no write access to the cached token file
        os.chmod(self._mock_config_directory_path, 0o777)  # nosec
        os.chmod(cached_token_path, 0o000)

        shell.LOG = mock.Mock()
        shell._cache_auth_token(token_obj=token_db)

        self.assertEqual(shell.LOG.warn.call_count, 1)
        log_message = shell.LOG.warn.call_args[0][0]

        expected_msg = (
            'Unable to write token to .*? doesn\'t have write access to this file'
        )
        self.assertRegexpMatches(log_message, expected_msg)
示例#6
0
    def test_cache_auth_token_success(self):
        client = Client()
        shell = Shell()
        username = '******'
        password = '******'
        expiry = datetime.datetime.utcnow() + datetime.timedelta(seconds=30)

        result = shell._get_cached_auth_token(client=client, username=username,
                                              password=password)
        self.assertEqual(result, None)

        token_db = TokenDB(user=username, token='fyeah', expiry=expiry)
        shell._cache_auth_token(token_obj=token_db)

        result = shell._get_cached_auth_token(client=client, username=username,
                                              password=password)
        self.assertEqual(result, 'fyeah')
示例#7
0
        }
    }
}

LIVE_ACTION_1 = {
    'action': 'sixpack.st2.dummy.action1',
    'parameters': {
        'hosts': 'localhost',
        'cmd': 'uname -a',
        'd': SUPER_SECRET_PARAMETER
    }
}

NOW = date_utils.get_datetime_utc_now()
EXPIRY = NOW + datetime.timedelta(seconds=300)
SYS_TOKEN = TokenDB(id=bson.ObjectId(), user='******', token=uuid.uuid4().hex, expiry=EXPIRY)
USR_TOKEN = TokenDB(id=bson.ObjectId(), user='******', token=uuid.uuid4().hex, expiry=EXPIRY)

FIXTURES_PACK = 'generic'
FIXTURES = {
    'users': ['system_user.yaml', 'token_user.yaml']
}


def mock_get_token(*args, **kwargs):
    if args[0] == SYS_TOKEN.token:
        return SYS_TOKEN
    return USR_TOKEN


@mock.patch.object(PoolPublisher, 'publish', mock.MagicMock())
示例#8
0
        "cmd": "uname -a",
        "d": SUPER_SECRET_PARAMETER,
    },
}
LIVE_ACTION_DEFAULT_ENCRYPT = {
    "action": "starterpack.st2.dummy.default_encrypted_value",
}

# NOTE: We use a longer expiry time because this variable is initialized on module import (aka
# when nosetests or similar imports this module before running the tests.
# Depending on when the import happens and when the tests actually run, token could already expire
# by that time and the tests would fail.
NOW = date_utils.get_datetime_utc_now()
EXPIRY = NOW + datetime.timedelta(seconds=1000)
SYS_TOKEN = TokenDB(id=bson.ObjectId(),
                    user="******",
                    token=uuid.uuid4().hex,
                    expiry=EXPIRY)
USR_TOKEN = TokenDB(id=bson.ObjectId(),
                    user="******",
                    token=uuid.uuid4().hex,
                    expiry=EXPIRY)

FIXTURES_PACK = "generic"
FIXTURES = {"users": ["system_user.yaml", "token_user.yaml"]}

# These parameters are used for the tests of getting value from datastore and decrypting it at
# Jinja expression in a action metadata definition.
TEST_USER = UserDB(name="user1")
TEST_TOKEN = TokenDB(id=bson.ObjectId(),
                     user=TEST_USER,
                     token=uuid.uuid4().hex,
示例#9
0
class TestTokenBasedAuth(FunctionalTest):

    enable_auth = True

    @mock.patch.object(
        Token,
        "get",
        mock.Mock(
            return_value=TokenDB(id=OBJ_ID, user=USER, token=TOKEN, expiry=FUTURE)
        ),
    )
    @mock.patch.object(User, "get_by_name", mock.Mock(return_value=USER_DB))
    def test_token_validation_token_in_headers(self):
        response = self.app.get(
            "/v1/actions", headers={"X-Auth-Token": TOKEN}, expect_errors=False
        )
        self.assertIn("application/json", response.headers["content-type"])
        self.assertEqual(response.status_int, 200)

    @mock.patch.object(
        Token,
        "get",
        mock.Mock(
            return_value=TokenDB(id=OBJ_ID, user=USER, token=TOKEN, expiry=FUTURE)
        ),
    )
    @mock.patch.object(User, "get_by_name", mock.Mock(return_value=USER_DB))
    def test_token_validation_token_in_query_params(self):
        response = self.app.get(
            "/v1/actions?x-auth-token=%s" % (TOKEN), expect_errors=False
        )
        self.assertIn("application/json", response.headers["content-type"])
        self.assertEqual(response.status_int, 200)

    @mock.patch.object(
        Token,
        "get",
        mock.Mock(
            return_value=TokenDB(id=OBJ_ID, user=USER, token=TOKEN, expiry=FUTURE)
        ),
    )
    @mock.patch.object(User, "get_by_name", mock.Mock(return_value=USER_DB))
    def test_token_validation_token_in_cookies(self):
        response = self.app.get(
            "/v1/actions", headers={"X-Auth-Token": TOKEN}, expect_errors=False
        )
        self.assertIn("application/json", response.headers["content-type"])
        self.assertEqual(response.status_int, 200)

        with mock.patch.object(self.app.cookiejar, "clear", return_value=None):
            response = self.app.get("/v1/actions", expect_errors=False)
        self.assertIn("application/json", response.headers["content-type"])
        self.assertEqual(response.status_int, 200)

    @mock.patch.object(
        Token,
        "get",
        mock.Mock(return_value=TokenDB(id=OBJ_ID, user=USER, token=TOKEN, expiry=PAST)),
    )
    def test_token_expired(self):
        response = self.app.get(
            "/v1/actions", headers={"X-Auth-Token": TOKEN}, expect_errors=True
        )
        self.assertIn("application/json", response.headers["content-type"])
        self.assertEqual(response.status_int, 401)

    @mock.patch.object(Token, "get", mock.MagicMock(side_effect=TokenNotFoundError()))
    def test_token_not_found(self):
        response = self.app.get(
            "/v1/actions", headers={"X-Auth-Token": TOKEN}, expect_errors=True
        )
        self.assertIn("application/json", response.headers["content-type"])
        self.assertEqual(response.status_int, 401)

    def test_token_not_provided(self):
        response = self.app.get("/v1/actions", expect_errors=True)
        self.assertIn("application/json", response.headers["content-type"])
        self.assertEqual(response.status_int, 401)
示例#10
0
class TestApiKeyBasedAuth(FunctionalTest):

    enable_auth = True

    apikey1 = None
    apikey_disabled = None

    @classmethod
    def setUpClass(cls):
        super(TestApiKeyBasedAuth, cls).setUpClass()
        models = FixturesLoader().save_fixtures_to_db(
            fixtures_pack=FIXTURES_PACK, fixtures_dict=TEST_MODELS
        )
        cls.apikey1 = models["apikeys"]["apikey1.yaml"]
        cls.apikey_disabled = models["apikeys"]["apikey_disabled.yaml"]

    @mock.patch.object(User, "get_by_name", mock.Mock(return_value=UserDB(name="bill")))
    def test_apikey_validation_apikey_in_headers(self):
        response = self.app.get(
            "/v1/actions", headers={"St2-Api-key": KEY1_KEY}, expect_errors=False
        )
        self.assertIn("application/json", response.headers["content-type"])
        self.assertEqual(response.status_int, 200)

    @mock.patch.object(User, "get_by_name", mock.Mock(return_value=UserDB(name="bill")))
    def test_apikey_validation_apikey_in_query_params(self):
        response = self.app.get(
            "/v1/actions?st2-api-key=%s" % (KEY1_KEY), expect_errors=False
        )
        self.assertIn("application/json", response.headers["content-type"])
        self.assertEqual(response.status_int, 200)

    @mock.patch.object(User, "get_by_name", mock.Mock(return_value=UserDB(name="bill")))
    def test_apikey_validation_apikey_in_cookies(self):
        response = self.app.get(
            "/v1/actions", headers={"St2-Api-key": KEY1_KEY}, expect_errors=False
        )
        self.assertIn("application/json", response.headers["content-type"])
        self.assertEqual(response.status_int, 200)

        with mock.patch.object(self.app.cookiejar, "clear", return_value=None):
            response = self.app.get("/v1/actions", expect_errors=True)
        self.assertEqual(response.status_int, 401)
        self.assertEqual(
            response.json_body["faultstring"],
            "Unauthorized - One of Token or API key required.",
        )

    def test_apikey_disabled(self):
        response = self.app.get(
            "/v1/actions", headers={"St2-Api-key": DISABLED_KEY}, expect_errors=True
        )
        self.assertIn("application/json", response.headers["content-type"])
        self.assertEqual(response.status_int, 401)
        self.assertEqual(
            response.json_body["faultstring"], "Unauthorized - API key is disabled."
        )

    def test_apikey_not_found(self):
        response = self.app.get(
            "/v1/actions", headers={"St2-Api-key": "UNKNOWN"}, expect_errors=True
        )
        self.assertIn("application/json", response.headers["content-type"])
        self.assertEqual(response.status_int, 401)
        self.assertRegexpMatches(
            response.json_body["faultstring"],
            "^Unauthorized - ApiKey with key_hash=([a-zA-Z0-9]+) not found.$",
        )

    @mock.patch.object(
        Token,
        "get",
        mock.Mock(
            return_value=TokenDB(id=OBJ_ID, user=USER, token=TOKEN, expiry=FUTURE)
        ),
    )
    @mock.patch.object(
        ApiKey,
        "get",
        mock.Mock(return_value=ApiKeyDB(user=USER, key_hash=KEY1_KEY, enabled=True)),
    )
    @mock.patch.object(User, "get_by_name", mock.Mock(return_value=USER_DB))
    def test_multiple_auth_sources(self):
        response = self.app.get(
            "/v1/actions",
            headers={"X-Auth-Token": TOKEN, "St2-Api-key": KEY1_KEY},
            expect_errors=True,
        )
        self.assertIn("application/json", response.headers["content-type"])
        self.assertEqual(response.status_int, 200)
示例#11
0
class TestTokenController(FunctionalTest):

    @classmethod
    def setUpClass(cls, **kwargs):
        kwargs['extra_environ'] = {
            'REMOTE_USER': USERNAME
        }
        super(TestTokenController, cls).setUpClass(**kwargs)

    def test_token_model(self):
        dt = date_utils.get_datetime_utc_now()
        tk1 = TokenAPI(user='******', token=uuid.uuid4().hex,
                       expiry=isotime.format(dt, offset=False))
        tkdb1 = TokenAPI.to_model(tk1)
        self.assertIsNotNone(tkdb1)
        self.assertIsInstance(tkdb1, TokenDB)
        self.assertEqual(tkdb1.user, tk1.user)
        self.assertEqual(tkdb1.token, tk1.token)
        self.assertEqual(tkdb1.expiry, isotime.parse(tk1.expiry))
        tkdb2 = Token.add_or_update(tkdb1)
        self.assertEqual(tkdb1, tkdb2)
        self.assertIsNotNone(tkdb2.id)
        tk2 = TokenAPI.from_model(tkdb2)
        self.assertEqual(tk2.user, tk1.user)
        self.assertEqual(tk2.token, tk1.token)
        self.assertEqual(tk2.expiry, tk1.expiry)

    def test_token_model_null_token(self):
        dt = date_utils.get_datetime_utc_now()
        tk = TokenAPI(user='******', token=None, expiry=isotime.format(dt))
        self.assertRaises(ValueError, Token.add_or_update, TokenAPI.to_model(tk))

    def test_token_model_null_user(self):
        dt = date_utils.get_datetime_utc_now()
        tk = TokenAPI(user=None, token=uuid.uuid4().hex, expiry=isotime.format(dt))
        self.assertRaises(ValueError, Token.add_or_update, TokenAPI.to_model(tk))

    def test_token_model_null_expiry(self):
        tk = TokenAPI(user='******', token=uuid.uuid4().hex, expiry=None)
        self.assertRaises(ValueError, Token.add_or_update, TokenAPI.to_model(tk))

    def _test_token_post(self, path=TOKEN_V1_PATH):
        ttl = cfg.CONF.auth.token_ttl
        timestamp = date_utils.get_datetime_utc_now()
        response = self.app.post_json(path, {}, expect_errors=False)
        expected_expiry = date_utils.get_datetime_utc_now() + datetime.timedelta(seconds=ttl)
        expected_expiry = date_utils.add_utc_tz(expected_expiry)
        self.assertEqual(response.status_int, 201)
        self.assertIsNotNone(response.json['token'])
        self.assertEqual(response.json['user'], USERNAME)
        actual_expiry = isotime.parse(response.json['expiry'])
        self.assertLess(timestamp, actual_expiry)
        self.assertLess(actual_expiry, expected_expiry)
        return response

    def test_token_post_unauthorized(self):
        response = self.app.post_json(TOKEN_V1_PATH, {}, expect_errors=True, extra_environ={
            'REMOTE_USER': ''
        })
        self.assertEqual(response.status_int, 401)

    @mock.patch.object(
        User, 'get_by_name',
        mock.MagicMock(side_effect=Exception()))
    @mock.patch.object(
        User, 'add_or_update',
        mock.Mock(return_value=UserDB(name=USERNAME)))
    def test_token_post_new_user(self):
        self._test_token_post()

    @mock.patch.object(
        User, 'get_by_name',
        mock.MagicMock(return_value=UserDB(name=USERNAME)))
    def test_token_post_existing_user(self):
        self._test_token_post()

    @mock.patch.object(
        User, 'get_by_name',
        mock.MagicMock(return_value=UserDB(name=USERNAME)))
    def test_token_post_success_x_api_url_header_value(self):
        # auth.api_url option is explicitly set
        cfg.CONF.set_override('api_url', override='https://example.com', group='auth')

        resp = self._test_token_post()
        self.assertEqual(resp.headers['X-API-URL'], 'https://example.com')

        # auth.api_url option is not set, url is inferred from listen host and port
        cfg.CONF.set_override('api_url', override=None, group='auth')

        resp = self._test_token_post()
        self.assertEqual(resp.headers['X-API-URL'], 'http://127.0.0.1:9101')

    @mock.patch.object(
        User, 'get_by_name',
        mock.MagicMock(return_value=UserDB(name=USERNAME)))
    def test_token_post_default_url_path(self):
        self._test_token_post(path=TOKEN_DEFAULT_PATH)

    @mock.patch.object(
        User, 'get_by_name',
        mock.MagicMock(return_value=UserDB(name=USERNAME)))
    def test_token_post_set_ttl(self):
        timestamp = date_utils.add_utc_tz(date_utils.get_datetime_utc_now())
        response = self.app.post_json(TOKEN_V1_PATH, {'ttl': 60}, expect_errors=False)
        expected_expiry = date_utils.get_datetime_utc_now() + datetime.timedelta(seconds=60)
        self.assertEqual(response.status_int, 201)
        actual_expiry = isotime.parse(response.json['expiry'])
        self.assertLess(timestamp, actual_expiry)
        self.assertLess(actual_expiry, expected_expiry)

    @mock.patch.object(
        User, 'get_by_name',
        mock.MagicMock(return_value=UserDB(name=USERNAME)))
    def test_token_post_no_data_in_body_text_plain_context_type_used(self):
        response = self.app.post(TOKEN_V1_PATH, expect_errors=False, content_type='text/plain')
        self.assertEqual(response.status_int, 201)

    @mock.patch.object(
        User, 'get_by_name',
        mock.MagicMock(return_value=UserDB(name=USERNAME)))
    def test_token_post_set_ttl_over_policy(self):
        ttl = cfg.CONF.auth.token_ttl
        response = self.app.post_json(TOKEN_V1_PATH, {'ttl': ttl + 60}, expect_errors=True)
        self.assertEqual(response.status_int, 400)
        message = 'TTL specified %s is greater than max allowed %s.' % (
                  ttl + 60, ttl
        )
        self.assertEqual(response.json['faultstring'], message)

    @mock.patch.object(
        User, 'get_by_name',
        mock.MagicMock(return_value=UserDB(name=USERNAME)))
    def test_token_post_set_bad_ttl(self):
        response = self.app.post_json(TOKEN_V1_PATH, {'ttl': -1}, expect_errors=True)
        self.assertEqual(response.status_int, 400)
        response = self.app.post_json(TOKEN_V1_PATH, {'ttl': 0}, expect_errors=True)
        self.assertEqual(response.status_int, 400)

    @mock.patch.object(
        User, 'get_by_name',
        mock.MagicMock(return_value=UserDB(name=USERNAME)))
    def test_token_get_unauthorized(self):
        # Create a new token.
        response = self.app.post_json(TOKEN_V1_PATH, expect_errors=False)

        # Verify the token. 401 is expected because an API key or token is not provided in header.
        data = {'token': str(response.json['token'])}
        response = self.app.post_json(TOKEN_VERIFY_PATH, data, expect_errors=True)
        self.assertEqual(response.status_int, 401)

    @mock.patch.object(
        User, 'get_by_name',
        mock.MagicMock(return_value=UserDB(name=USERNAME)))
    def test_token_get_unauthorized_bad_api_key(self):
        # Create a new token.
        response = self.app.post_json(TOKEN_V1_PATH, expect_errors=False)

        # Verify the token. 401 is expected because the API key is bad.
        headers = {'St2-Api-Key': 'foobar'}
        data = {'token': str(response.json['token'])}
        response = self.app.post_json(TOKEN_VERIFY_PATH, data, headers=headers, expect_errors=True)
        self.assertEqual(response.status_int, 401)

    @mock.patch.object(
        User, 'get_by_name',
        mock.MagicMock(return_value=UserDB(name=USERNAME)))
    def test_token_get_unauthorized_bad_token(self):
        # Create a new token.
        response = self.app.post_json(TOKEN_V1_PATH, expect_errors=False)

        # Verify the token. 401 is expected because the token is bad.
        headers = {'X-Auth-Token': 'foobar'}
        data = {'token': str(response.json['token'])}
        response = self.app.post_json(TOKEN_VERIFY_PATH, data, headers=headers, expect_errors=True)
        self.assertEqual(response.status_int, 401)

    @mock.patch.object(
        User, 'get_by_name',
        mock.MagicMock(return_value=UserDB(name=USERNAME)))
    @mock.patch.object(
        ApiKey, 'get',
        mock.MagicMock(return_value=ApiKeyDB(user=USERNAME, key_hash='foobar')))
    def test_token_get_auth_with_api_key(self):
        # Create a new token.
        response = self.app.post_json(TOKEN_V1_PATH, expect_errors=False)

        # Verify the token. Use an API key to authenticate with the st2 auth get token endpoint.
        headers = {'St2-Api-Key': 'foobar'}
        data = {'token': str(response.json['token'])}
        response = self.app.post_json(TOKEN_VERIFY_PATH, data, headers=headers, expect_errors=True)
        self.assertEqual(response.status_int, 200)
        self.assertTrue(response.json['valid'])

    @mock.patch.object(
        User, 'get_by_name',
        mock.MagicMock(return_value=UserDB(name=USERNAME)))
    def test_token_get_auth_with_token(self):
        # Create a new token.
        response = self.app.post_json(TOKEN_V1_PATH, {}, expect_errors=False)

        # Verify the token. Use a token to authenticate with the st2 auth get token endpoint.
        headers = {'X-Auth-Token': str(response.json['token'])}
        data = {'token': str(response.json['token'])}
        response = self.app.post_json(TOKEN_VERIFY_PATH, data, headers=headers, expect_errors=True)
        self.assertEqual(response.status_int, 200)
        self.assertTrue(response.json['valid'])

    @mock.patch.object(
        User, 'get_by_name',
        mock.MagicMock(return_value=UserDB(name=USERNAME)))
    @mock.patch.object(
        ApiKey, 'get',
        mock.MagicMock(return_value=ApiKeyDB(user=USERNAME, key_hash='foobar')))
    @mock.patch.object(
        Token, 'get',
        mock.MagicMock(
            return_value=TokenDB(
                user=USERNAME, token='12345',
                expiry=date_utils.get_datetime_utc_now() - datetime.timedelta(minutes=1))))
    def test_token_get_unauthorized_bad_ttl(self):
        # Verify the token. 400 is expected because the token has expired.
        headers = {'St2-Api-Key': 'foobar'}
        data = {'token': '12345'}
        response = self.app.post_json(TOKEN_VERIFY_PATH, data, headers=headers, expect_errors=False)
        self.assertEqual(response.status_int, 200)
        self.assertFalse(response.json['valid'])
示例#12
0
class TestTokenController(FunctionalTest):
    def setUp(self):
        super(TestTokenController, self).setUp()
        type(pecan.request).remote_user = mock.PropertyMock(
            return_value=USERNAME)

    def test_token_model(self):
        dt = date_utils.get_datetime_utc_now()
        tk1 = TokenAPI(user='******',
                       token=uuid.uuid4().hex,
                       expiry=isotime.format(dt, offset=False))
        tkdb1 = TokenAPI.to_model(tk1)
        self.assertIsNotNone(tkdb1)
        self.assertIsInstance(tkdb1, TokenDB)
        self.assertEqual(tkdb1.user, tk1.user)
        self.assertEqual(tkdb1.token, tk1.token)
        self.assertEqual(tkdb1.expiry, isotime.parse(tk1.expiry))
        tkdb2 = Token.add_or_update(tkdb1)
        self.assertEqual(tkdb1, tkdb2)
        self.assertIsNotNone(tkdb2.id)
        tk2 = TokenAPI.from_model(tkdb2)
        self.assertEqual(tk2.user, tk1.user)
        self.assertEqual(tk2.token, tk1.token)
        self.assertEqual(tk2.expiry, tk1.expiry)

    def test_token_model_null_token(self):
        dt = date_utils.get_datetime_utc_now()
        tk = TokenAPI(user='******', token=None, expiry=isotime.format(dt))
        self.assertRaises(ValueError, Token.add_or_update,
                          TokenAPI.to_model(tk))

    def test_token_model_null_user(self):
        dt = date_utils.get_datetime_utc_now()
        tk = TokenAPI(user=None,
                      token=uuid.uuid4().hex,
                      expiry=isotime.format(dt))
        self.assertRaises(ValueError, Token.add_or_update,
                          TokenAPI.to_model(tk))

    def test_token_model_null_expiry(self):
        tk = TokenAPI(user='******', token=uuid.uuid4().hex, expiry=None)
        self.assertRaises(ValueError, Token.add_or_update,
                          TokenAPI.to_model(tk))

    def _test_token_post(self, path=TOKEN_V1_PATH):
        ttl = cfg.CONF.auth.token_ttl
        timestamp = date_utils.get_datetime_utc_now()
        response = self.app.post_json(path, {}, expect_errors=False)
        expected_expiry = date_utils.get_datetime_utc_now(
        ) + datetime.timedelta(seconds=ttl)
        expected_expiry = date_utils.add_utc_tz(expected_expiry)
        self.assertEqual(response.status_int, 201)
        self.assertIsNotNone(response.json['token'])
        self.assertEqual(response.json['user'], USERNAME)
        actual_expiry = isotime.parse(response.json['expiry'])
        self.assertLess(timestamp, actual_expiry)
        self.assertLess(actual_expiry, expected_expiry)

    def test_token_post_unauthorized(self):
        type(pecan.request).remote_user = None
        response = self.app.post_json(TOKEN_V1_PATH, {}, expect_errors=True)
        self.assertEqual(response.status_int, 401)

    @mock.patch.object(User, 'get_by_name',
                       mock.MagicMock(side_effect=Exception()))
    @mock.patch.object(User, 'add_or_update',
                       mock.Mock(return_value=UserDB(name=USERNAME)))
    def test_token_post_new_user(self):
        self._test_token_post()

    @mock.patch.object(User, 'get_by_name',
                       mock.MagicMock(return_value=UserDB(name=USERNAME)))
    def test_token_post_existing_user(self):
        self._test_token_post()

    @mock.patch.object(User, 'get_by_name',
                       mock.MagicMock(return_value=UserDB(name=USERNAME)))
    def test_token_post_default_url_path(self):
        self._test_token_post(path=TOKEN_DEFAULT_PATH)

    @mock.patch.object(User, 'get_by_name',
                       mock.MagicMock(return_value=UserDB(name=USERNAME)))
    def test_token_post_set_ttl(self):
        timestamp = date_utils.add_utc_tz(date_utils.get_datetime_utc_now())
        response = self.app.post_json(TOKEN_V1_PATH, {'ttl': 60},
                                      expect_errors=False)
        expected_expiry = date_utils.get_datetime_utc_now(
        ) + datetime.timedelta(seconds=60)
        self.assertEqual(response.status_int, 201)
        actual_expiry = isotime.parse(response.json['expiry'])
        self.assertLess(timestamp, actual_expiry)
        self.assertLess(actual_expiry, expected_expiry)

    @mock.patch.object(User, 'get_by_name',
                       mock.MagicMock(return_value=UserDB(name=USERNAME)))
    def test_token_post_set_ttl_over_policy(self):
        ttl = cfg.CONF.auth.token_ttl
        response = self.app.post_json(TOKEN_V1_PATH, {'ttl': ttl + 60},
                                      expect_errors=True)
        self.assertEqual(response.status_int, 400)
        message = 'TTL specified %s is greater than max allowed %s.' % (
            ttl + 60, ttl)
        self.assertEqual(response.json['faultstring'], message)

    @mock.patch.object(User, 'get_by_name',
                       mock.MagicMock(return_value=UserDB(name=USERNAME)))
    def test_token_post_set_bad_ttl(self):
        response = self.app.post_json(TOKEN_V1_PATH, {'ttl': -1},
                                      expect_errors=True)
        self.assertEqual(response.status_int, 400)
        response = self.app.post_json(TOKEN_V1_PATH, {'ttl': 0},
                                      expect_errors=True)
        self.assertEqual(response.status_int, 400)

    @mock.patch.object(User, 'get_by_name',
                       mock.MagicMock(return_value=UserDB(name=USERNAME)))
    def test_token_get_unauthorized(self):
        # Create a new token.
        response = self.app.post_json(TOKEN_V1_PATH, expect_errors=False)

        # Verify the token. 401 is expected because an API key or token is not provided in header.
        data = {'token': str(response.json['token'])}
        response = self.app.post_json(TOKEN_VERIFY_PATH,
                                      data,
                                      expect_errors=True)
        self.assertEqual(response.status_int, 401)

    @mock.patch.object(User, 'get_by_name',
                       mock.MagicMock(return_value=UserDB(name=USERNAME)))
    def test_token_get_unauthorized_bad_api_key(self):
        # Create a new token.
        response = self.app.post_json(TOKEN_V1_PATH, expect_errors=False)

        # Verify the token. 401 is expected because the API key is bad.
        headers = {'St2-Api-Key': 'foobar'}
        data = {'token': str(response.json['token'])}
        response = self.app.post_json(TOKEN_VERIFY_PATH,
                                      data,
                                      headers=headers,
                                      expect_errors=True)
        self.assertEqual(response.status_int, 401)

    @mock.patch.object(User, 'get_by_name',
                       mock.MagicMock(return_value=UserDB(name=USERNAME)))
    def test_token_get_unauthorized_bad_token(self):
        # Create a new token.
        response = self.app.post_json(TOKEN_V1_PATH, expect_errors=False)

        # Verify the token. 401 is expected because the token is bad.
        headers = {'X-Auth-Token': 'foobar'}
        data = {'token': str(response.json['token'])}
        response = self.app.post_json(TOKEN_VERIFY_PATH,
                                      data,
                                      headers=headers,
                                      expect_errors=True)
        self.assertEqual(response.status_int, 401)

    @mock.patch.object(User, 'get_by_name',
                       mock.MagicMock(return_value=UserDB(name=USERNAME)))
    @mock.patch.object(
        ApiKey, 'get',
        mock.MagicMock(return_value=ApiKeyDB(user=USERNAME, key_hash='foobar'))
    )
    def test_token_get_auth_with_api_key(self):
        # Create a new token.
        response = self.app.post_json(TOKEN_V1_PATH, expect_errors=False)

        # Verify the token. Use an API key to authenticate with the st2 auth get token endpoint.
        headers = {'St2-Api-Key': 'foobar'}
        data = {'token': str(response.json['token'])}
        response = self.app.post_json(TOKEN_VERIFY_PATH,
                                      data,
                                      headers=headers,
                                      expect_errors=True)
        self.assertEqual(response.status_int, 200)
        self.assertTrue(response.json['valid'])

    @mock.patch.object(User, 'get_by_name',
                       mock.MagicMock(return_value=UserDB(name=USERNAME)))
    def test_token_get_auth_with_token(self):
        # Create a new token.
        response = self.app.post_json(TOKEN_V1_PATH, expect_errors=False)

        # Verify the token. Use a token to authenticate with the st2 auth get token endpoint.
        headers = {'X-Auth-Token': str(response.json['token'])}
        data = {'token': str(response.json['token'])}
        response = self.app.post_json(TOKEN_VERIFY_PATH,
                                      data,
                                      headers=headers,
                                      expect_errors=True)
        self.assertEqual(response.status_int, 200)
        self.assertTrue(response.json['valid'])

    @mock.patch.object(User, 'get_by_name',
                       mock.MagicMock(return_value=UserDB(name=USERNAME)))
    @mock.patch.object(
        ApiKey, 'get',
        mock.MagicMock(return_value=ApiKeyDB(user=USERNAME, key_hash='foobar'))
    )
    @mock.patch.object(
        Token, 'get',
        mock.MagicMock(
            return_value=TokenDB(user=USERNAME,
                                 token='12345',
                                 expiry=date_utils.get_datetime_utc_now() -
                                 datetime.timedelta(minutes=1))))
    def test_token_get_unauthorized_bad_ttl(self):
        # Verify the token. 400 is expected because the token has expired.
        headers = {'St2-Api-Key': 'foobar'}
        data = {'token': '12345'}
        response = self.app.post_json(TOKEN_VERIFY_PATH,
                                      data,
                                      headers=headers,
                                      expect_errors=False)
        self.assertEqual(response.status_int, 200)
        self.assertFalse(response.json['valid'])
示例#13
0
class TestTokenBasedAuth(FunctionalTest):

    enable_auth = True

    @mock.patch.object(
        Token,
        "get",
        mock.Mock(return_value=TokenDB(
            id=OBJ_ID, user=USER, token=TOKEN, expiry=FUTURE)),
    )
    @mock.patch.object(User, "get_by_name", mock.Mock(return_value=USER_DB))
    def test_token_validation_token_in_headers(self):
        response = self.app.get("/v1/actions",
                                headers={"X-Auth-Token": TOKEN},
                                expect_errors=False)
        self.assertIn("application/json", response.headers["content-type"])
        self.assertEqual(response.status_int, 200)

    @mock.patch.object(
        Token,
        "get",
        mock.Mock(return_value=TokenDB(
            id=OBJ_ID, user=USER, token=TOKEN, expiry=FUTURE)),
    )
    @mock.patch.object(User, "get_by_name", mock.Mock(return_value=USER_DB))
    def test_token_validation_token_in_query_params(self):
        response = self.app.get("/v1/actions?x-auth-token=%s" % (TOKEN),
                                expect_errors=False)
        self.assertIn("application/json", response.headers["content-type"])
        self.assertEqual(response.status_int, 200)

    @mock.patch.object(
        Token,
        "get",
        mock.Mock(return_value=TokenDB(
            id=OBJ_ID, user=USER, token=TOKEN, expiry=FUTURE)),
    )
    @mock.patch.object(User, "get_by_name", mock.Mock(return_value=USER_DB))
    def test_token_validation_token_in_query_params_auth_cookie_is_set(self):
        response = self.app.get("/v1/actions?x-auth-token=%s" % (TOKEN),
                                expect_errors=False)
        self.assertIn("application/json", response.headers["content-type"])
        self.assertEqual(response.status_int, 200)
        self.assertTrue("Set-Cookie" in response.headers)
        self.assertTrue("HttpOnly" in response.headers["Set-Cookie"])

        # Also test same cookie values + secure
        valid_values = ["strict", "lax", "none", "unset"]

        for value in valid_values:
            cfg.CONF.set_override(group="api",
                                  name="auth_cookie_same_site",
                                  override=value)
            cfg.CONF.set_override(group="api",
                                  name="auth_cookie_secure",
                                  override=True)

            response = self.app.get("/v1/actions?x-auth-token=%s" % (TOKEN),
                                    expect_errors=False)
            self.assertIn("application/json", response.headers["content-type"])
            self.assertEqual(response.status_int, 200)
            self.assertTrue("Set-Cookie" in response.headers)
            self.assertTrue("HttpOnly" in response.headers["Set-Cookie"])

            if value == "unset":
                self.assertFalse("SameSite" in response.headers["Set-Cookie"])
            else:
                self.assertTrue("SameSite=%s" %
                                (value) in response.headers["Set-Cookie"])

            self.assertTrue("secure" in response.headers["Set-Cookie"])

        # SameSite=Lax, Secure=False
        cfg.CONF.set_override(group="api",
                              name="auth_cookie_same_site",
                              override="lax")
        cfg.CONF.set_override(group="api",
                              name="auth_cookie_secure",
                              override=False)

        response = self.app.get("/v1/actions?x-auth-token=%s" % (TOKEN),
                                expect_errors=False)
        self.assertIn("application/json", response.headers["content-type"])
        self.assertEqual(response.status_int, 200)
        self.assertTrue("Set-Cookie" in response.headers)
        self.assertTrue("HttpOnly" in response.headers["Set-Cookie"])
        self.assertTrue("SameSite=lax" in response.headers["Set-Cookie"])
        self.assertTrue("secure" not in response.headers["Set-Cookie"])

    @mock.patch.object(
        Token,
        "get",
        mock.Mock(return_value=TokenDB(
            id=OBJ_ID, user=USER, token=TOKEN, expiry=FUTURE)),
    )
    @mock.patch.object(User, "get_by_name", mock.Mock(return_value=USER_DB))
    def test_token_validation_token_in_cookies(self):
        response = self.app.get("/v1/actions",
                                headers={"X-Auth-Token": TOKEN},
                                expect_errors=False)
        self.assertIn("application/json", response.headers["content-type"])
        self.assertEqual(response.status_int, 200)

        with mock.patch.object(self.app.cookiejar, "clear", return_value=None):
            response = self.app.get("/v1/actions", expect_errors=False)
        self.assertIn("application/json", response.headers["content-type"])
        self.assertEqual(response.status_int, 200)

    @mock.patch.object(
        Token,
        "get",
        mock.Mock(return_value=TokenDB(
            id=OBJ_ID, user=USER, token=TOKEN, expiry=PAST)),
    )
    def test_token_expired(self):
        response = self.app.get("/v1/actions",
                                headers={"X-Auth-Token": TOKEN},
                                expect_errors=True)
        self.assertIn("application/json", response.headers["content-type"])
        self.assertEqual(response.status_int, 401)

    @mock.patch.object(Token, "get",
                       mock.MagicMock(side_effect=TokenNotFoundError()))
    def test_token_not_found(self):
        response = self.app.get("/v1/actions",
                                headers={"X-Auth-Token": TOKEN},
                                expect_errors=True)
        self.assertIn("application/json", response.headers["content-type"])
        self.assertEqual(response.status_int, 401)

    def test_token_not_provided(self):
        response = self.app.get("/v1/actions", expect_errors=True)
        self.assertIn("application/json", response.headers["content-type"])
        self.assertEqual(response.status_int, 401)