Merge "Add user management REST API"
This commit is contained in:
commit
c78ce638e3
@ -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()
|
||||
|
82
refstack/api/controllers/vendors.py
Normal file
82
refstack/api/controllers/vendors.py
Normal 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()
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
|
Loading…
x
Reference in New Issue
Block a user