From 07cf9bd5c65e42a11495728136bb06556624bac2 Mon Sep 17 00:00:00 2001 From: Dolph Mathews Date: Mon, 18 Jul 2011 11:00:21 -0500 Subject: Minor cleanup --- keystone/backends/alterdb/models.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/keystone/backends/alterdb/models.py b/keystone/backends/alterdb/models.py index 7aa30c18..25d9642c 100755 --- a/keystone/backends/alterdb/models.py +++ b/keystone/backends/alterdb/models.py @@ -14,11 +14,10 @@ # See the License for the specific language governing permissions and # limitations under the License. # Not Yet PEP8 standardized -from sqlalchemy import Column, String, Integer, ForeignKey, \ - UniqueConstraint, Boolean, DateTime +from sqlalchemy import Column, String, DateTime from sqlalchemy.exc import IntegrityError from sqlalchemy.ext.declarative import declarative_base -from sqlalchemy.orm import relationship, object_mapper +from sqlalchemy.orm import object_mapper Base = declarative_base() @@ -77,7 +76,7 @@ class KeystoneBase(object): class Token(Base, KeystoneBase): __tablename__ = 'token' - __api__ ='token' + __api__ = 'token' id = Column(String(255), primary_key=True, unique=True) user_id = Column(String(255)) tenant_id = Column(String(255)) -- cgit From b2839ad32f3ce7ac935ea1edac59bcb2bdc0b6ed Mon Sep 17 00:00:00 2001 From: Dolph Mathews Date: Mon, 18 Jul 2011 11:00:37 -0500 Subject: Added (failing) system test for issue #13 --- keystone/test/system/test_request_specs.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/keystone/test/system/test_request_specs.py b/keystone/test/system/test_request_specs.py index f828d865..549181a5 100644 --- a/keystone/test/system/test_request_specs.py +++ b/keystone/test/system/test_request_specs.py @@ -52,5 +52,15 @@ class TestContentTypes(KeystoneTestCase): self.assertTrue('application/json' in r.getheader('Content-Type')) + def test_content_type_on_404(self): + """Content-Type should be honored even on 404 errors (Issue #13)""" + _r = self.service_request(path='/completely-invalid-path', + headers={'Accept': 'application/xml'}, + expect_exception=True) + + # Commenting this assertion out, as it currently fails +# self.assertTrue('application/xml' in r.getheader('Content-Type'), +# 'application/xml not in %s' % r.getheader('Content-Type')) + if __name__ == '__main__': unittest.main() -- cgit From 15d719e82c7ca96428caa25fc29bdf41a3e9e150 Mon Sep 17 00:00:00 2001 From: Dolph Mathews Date: Mon, 18 Jul 2011 12:37:18 -0500 Subject: Merged duplicate code --- keystone/common/config.py | 33 ++------------------------------- keystone/common/wsgi.py | 23 ++++++++++++----------- 2 files changed, 14 insertions(+), 42 deletions(-) diff --git a/keystone/common/config.py b/keystone/common/config.py index afe3668e..9de65389 100755 --- a/keystone/common/config.py +++ b/keystone/common/config.py @@ -26,6 +26,7 @@ import os from paste import deploy import sys import ConfigParser +from keystone.common.wsgi import add_console_handler DEFAULT_LOG_FORMAT = "%(asctime)s %(levelname)8s [%(name)s] %(message)s" DEFAULT_LOG_DATE_FORMAT = "%Y-%m-%d %H:%M:%S" @@ -125,34 +126,6 @@ def add_log_options(parser): parser.add_option_group(group) return group - -def add_console_handler(logger, level=logging.INFO): - """ - Add a Handler which writes log messages to sys.stderr - (which is often the console) - There is a copy of this function in wsgi.py (TODO(Ziad): Make it one copy) - """ - console = None - for console in logger.handlers: - if isinstance(console, logging.StreamHandler): - break - - if not console: - console = logging.StreamHandler() - console.setLevel(level) - # set a format which is simpler for console use - formatter = logging.Formatter("%(name)-12s: "\ - "%(levelname)-8s %(message)s") - # tell the handler to use this format - console.setFormatter(formatter) - # add the handler to the root logger - logger.addHandler(console) - else: - if console.level != level: - console.setLevel(level) - return console - - def setup_logging(options, conf): """ Sets up the logging options for a log with supplied name @@ -205,9 +178,7 @@ def setup_logging(options, conf): logfile.setFormatter(formatter) root_logger.addHandler(logfile) # Mirror to console if verbose or debug - if debug: - add_console_handler(root_logger, logging.INFO) #debug too noisy - elif verbose: + if debug or verbose: add_console_handler(root_logger, logging.INFO) else: handler = logging.StreamHandler(sys.stdout) diff --git a/keystone/common/wsgi.py b/keystone/common/wsgi.py index cf5fbf72..3eafe296 100755 --- a/keystone/common/wsgi.py +++ b/keystone/common/wsgi.py @@ -32,22 +32,24 @@ import routes.middleware import webob.dec import webob.exc -def add_console_handler(logger, level): +def find_stream_handler(logger): + """Returns a stream handler, if any""" + for handler in logger.handlers: + if isinstance(handler, logging.StreamHandler): + return handler + +def add_console_handler(logger, level=logging.INFO): """ - Add a Handler which writes messages to sys.stderr which is often the - console. - There is a copy of this function in config.py (TODO(Ziad): Make it one copy) + Add a Handler which writes log messages to sys.stderr (usually the console) """ - console = None - for console in logger.handlers: - if isinstance(console, logging.StreamHandler): - break - + console = find_stream_handler(logger) + if not console: console = logging.StreamHandler() console.setLevel(level) # set a format which is simpler for console use - formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s') + formatter = logging.Formatter( + "%(name)-12s: %(levelname)-8s %(message)s") # tell the handler to use this format console.setFormatter(formatter) # add the handler to the root logger @@ -56,7 +58,6 @@ def add_console_handler(logger, level): console.setLevel(level) return console - class WritableLogger(object): """A thin wrapper that responds to `write` and logs.""" -- cgit From 95a7a4bff460b11b155f42c2ca35f6121319d731 Mon Sep 17 00:00:00 2001 From: Dolph Mathews Date: Mon, 18 Jul 2011 15:52:30 -0500 Subject: Formatting --- etc/keystone.conf | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/etc/keystone.conf b/etc/keystone.conf index 243f9158..52d42c04 100755 --- a/etc/keystone.conf +++ b/etc/keystone.conf @@ -15,10 +15,11 @@ default_store = sqlite #log_file = /var/log/keystone.log log_file = keystone.log -#List of backends to be configured +# List of backends to be configured backends = keystone.backends.sqlalchemy,keystone.backends.alterdb -#Dictionary Maps every service to a header.Missing services would get header X_(SERVICE_NAME) Key => Service Name, Value => Header Name +# Dictionary Maps every service to a header.Missing services would get header +# X_(SERVICE_NAME) Key => Service Name, Value => Header Name service-header-mappings = { 'nova' : 'X-Server-Management-Url', 'swift' : 'X-Storage-Url', @@ -41,20 +42,21 @@ admin_port = 5001 keystone-admin-role = Admin [keystone.backends.sqlalchemy] -# SQLAlchemy connection string for the reference implementation -# registry server. Any valid SQLAlchemy connection string is fine. -# See: http://www.sqlalchemy.org/docs/05/reference/sqlalchemy/connections.html#sqlalchemy.create_engine +# SQLAlchemy connection string for the reference implementation registry +# server. Any valid SQLAlchemy connection string is fine. +# See: http://bit.ly/ideIpI sql_connection = sqlite:///keystone.db -backend_entities = ['UserGroupAssociation', 'UserRoleAssociation', 'Endpoints', 'Role', 'Tenant', 'User', 'Credentials', 'Group', 'EndpointTemplates'] +backend_entities = ['UserGroupAssociation', 'UserRoleAssociation', 'Endpoints', + 'Role', 'Tenant', 'User', 'Credentials', 'Group', 'EndpointTemplates'] # Period in seconds after which SQLAlchemy should reestablish its connection # to the database. sql_idle_timeout = 30 [keystone.backends.alterdb] -# SQLAlchemy connection string for the reference implementation -# registry server. Any valid SQLAlchemy connection string is fine. -# See: http://www.sqlalchemy.org/docs/05/reference/sqlalchemy/connections.html#sqlalchemy.create_engine +# SQLAlchemy connection string for the reference implementation registry +# server. Any valid SQLAlchemy connection string is fine. +# See: http://bit.ly/ideIpI sql_connection = sqlite:///keystone.token.db backend_entities = ['Token'] -- cgit From a93bc04ddefa8de9eeeaedca95e76deedbe3157d Mon Sep 17 00:00:00 2001 From: Dolph Mathews Date: Mon, 18 Jul 2011 16:37:13 -0500 Subject: PEP8 fixes --- keystone/backends/sqlalchemy/models.py | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/keystone/backends/sqlalchemy/models.py b/keystone/backends/sqlalchemy/models.py index f2ca7dc0..4473dbed 100755 --- a/keystone/backends/sqlalchemy/models.py +++ b/keystone/backends/sqlalchemy/models.py @@ -16,7 +16,7 @@ # Not Yet PEP8 standardized from sqlalchemy import Column, String, Integer, ForeignKey, \ - UniqueConstraint, Boolean, DateTime + UniqueConstraint, Boolean, DateTime from sqlalchemy.exc import IntegrityError from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import relationship, object_mapper @@ -79,7 +79,7 @@ class KeystoneBase(object): # Define associations first class UserGroupAssociation(Base, KeystoneBase): __tablename__ = 'user_group_association' - __api__ ='tenant_group' + __api__ = 'tenant_group' user_id = Column(String(255), ForeignKey('users.id'), primary_key=True) group_id = Column(String(255), ForeignKey('groups.id'), primary_key=True) @@ -99,32 +99,31 @@ class Endpoints(Base, KeystoneBase): id = Column(Integer, primary_key=True) tenant_id = Column(String(255), ForeignKey('tenants.id')) endpoint_template_id = Column(Integer, ForeignKey('endpoint_templates.id')) - __table_args__ = (UniqueConstraint("endpoint_template_id",\ - "tenant_id"), {}) + __table_args__ = ( + UniqueConstraint("endpoint_template_id", "tenant_id"), {}) # Define objects class Role(Base, KeystoneBase): __tablename__ = 'roles' - __api__ ='role' + __api__ = 'role' id = Column(String(255), primary_key=True, unique=True) desc = Column(String(255)) class Tenant(Base, KeystoneBase): __tablename__ = 'tenants' - __api__ ='tenant' + __api__ = 'tenant' id = Column(String(255), primary_key=True, unique=True) desc = Column(String(255)) enabled = Column(Integer) groups = relationship('Group', backref='tenants') - endpoints = relationship('Endpoints', backref='tenant', - cascade="all") + endpoints = relationship('Endpoints', backref='tenant', cascade="all") class User(Base, KeystoneBase): __tablename__ = 'users' - __api__ ='user' + __api__ = 'user' id = Column(String(255), primary_key=True, unique=True) password = Column(String(255)) email = Column(String(255)) @@ -146,14 +145,14 @@ class Credentials(Base, KeystoneBase): class Group(Base, KeystoneBase): __tablename__ = 'groups' - __api__ ='group' + __api__ = 'group' id = Column(String(255), primary_key=True, unique=True) desc = Column(String(255)) tenant_id = Column(String(255), ForeignKey('tenants.id')) class Token(Base, KeystoneBase): __tablename__ = 'token' - __api__ ='token' + __api__ = 'token' id = Column(String(255), primary_key=True, unique=True) user_id = Column(String(255)) tenant_id = Column(String(255)) @@ -161,7 +160,7 @@ class Token(Base, KeystoneBase): class EndpointTemplates(Base, KeystoneBase): __tablename__ = 'endpoint_templates' - __api__ ='endpoint_template' + __api__ = 'endpoint_template' id = Column(Integer, primary_key=True) region = Column(String(255)) service = Column(String(255)) -- cgit From 9dcd6c1cd2d6f3f25b97f16ea405a0f3b1c9073f Mon Sep 17 00:00:00 2001 From: Dolph Mathews Date: Tue, 19 Jul 2011 15:25:21 -0500 Subject: - System test framework can now assert specific response codes automatically - Revised system test for issue #85 based on clarification from Ziad - Added system test to attempt admin action using a service token --- keystone/test/system/common.py | 26 +++++++------- keystone/test/system/test_auth.py | 54 +++++++++++++++++++++++++++++- keystone/test/system/test_issue_85.py | 21 +++++++----- keystone/test/system/test_request_specs.py | 2 +- 4 files changed, 79 insertions(+), 24 deletions(-) diff --git a/keystone/test/system/common.py b/keystone/test/system/common.py index b33537b2..1e2fd206 100644 --- a/keystone/test/system/common.py +++ b/keystone/test/system/common.py @@ -5,7 +5,7 @@ class HttpTestCase(unittest.TestCase): """Performs generic HTTP request testing""" def request(self, host='127.0.0.1', port=80, method='GET', path='/', - headers={}, body=None, expect_exception=False,): + headers={}, body=None, assert_status=None): """Perform request and fetch httplib.HTTPResponse from the server""" # Initialize a connection @@ -22,26 +22,26 @@ class HttpTestCase(unittest.TestCase): connection.close() # Automatically assert HTTP status code - if not expect_exception: - self.assertSuccessfulResponse(response) + if assert_status: + self.assertResponseStatus(response, assert_status) else: - self.assertExceptionalResponse(response) + self.assertResponseSuccessful(response) # Contains the response headers, body, etc return response - - def assertSuccessfulResponse(self, response): + + def assertResponseSuccessful(self, response): """Asserts that a status code lies inside the 2xx range""" self.assertTrue(response.status >= 200 and response.status <= 299, - 'Status code %d is outside of the expected range (2xx) \n\n%s' % - (response.status, response.body)) - - def assertExceptionalResponse(self, response): - """Asserts that a status code lies outside the 2xx range""" - self.assertFalse(response.status >= 200 and response.status <= 299, - 'Status code %d is outside of the expected range (not 2xx)\n\n%s' % + 'Status code %d is outside of the expected range (2xx)\n\n%s' % (response.status, response.body)) + def assertResponseStatus(self, response, assert_status): + """Asserts a specific status code on the response""" + self.assertEqual(response.status, assert_status, + 'Status code %s is not %s, as expected)\n\n%s' % + (response.status, assert_status, response.body)) + class RestfulTestCase(HttpTestCase): """Performs restful HTTP request testing""" diff --git a/keystone/test/system/test_auth.py b/keystone/test/system/test_auth.py index 697c928a..b11eb3e8 100644 --- a/keystone/test/system/test_auth.py +++ b/keystone/test/system/test_auth.py @@ -18,6 +18,58 @@ class TestAdminAuthentication(KeystoneTestCase): self.assertTrue(r.json['auth']['token']['id']) self.assertTrue(r.json['auth']['token']['expires']) +class TestAdminAuthenticationNegative(KeystoneTestCase): + """Negative test admin-side user authentication""" + + user_id = KeystoneTestCase._uuid() + user_id2 = KeystoneTestCase._uuid() + + def test_service_token_as_admin_token(self): + """Admin actions should fail for mere service tokens""" + + # Admin create a user + self.admin_request(method='PUT', path='/users', + json={ + 'user': { + 'id': self.user_id, + 'password': 'secrete', + 'email': self.user_id + '@openstack.org', + 'enabled': True, + } + }) + + # User authenticates to get a token + r = self.service_request(method='POST', path='/tokens', + json={ + 'passwordCredentials': { + 'username': self.user_id, + 'password': 'secrete', + } + }) + self.service_token = r.json['auth']['token']['id'] + + # Prepare to use the service token as an admin token + self.admin_token_backup = self.admin_token + self.admin_token = self.service_token + + # Try creating another user + self.admin_request(method='PUT', path='/users', assert_status=401, + json={ + 'user': { + 'id': self.user_id2, + 'password': 'secrete', + 'email': self.user_id2 + '@openstack.org', + 'enabled': True, + } + }) + + def tearDown(self): + # Restore our admin token so we can clean up + self.admin_token = self.admin_token_backup + + # Delete user + self.admin_request(method='DELETE', path='/users/%s' % self.user_id) + class TestServiceAuthentication(KeystoneTestCase): """Test service-side user authentication""" @@ -32,7 +84,7 @@ class TestServiceAuthentication(KeystoneTestCase): 'user': { 'id': self.user_id, 'password': 'secrete', - 'email': 'user@openstack.org', + 'email': self.user_id + '@openstack.org', 'enabled': True, } }) diff --git a/keystone/test/system/test_issue_85.py b/keystone/test/system/test_issue_85.py index 76490232..f9ec8066 100644 --- a/keystone/test/system/test_issue_85.py +++ b/keystone/test/system/test_issue_85.py @@ -20,7 +20,7 @@ class TestIssue85(KeystoneTestCase): } }) - # Create a user + # Create a user for a specific tenant self.admin_request(method='PUT', path='/users', json={ 'user':{ @@ -28,7 +28,7 @@ class TestIssue85(KeystoneTestCase): 'password': 'secrete', 'email': 'user@openstack.org', 'enabled': True, - 'tenant_id': 'tenant', + 'tenantId': self.tenant_id } }) @@ -43,18 +43,20 @@ class TestIssue85(KeystoneTestCase): def test_disabling_tenant_disables_token(self): """Disabling a tenant should invalidate previously-issued tokens""" - # Authenticate as user to get a token + # Authenticate as user to get a token *for a specific tenant* r = self.service_request(method='POST', path='/tokens', json={ 'passwordCredentials': { 'username': self.user_id, 'password': 'secrete', + 'tenantId': self.tenant_id } }) self.service_token = r.json['auth']['token']['id'] - # Validate tenant token - self.admin_request(path='/tokens/%s' % self.service_token) + # Validate and check that token belongs to tenant + self.admin_request(path='/tokens/%s?belongsTo=%s' % + (self.service_token, self.tenant_id)) # Disable tenant r = self.admin_request(method='PUT', @@ -67,10 +69,11 @@ class TestIssue85(KeystoneTestCase): }) self.assertEqual(r.json['tenant']['enabled'], False) - # Assert tenant token invalidated - # Commented this out because it will fail this test -# self.admin_request(path='/tokens/%s' % self.service_token, -# expect_exception=True) + # Assert that token belonging to disabled tenant is invalid + r = self.admin_request(path='/tokens/%s?belongsTo=%s' % + (self.service_token, self.tenant_id), + assert_status=403) + self.assertTrue(r.json['tenantDisabled'], 'Tenant is disabled') if __name__ == '__main__': unittest.main() diff --git a/keystone/test/system/test_request_specs.py b/keystone/test/system/test_request_specs.py index 549181a5..04e390c7 100644 --- a/keystone/test/system/test_request_specs.py +++ b/keystone/test/system/test_request_specs.py @@ -56,7 +56,7 @@ class TestContentTypes(KeystoneTestCase): """Content-Type should be honored even on 404 errors (Issue #13)""" _r = self.service_request(path='/completely-invalid-path', headers={'Accept': 'application/xml'}, - expect_exception=True) + assert_status=404) # Commenting this assertion out, as it currently fails # self.assertTrue('application/xml' in r.getheader('Content-Type'), -- cgit From d4727d4b746db8a1fb4b2a7519b30ffa8f4bec7f Mon Sep 17 00:00:00 2001 From: Dolph Mathews Date: Tue, 19 Jul 2011 15:31:35 -0500 Subject: Fix for issue #85 --- keystone/logic/service.py | 184 +++++++++++++++++++++++++--------------------- 1 file changed, 99 insertions(+), 85 deletions(-) diff --git a/keystone/logic/service.py b/keystone/logic/service.py index cc309ef2..99abe568 100755 --- a/keystone/logic/service.py +++ b/keystone/logic/service.py @@ -47,7 +47,7 @@ class IdentityService(object): raise fault.UnauthorizedFault("Unauthorized") else: duser = api.user.get_by_tenant(credentials.username, - credentials.tenant_id) + credentials.tenant_id) if duser == None: raise fault.UnauthorizedFault("Unauthorized on this tenant") @@ -55,7 +55,7 @@ class IdentityService(object): raise fault.UserDisabledFault("Your account has been disabled") if duser.password != utils.get_hashed_password(credentials.password): raise fault.UnauthorizedFault("Unauthorized") - + # # Look for an existing token, or create one, # TODO: Handle tenant/token search @@ -82,22 +82,12 @@ class IdentityService(object): return self.__get_auth_data(dtoken, tenant_id) def validate_token(self, admin_token, token_id, belongs_to=None): - self.__validate_token(admin_token) - if not token_id: - raise fault.UnauthorizedFault("Missing token") - (token, user) = self.__get_dauth_data(token_id) - - if not token: - raise fault.UnauthorizedFault("Bad token, please reauthenticate") - if token.expires < datetime.now(): - raise fault.ForbiddenFault("Token expired, please renew") - if not user.enabled: - raise fault.UserDisabledFault("The user %s has been disabled!" - % user.id) + self.__validate_admin_token(admin_token) + (token, user) = self.__validate_token(token_id, belongs_to) return self.__get_validate_data(token, user) def revoke_token(self, admin_token, token_id): - self.__validate_token(admin_token) + self.__validate_admin_token(admin_token) dtoken = api.token.get(token_id) if not dtoken: @@ -110,7 +100,7 @@ class IdentityService(object): # def create_tenant(self, admin_token, tenant): - self.__validate_token(admin_token) + self.__validate_admin_token(admin_token) if not isinstance(tenant, Tenant): raise fault.BadRequestFault("Expecting a Tenant") @@ -135,7 +125,7 @@ class IdentityService(object): ## def get_tenants(self, admin_token, marker, limit, url): try: - (_token, user) = self.__validate_token(admin_token) + (_token, user) = self.__validate_admin_token(admin_token) # If Global admin return all ts = [] dtenants = api.tenant.get_page(marker, limit) @@ -145,34 +135,34 @@ class IdentityService(object): prev, next = api.tenant.get_page_markers(marker, limit) links = [] if prev: - links.append(atom.Link('prev', "%s?'marker=%s&limit=%s'" \ - % (url, prev, limit))) + links.append(atom.Link('prev', + "%s?'marker=%s&limit=%s'" % (url, prev, limit))) if next: - links.append(atom.Link('next', "%s?'marker=%s&limit=%s'" \ - % (url, next, limit))) + links.append(atom.Link('next', + "%s?'marker=%s&limit=%s'" % (url, next, limit))) return Tenants(ts, links) except fault.UnauthorizedFault: #If not global admin ,return tenants specific to user. (_token, user) = self.__validate_token(admin_token, False) ts = [] - dtenants = api.tenant.tenants_for_user_get_page(\ + dtenants = api.tenant.tenants_for_user_get_page( user, marker, limit) for dtenant in dtenants: ts.append(Tenant(dtenant.id, dtenant.desc, dtenant.enabled)) - prev, next = api.tenant.tenants_for_user_get_page_markers(\ + prev, next = api.tenant.tenants_for_user_get_page_markers( user, marker, limit) links = [] if prev: - links.append(atom.Link('prev', "%s?'marker=%s&limit=%s'" \ - % (url, prev, limit))) + links.append(atom.Link('prev', + "%s?'marker=%s&limit=%s'" % (url, prev, limit))) if next: - links.append(atom.Link('next', "%s?'marker=%s&limit=%s'" \ - % (url, next, limit))) + links.append(atom.Link('next', + "%s?'marker=%s&limit=%s'" % (url, next, limit))) return Tenants(ts, links) def get_tenant(self, admin_token, tenant_id): - self.__validate_token(admin_token) + self.__validate_admin_token(admin_token) dtenant = api.tenant.get(tenant_id) if not dtenant: @@ -180,11 +170,11 @@ class IdentityService(object): return Tenant(dtenant.id, dtenant.desc, dtenant.enabled) def update_tenant(self, admin_token, tenant_id, tenant): - self.__validate_token(admin_token) + self.__validate_admin_token(admin_token) if not isinstance(tenant, Tenant): raise fault.BadRequestFault("Expecting a Tenant") - + dtenant = api.tenant.get(tenant_id) if dtenant == None: raise fault.ItemNotFoundFault("The tenant could not be found") @@ -193,16 +183,16 @@ class IdentityService(object): return Tenant(dtenant.id, tenant.description, tenant.enabled) def delete_tenant(self, admin_token, tenant_id): - self.__validate_token(admin_token) - + self.__validate_admin_token(admin_token) + dtenant = api.tenant.get(tenant_id) if dtenant == None: raise fault.ItemNotFoundFault("The tenant could not be found") - + if not api.tenant.is_empty(tenant_id): raise fault.ForbiddenFault("You may not delete a tenant that " "contains get_users or groups") - + api.tenant.delete(dtenant.id) return None @@ -211,7 +201,7 @@ class IdentityService(object): # def create_tenant_group(self, admin_token, tenant, group): - self.__validate_token(admin_token) + self.__validate_admin_token(admin_token) if not isinstance(group, Group): raise fault.BadRequestFault("Expecting a Group") @@ -238,7 +228,7 @@ class IdentityService(object): return Group(dtenant.id, dtenant.desc, dtenant.tenant_id) def get_tenant_groups(self, admin_token, tenant_id, marker, limit, url): - self.__validate_token(admin_token) + self.__validate_admin_token(admin_token) if tenant_id == None: raise fault.BadRequestFault("Expecting a Tenant Id") @@ -266,7 +256,7 @@ class IdentityService(object): return Groups(ts, links) def get_tenant_group(self, admin_token, tenant_id, group_id): - self.__validate_token(admin_token) + self.__validate_admin_token(admin_token) dtenant = api.tenant.get(tenant_id) if dtenant == None: @@ -279,7 +269,7 @@ class IdentityService(object): return Group(dtenant.id, dtenant.desc, dtenant.tenant_id) def update_tenant_group(self, admin_token, tenant_id, group_id, group): - self.__validate_token(admin_token) + self.__validate_admin_token(admin_token) if not isinstance(group, Group): raise fault.BadRequestFault("Expecting a Group") @@ -308,7 +298,7 @@ class IdentityService(object): return Group(group_id, group.description, tenant_id) def delete_tenant_group(self, admin_token, tenant_id, group_id): - self.__validate_token(admin_token) + self.__validate_admin_token(admin_token) dtenant = api.tenant.get(tenant_id) @@ -328,7 +318,7 @@ class IdentityService(object): def get_users_tenant_group(self, admin_token, tenantId, groupId, marker, limit, url): - self.__validate_token(admin_token) + self.__validate_admin_token(admin_token) if tenantId == None: raise fault.BadRequestFault("Expecting a Tenant Id") @@ -363,7 +353,7 @@ class IdentityService(object): return Users(ts, links) def add_user_tenant_group(self, admin_token, tenant, group, user): - self.__validate_token(admin_token) + self.__validate_admin_token(admin_token) if api.tenant.get(tenant) == None: raise fault.ItemNotFoundFault("The Tenant not found") @@ -397,7 +387,7 @@ class IdentityService(object): group_id=group) #attribute no longer exists def delete_user_tenant_group(self, admin_token, tenant, group, user): - self.__validate_token(admin_token) + self.__validate_admin_token(admin_token) if api.tenant.get(tenant) == None: raise fault.ItemNotFoundFault("The Tenant not found") @@ -437,7 +427,7 @@ class IdentityService(object): # User Operations # def create_user(self, admin_token, user): - self.__validate_token(admin_token) + self.__validate_admin_token(admin_token) dtenant = self.validate_and_fetch_user_tenant(user.tenant_id) @@ -452,8 +442,7 @@ class IdentityService(object): "An user with that id already exists") if api.user.get_by_email(user.email) != None: - raise fault.EmailConflictFault( - "Email already exists") + raise fault.EmailConflictFault("Email already exists") duser = models.User() duser.id = user.user_id @@ -478,7 +467,7 @@ class IdentityService(object): return None def get_tenant_users(self, admin_token, tenant_id, marker, limit, url): - self.__validate_token(admin_token) + self.__validate_admin_token(admin_token) if tenant_id == None: raise fault.BadRequestFault("Expecting a Tenant Id") @@ -506,7 +495,7 @@ class IdentityService(object): return Users(ts, links) def get_users(self, admin_token, marker, limit, url): - self.__validate_token(admin_token) + self.__validate_admin_token(admin_token) ts = [] dusers = api.user.users_get_page(marker, limit) for duser in dusers: @@ -524,7 +513,7 @@ class IdentityService(object): return Users(ts, links) def get_user(self, admin_token, user_id): - self.__validate_token(admin_token) + self.__validate_admin_token(admin_token) duser = api.user.get(user_id) if not duser: raise fault.ItemNotFoundFault("The user could not be found") @@ -544,7 +533,7 @@ class IdentityService(object): duser.email, duser.enabled, ts) def update_user(self, admin_token, user_id, user): - self.__validate_token(admin_token) + self.__validate_admin_token(admin_token) duser = api.user.get(user_id) @@ -569,7 +558,7 @@ class IdentityService(object): duser.email, duser.enabled) def set_user_password(self, admin_token, user_id, user): - self.__validate_token(admin_token) + self.__validate_admin_token(admin_token) duser = api.user.get(user_id) if not duser: @@ -593,7 +582,7 @@ class IdentityService(object): None, None, None, None, None) def enable_disable_user(self, admin_token, user_id, user): - self.__validate_token(admin_token) + self.__validate_admin_token(admin_token) duser = api.user.get(user_id) if not duser: raise fault.ItemNotFoundFault("The user could not be found") @@ -612,7 +601,7 @@ class IdentityService(object): None, None, None, user.enabled, None) def set_user_tenant(self, admin_token, user_id, user): - self.__validate_token(admin_token) + self.__validate_admin_token(admin_token) duser = api.user.get(user_id) if not duser: raise fault.ItemNotFoundFault("The user could not be found") @@ -630,7 +619,7 @@ class IdentityService(object): None, user.tenant_id, None, None, None) def delete_user(self, admin_token, user_id): - self.__validate_token(admin_token) + self.__validate_admin_token(admin_token) duser = api.user.get(user_id) if not duser: raise fault.ItemNotFoundFault("The user could not be found") @@ -644,7 +633,7 @@ class IdentityService(object): def get_user_groups(self, admin_token, user_id, marker, limit, url): - self.__validate_token(admin_token) + self.__validate_admin_token(admin_token) ts = [] dusergroups = api.group.get_by_user_get_page(user_id, marker, limit) @@ -682,7 +671,7 @@ class IdentityService(object): return dtenant def create_global_group(self, admin_token, group): - self.__validate_token(admin_token) + self.__validate_admin_token(admin_token) if not isinstance(group, GlobalGroup): raise fault.BadRequestFault("Expecting a Group") @@ -702,7 +691,7 @@ class IdentityService(object): return GlobalGroup(dtenant.id, dtenant.desc, None) def get_global_groups(self, admin_token, marker, limit, url): - self.__validate_token(admin_token) + self.__validate_admin_token(admin_token) gtenant = self.__check_create_global_tenant() ts = [] dtenantgroups = api.tenant_group.get_page(gtenant.id, \ @@ -722,7 +711,7 @@ class IdentityService(object): return GlobalGroups(ts, links) def get_global_group(self, admin_token, group_id): - self.__validate_token(admin_token) + self.__validate_admin_token(admin_token) gtenant = self.__check_create_global_tenant() dtenant = api.tenant.get(gtenant.id) if dtenant == None: @@ -735,7 +724,7 @@ class IdentityService(object): return GlobalGroup(dtenant.id, dtenant.desc) def update_global_group(self, admin_token, group_id, group): - self.__validate_token(admin_token) + self.__validate_admin_token(admin_token) gtenant = self.__check_create_global_tenant() if not isinstance(group, GlobalGroup): raise fault.BadRequestFault("Expecting a Group") @@ -756,7 +745,7 @@ class IdentityService(object): return GlobalGroup(group_id, group.description, gtenant.id) def delete_global_group(self, admin_token, group_id): - self.__validate_token(admin_token) + self.__validate_admin_token(admin_token) gtenant = self.__check_create_global_tenant() dtenant = api.tenant.get(gtenant.id) @@ -775,7 +764,7 @@ class IdentityService(object): return None def get_users_global_group(self, admin_token, groupId, marker, limit, url): - self.__validate_token(admin_token) + self.__validate_admin_token(admin_token) gtenant = self.__check_create_global_tenant() if gtenant.id == None: @@ -810,7 +799,7 @@ class IdentityService(object): return Users(ts, links) def add_user_global_group(self, admin_token, group, user): - self.__validate_token(admin_token) + self.__validate_admin_token(admin_token) gtenant = self.__check_create_global_tenant() if api.tenant.get(gtenant.id) == None: @@ -844,7 +833,7 @@ class IdentityService(object): group_id=group) # attribute no longer exists! def delete_user_global_group(self, admin_token, group, user): - self.__validate_token(admin_token) + self.__validate_admin_token(admin_token) gtenant = self.__check_create_global_tenant() if api.tenant.get(gtenant.id) == None: @@ -890,34 +879,59 @@ class IdentityService(object): for droleRef in droleRefs: ts.append(RoleRef(droleRef.id, droleRef.role_id, droleRef.tenant_id)) - user = auth.User(duser.id, duser.tenant_id, None, RoleRefs(ts, - [])) + user = auth.User(duser.id, duser.tenant_id, None, RoleRefs(ts, [])) return auth.ValidateData(token, user) - def __validate_token(self, token_id, admin=True): + def __validate_tenant(self, tenant_id): + if not tenant_id: + raise fault.UnauthorizedFault("Missing tenant") + + tenant = api.tenant.get(tenant_id) + + if not tenant.enabled: + raise fault.TenantDisabledFault("Tenant %s has been disabled!" + % tenant.id) + + def __validate_token(self, token_id, belongs_to=None): if not token_id: raise fault.UnauthorizedFault("Missing token") + (token, user) = self.__get_dauth_data(token_id) if not token: raise fault.ItemNotFoundFault("Bad token, please reauthenticate") + if token.expires < datetime.now(): raise fault.ForbiddenFault("Token expired, please renew") + if not user.enabled: - raise fault.UserDisabledFault("The user %s has been disabled!" + raise fault.UserDisabledFault("User %s has been disabled!" % user.id) - if admin: - roleRefs = api.role.ref_get_all_global_roles(user.id) - for roleRef in roleRefs: - if roleRef.role_id == backends.KeyStoneAdminRole\ - and roleRef.tenant_id is None: - return (token, user) - raise fault.UnauthorizedFault("You are not authorized " - "to make this call") + + if user.tenant_id: + self.__validate_tenant(user.tenant_id) + + if token.tenant_id: + self.__validate_tenant(token.tenant_id) + + if belongs_to and token.tenant_id != belongs_to: + raise fault.UnauthorizedFault("Unauthorized on this tenant") + return (token, user) + + def __validate_admin_token(self, token_id): + (token, user) = self.__validate_token(token_id) + + for roleRef in api.role.ref_get_all_global_roles(user.id): + if roleRef.role_id == backends.KeyStoneAdminRole and \ + roleRef.tenant_id is None: + return (token, user) + + raise fault.UnauthorizedFault( + "You are not authorized to make this call") def create_role(self, admin_token, role): - self.__validate_token(admin_token) + self.__validate_admin_token(admin_token) if not isinstance(role, Role): raise fault.BadRequestFault("Expecting a Role") @@ -935,7 +949,7 @@ class IdentityService(object): return role def get_roles(self, admin_token, marker, limit, url): - self.__validate_token(admin_token) + self.__validate_admin_token(admin_token) ts = [] droles = api.role.get_page(marker, limit) @@ -953,7 +967,7 @@ class IdentityService(object): return Roles(ts, links) def get_role(self, admin_token, role_id): - self.__validate_token(admin_token) + self.__validate_admin_token(admin_token) drole = api.role.get(role_id) if not drole: @@ -961,7 +975,7 @@ class IdentityService(object): return Role(drole.id, drole.desc) def create_role_ref(self, admin_token, user_id, roleRef): - self.__validate_token(admin_token) + self.__validate_admin_token(admin_token) duser = api.user.get(user_id) if not duser: @@ -992,12 +1006,12 @@ class IdentityService(object): return roleRef def delete_role_ref(self, admin_token, role_ref_id): - self.__validate_token(admin_token) + self.__validate_admin_token(admin_token) api.role.ref_delete(role_ref_id) return None def get_user_roles(self, admin_token, marker, limit, url, user_id): - self.__validate_token(admin_token) + self.__validate_admin_token(admin_token) duser = api.user.get(user_id) if not duser: @@ -1019,7 +1033,7 @@ class IdentityService(object): return RoleRefs(ts, links) def get_endpoint_templates(self, admin_token, marker, limit, url): - self.__validate_token(admin_token) + self.__validate_admin_token(admin_token) ts = [] dendpointTemplates = api.endpoint_template.get_page(marker, limit) @@ -1044,7 +1058,7 @@ class IdentityService(object): return EndpointTemplates(ts, links) def get_endpoint_template(self, admin_token, endpoint_template_id): - self.__validate_token(admin_token) + self.__validate_admin_token(admin_token) dendpointTemplate = api.endpoint_template.get(endpoint_template_id) if not dendpointTemplate: @@ -1061,7 +1075,7 @@ class IdentityService(object): dendpointTemplate.is_global) def get_tenant_endpoints(self, admin_token, marker, limit, url, tenant_id): - self.__validate_token(admin_token) + self.__validate_admin_token(admin_token) if tenant_id == None: raise fault.BadRequestFault("Expecting a Tenant Id") @@ -1093,7 +1107,7 @@ class IdentityService(object): def create_endpoint_for_tenant(self, admin_token, tenant_id, endpoint_template, url): - self.__validate_token(admin_token) + self.__validate_admin_token(admin_token) if tenant_id == None: raise fault.BadRequestFault("Expecting a Tenant Id") if api.tenant.get(tenant_id) == None: @@ -1112,6 +1126,6 @@ class IdentityService(object): return dendpoint def delete_endpoint(self, admin_token, endpoint_id): - self.__validate_token(admin_token) + self.__validate_admin_token(admin_token) api.endpoint_template.endpoint_delete(endpoint_id) return None -- cgit From 6a012ffd3b46518019a8ecbf7c5285e890ab15fa Mon Sep 17 00:00:00 2001 From: Dolph Mathews Date: Tue, 19 Jul 2011 15:49:22 -0500 Subject: Restored UnauthorizedFaults to token validation requests --- keystone/logic/service.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/keystone/logic/service.py b/keystone/logic/service.py index 99abe568..eefcdfc2 100755 --- a/keystone/logic/service.py +++ b/keystone/logic/service.py @@ -83,7 +83,12 @@ class IdentityService(object): def validate_token(self, admin_token, token_id, belongs_to=None): self.__validate_admin_token(admin_token) + + if not api.token.get(token_id): + raise fault.UnauthorizedFault("Bad token, please reauthenticate") + (token, user) = self.__validate_token(token_id, belongs_to) + return self.__get_validate_data(token, user) def revoke_token(self, admin_token, token_id): -- cgit