Merge "Add user management REST API"

This commit is contained in:
Jenkins 2016-03-26 20:57:37 +00:00 committed by Gerrit Code Review
commit c78ce638e3
8 changed files with 260 additions and 2 deletions

View File

@ -19,6 +19,7 @@ from refstack.api.controllers import auth
from refstack.api.controllers import guidelines
from refstack.api.controllers import results
from refstack.api.controllers import user
from refstack.api.controllers import vendors
class V1Controller(object):
@ -28,3 +29,4 @@ class V1Controller(object):
guidelines = guidelines.GuidelinesController()
auth = auth.AuthController()
profile = user.ProfileController()
vendors = vendors.VendorsController()

View File

@ -0,0 +1,82 @@
# Copyright (c) 2015 Mirantis, Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""Vendors controller."""
import base64
import six
from oslo_config import cfg
from oslo_log import log
import pecan
from pecan import rest
from pecan.secure import secure
from refstack.api import utils as api_utils
from refstack import db
LOG = log.getLogger(__name__)
CONF = cfg.CONF
class UsersController(rest.RestController):
"""/v1/vendors/<vendor_id>/users handler."""
@secure(api_utils.is_authenticated)
@pecan.expose('json')
def get(self, vendor_id):
"""Return list of users in the vendor's group."""
if not (api_utils.check_user_is_foundation_admin()
or api_utils.check_user_is_vendor_admin(vendor_id)):
return None
org_users = db.get_organization_users(vendor_id)
return [x for x in six.itervalues(org_users)]
@secure(api_utils.is_authenticated)
@pecan.expose('json')
def put(self, vendor_id, openid):
"""Add user to vendor group."""
openid = base64.b64decode(openid)
if not (api_utils.check_user_is_foundation_admin()
or api_utils.check_user_is_vendor_admin(vendor_id)):
pecan.abort(403, 'Forbidden.')
vendor = db.get_organization(vendor_id)
creator = api_utils.get_user_id()
db.add_user_to_group(openid, vendor['group_id'], creator)
pecan.response.status = 204
@secure(api_utils.is_authenticated)
@pecan.expose('json')
def delete(self, vendor_id, openid):
"""Remove user from vendor group."""
openid = base64.b64decode(openid)
if not (api_utils.check_user_is_foundation_admin()
or api_utils.check_user_is_vendor_admin(vendor_id)):
pecan.abort(403, 'Forbidden.')
vendor = db.get_organization(vendor_id)
db.remove_user_from_group(openid, vendor['group_id'])
pecan.response.status = 204
class VendorsController(rest.RestController):
"""/v1/vendors handler."""
users = UsersController()

View File

@ -311,3 +311,10 @@ def check_user_is_foundation_admin():
user = get_user_id()
org_users = db.get_foundation_users()
return user in org_users
def check_user_is_vendor_admin(vendor_id):
"""Check is user in vendor group or not."""
user = get_user_id()
org_users = db.get_organization_users(vendor_id)
return user in org_users

View File

@ -215,3 +215,13 @@ def delete_product(id):
def get_foundation_users():
"""Get users' openid-s that belong to group of foundation."""
return IMPL.get_foundation_users()
def get_organization_users(organization_id):
"""Get users with info that belong to group of organization."""
return IMPL.get_organization_users(organization_id)
def get_organizations():
"""Get all organizations."""
return IMPL.get_organizations()

View File

@ -498,3 +498,30 @@ def get_foundation_users():
users = (session.query(models.UserToGroup.user_openid).
filter_by(group_id=group_id))
return [user.user_openid for user in users]
def get_organization_users(organization_id):
"""Get users that belong to group of organization."""
session = get_session()
organization = (session.query(models.Organization.group_id)
.filter_by(id=organization_id).first())
if organization is None:
raise NotFound('Organization with id %s is not found'
% organization_id)
group_id = organization.group_id
users = (session.query(models.UserToGroup, models.User)
.join(models.User,
models.User.openid == models.UserToGroup.user_openid)
.filter(models.UserToGroup.group_id == group_id))
keys = ['openid', 'fullname', 'email']
return {item[1].openid: _to_dict(item[1], allowed_keys=keys)
for item in users}
def get_organizations():
"""Get all organizations."""
session = get_session()
items = (
session.query(models.Organization)
.order_by(models.Organization.created_at.desc()).all())
return _to_dict(items)

View File

@ -27,8 +27,9 @@ from refstack.api import exceptions as api_exc
from refstack.api.controllers import auth
from refstack.api.controllers import guidelines
from refstack.api.controllers import results
from refstack.api.controllers import validation
from refstack.api.controllers import user
from refstack.api.controllers import validation
from refstack.api.controllers import vendors
from refstack.tests import unit as base
@ -45,7 +46,7 @@ class BaseControllerTestCase(base.RefstackBaseTestCase):
self.setup_mock('refstack.api.utils.get_user_role')
self.mock_is_authenticated = \
self.setup_mock('refstack.api.utils.is_authenticated',
return_value=True)
return_value=True, spec=self.setUp)
class RootControllerTestCase(BaseControllerTestCase):
@ -594,3 +595,73 @@ class PublicKeysControllerTestCase(BaseControllerTestCase):
self.assertRaises(webob.exc.HTTPError,
self.controller.delete, 'other_key_id')
class VendorUsersControllerTestCase(BaseControllerTestCase):
def setUp(self):
super(VendorUsersControllerTestCase, self).setUp()
self.controller = vendors.UsersController()
@mock.patch('refstack.db.get_organization_users')
@mock.patch('refstack.api.utils.check_user_is_foundation_admin')
@mock.patch('refstack.api.utils.check_user_is_vendor_admin')
def test_get(self, mock_vendor, mock_foundation, mock_db_get_org_users):
mock_vendor.return_value = True
mock_foundation.return_value = False
mock_db_get_org_users.return_value = {
'foobar': {
'openid': 'foobar',
'fullname': 'Foo Bar',
'email': 'foo@bar.com'
}
}
expected = [{'openid': 'foobar',
'fullname': 'Foo Bar',
'email': 'foo@bar.com'}]
self.assertEqual(expected, self.controller.get('some-org'))
mock_vendor.return_value = False
self.assertIsNone(self.controller.get('some-org'))
mock_foundation.return_value = True
self.assertEqual(expected, self.controller.get('some-org'))
@mock.patch('refstack.db.add_user_to_group')
@mock.patch('refstack.db.get_organization')
@mock.patch('refstack.api.utils.check_user_is_foundation_admin')
@mock.patch('refstack.api.utils.check_user_is_vendor_admin')
@mock.patch('refstack.api.utils.get_user_id')
def test_put(self, mock_get_user, mock_vendor, mock_foundation,
mock_db_org, mock_add):
# This is 'foo' in Base64
encoded_openid = 'Zm9v'
mock_vendor.return_value = True
mock_foundation.return_value = False
mock_db_org.return_value = {'group_id': 'abc'}
mock_get_user.return_value = 'fake-id'
self.controller.put('fake-vendor', encoded_openid)
mock_add.assert_called_once_with(b'foo', 'abc', 'fake-id')
mock_vendor.return_value = False
self.assertRaises(webob.exc.HTTPError,
self.controller.put, 'fake-vendor', encoded_openid)
@mock.patch('refstack.db.remove_user_from_group')
@mock.patch('refstack.db.get_organization')
@mock.patch('refstack.api.utils.check_user_is_foundation_admin')
@mock.patch('refstack.api.utils.check_user_is_vendor_admin')
def test_delete(self, mock_vendor, mock_foundation, mock_db_org,
mock_remove):
# This is 'foo' in Base64
encoded_openid = 'Zm9v'
mock_vendor.return_value = True
mock_foundation.return_value = False
mock_db_org.return_value = {'group_id': 'abc'}
self.controller.delete('fake-vendor', encoded_openid)
mock_remove.assert_called_with(b'foo', 'abc')
mock_vendor.return_value = False
self.assertRaises(webob.exc.HTTPError, self.controller.delete,
'fake-vendor', encoded_openid)

View File

@ -478,3 +478,15 @@ class APIUtilsTestCase(base.BaseTestCase):
401, 'Authentication is failed. '
'Please permit access to your name.'
)
@mock.patch('refstack.db.get_organization_users')
@mock.patch.object(api_utils, 'get_user_id', return_value='fake_id')
def test_check_user_is_vendor_admin(self, mock_user, mock_db):
mock_user.return_value = 'some-user'
mock_db.return_value = ['some-user', 'another-user']
result = api_utils.check_user_is_vendor_admin('some-vendor')
self.assertTrue(result)
mock_db.return_value = ['another-user']
result = api_utils.check_user_is_vendor_admin('some-vendor')
self.assertFalse(result)

View File

@ -766,3 +766,50 @@ class DBBackendTestCase(base.BaseTestCase):
mock.call(id='product_id'),
mock.call().delete(synchronize_session=False)))
session.begin.assert_called_once_with()
@mock.patch.object(api, 'get_session',
return_value=mock.Mock(name='session'),)
@mock.patch('refstack.db.sqlalchemy.api.models')
def test_get_organization_users(self, mock_models, mock_get_session):
organization_id = 12345
session = mock_get_session.return_value
query = session.query.return_value
filtered = query.filter_by.return_value
filtered.first.return_value.group_id = 'foo'
join = query.join.return_value
fake_user = models.User()
fake_user.openid = 'foobar'
fake_user.fullname = 'Foo Bar'
fake_user.email = 'foo@bar.com'
join.filter.return_value = [(mock.Mock(), fake_user)]
result = api.get_organization_users(organization_id)
expected = {'foobar': {'openid': 'foobar',
'fullname': 'Foo Bar',
'email': 'foo@bar.com'}}
self.assertEqual(expected, result)
session.query.assert_any_call(mock_models.Organization.group_id)
query.filter_by.assert_called_once_with(id=organization_id)
session.query.assert_any_call(mock_models.UserToGroup,
mock_models.User)
@mock.patch.object(api, 'get_session',
return_value=mock.Mock(name='session'),)
@mock.patch('refstack.db.sqlalchemy.models.Organization')
@mock.patch.object(api, '_to_dict', side_effect=lambda x: x)
def test_organizations_get(self, mock_to_dict, mock_model,
mock_get_session):
session = mock_get_session.return_value
query = session.query.return_value
ordered = query.order_by.return_value
organizations = ordered.all.return_value
result = api.get_organizations()
self.assertEqual(organizations, result)
session.query.assert_called_once_with(mock_model)
query.order_by.assert_called_once_with(mock_model.created_at.desc())
ordered.all.assert_called_once_with()