def validate_request(req, path_pattern, path_params, spec, url): print('Validating URL: {}'.format(url)) counters['requests'] += 1 openapi_request = RequestsOpenAPIRequest(req, path_pattern, path_params) validator = RequestValidator(spec) result = validator.validate(openapi_request) request_errors = result.errors r = req.prepare() s = requests.Session() res = s.send(r) openapi_response = RequestsOpenAPIResponse(res) validator = ResponseValidator(spec) result = validator.validate(openapi_request, openapi_response) response_errors = result.errors print('Request errors: {}'.format(request_errors)) print('Response errors: {}'.format(response_errors)) if request_errors or response_errors: errors_count = len(request_errors) + len(response_errors) print(color(' [FAIL] {:d} errors found '.format(errors_count), fg='white', bg='red', style='bold')) print("Response body: {}".format(res.text)) else: errors_count = 0 print(color(' [PASS] No errors found ', fg='white', bg='green', style='bold')) print() return errors_count
def validate(req, resp, path, query): """Validate a query and its response.""" settings = utils.setup.Settings() warnings = [] if settings.openapi: # check that the query complies to the api spec validator = RequestValidator(settings.openapi) result = validator.validate(req) settings.query_warnings += map(str, result.errors) warnings.extend(['OpenApi: ' + str(x) for x in result.errors]) # check that the response complies to the api spec validator = ResponseValidator(settings.openapi) result = validator.validate(req, resp) settings.warnings += map(str, result.errors) warnings.extend(['OpenAPI: ' + str(x) for x in result.errors]) if settings.use_json_schemas: if path != '/' and query is not None: # validate query against jsons schemas q_warns = utils.jsonschemas.validate(query, 'query', settings) warnings.extend(q_warns) settings.query_warnings += q_warns # validate response against jsons schemas warns = utils.jsonschemas.validate(resp.data, 'response', settings, path) warnings.extend(warns) settings.warnings += warns for warning in warnings: logging.warning(warning) return json.loads(resp.data)
def post(self): logger.debug("top of POST /ldaps") validator = RequestValidator(utils.spec) result = validator.validate(FlaskOpenAPIRequest(request)) if result.errors: raise errors.ResourceError(msg=f'Invalid POST data: {result.errors}.') validated_params = result.parameters validated_body = result.body logger.debug(f"validated_body: {dir(validated_body)}") ldap = LDAPConnection(ldap_id=validated_body.ldap_id, url=validated_body.url, port=validated_body.port, use_ssl=validated_body.use_ssl, user_dn=validated_body.user_dn, bind_dn=validated_body.bind_dn, bind_credential=validated_body.bind_credential, account_type=validated_body.account_type, create_time=datetime.datetime.utcnow(), last_update_time = datetime.datetime.utcnow()) db.session.add(ldap) try: db.session.commit() except (sqlalchemy.exc.SQLAlchemyError, sqlalchemy.exc.DBAPIError) as e: msg = utils.get_message_from_sql_exc(e) raise errors.ResourceError(f"Invalid POST data; {msg}") return utils.ok(result=ldap.serialize, msg="LDAP object created successfully.")
def post(self): logger.debug("top of POST /tokens") validator = RequestValidator(utils.spec) validated = validator.validate(FlaskOpenAPIRequest(request)) if validated.errors: raise errors.ResourceError(msg=f'Invalid POST data: {validated.errors}.') validated_body = validated.body # this raises an exception if the claims are invalid - if hasattr(validated_body, 'claims'): check_extra_claims(request.json.get('claims')) # set it to the raw request's claims object which is an arbitrary python dictionary validated_body.claims = request.json.get('claims') try: token_data = TapisAccessToken.get_derived_values(validated_body) except Exception as e: logger.error(f"Got exception trying to compute get_derived_values() for validated body; e: {e}") raise errors.AuthenticationError("Unable to create token. Please contact system administrator.") access_token = TapisAccessToken(**token_data) try: access_token.sign_token() except Exception as e: logger.error(f"Got exception trying to sign token! Exception: {e}") raise errors.AuthenticationError("Unable to sign token. Please contact system administrator.") result = {'access_token': access_token.serialize} # refresh token -- if hasattr(validated_body, 'generate_refresh_token') and validated_body.generate_refresh_token: if hasattr(validated_body, 'refresh_token_ttl'): token_data['refresh_token_ttl'] = validated_body.refresh_token_ttl refresh_token = TokensResource.get_refresh_from_access_token_data(token_data, access_token) result['refresh_token'] = refresh_token.serialize return utils.ok(result=result, msg="Token generation successful.")
def put(self): logger.debug("top of PUT /tokens/keys") validator = RequestValidator(utils.spec) validated = validator.validate(FlaskOpenAPIRequest(request)) if validated.errors: raise errors.ResourceError(msg=f'Invalid PUT data: {validated.errors}.') tenant_id = validated.body.tenant_id logger.debug(f"calling check_authz_private_keypair with tenant_id {tenant_id}") check_authz_private_keypair(tenant_id) logger.debug("returned from check_authz_private_keypair; updating keys...") private_key, public_key = generate_private_keypair_in_sk(tenant_id) # update the tenant definition with the new public key logger.debug(f"making request to update tenant {tenant_id} with new public key.") try: t.tenants.update_tenant(tenant_id=tenant_id, public_key=public_key) except Exception as e: logger.error(f"Got exception trying to update tenant with new public key. Tenants API" f"and SK are now out of sync!! SHOULD BE LOOKED AT IMMEDIATELY. " f"Exception: {e}") raise errors.ResourceError(msg=f'Unable to update tenant definition with new public key' f'Please contact system administrators.') logger.info(f"tenant {tenant_id} has been updated with the new public key.") # update token's tenant cache with this private key for signing: logger.debug("updating token cache...") for tenant in tenants.tenants: if tenant.tenant_id == tenant_id: tenant.private_key = private_key result = {'public_key': public_key} return utils.ok(result=result, msg="Tenant signing keys update successful.")
def put(self): try: validator = RequestValidator(utils.spec) validated = validator.validate(FlaskOpenAPIRequest(request)) if validated.errors: raise errors.ResourceError( msg=f'Invalid PUT data: {validated.errors}.') refresh_token = validated.body.refresh_token refresh_token_data = auth.validate_token(refresh_token) token_data = refresh_token_data['access_token'] token_data.pop('token_type') token_data['exp'] = TapisAccessToken.compute_exp(token_data['ttl']) access_token = TapisAccessToken(**token_data) access_token.sign_token() refresh_token = TokensResource.get_refresh_from_access_token_data( token_data, access_token) result = { 'access_token': access_token.serialize, 'refres_token': refresh_token.serialize } return utils.ok(result=result, msg="Token generation successful.") except Exception as e: # return utils.ok(result="Got exception", msg=f"{refresh_token.serialize}") return utils.ok(result="Got exception", msg=f"Exception: {traceback.format_exc()}")
def post(self): logger.debug("top of POST /tokens") # try: validator = RequestValidator(utils.spec) validated = validator.validate(FlaskOpenAPIRequest(request)) if validated.errors: raise errors.ResourceError( msg=f'Invalid POST data: {validated.errors}.') validated_body = validated.body # this raises an exception of the claims are invalid - if hasattr(validated_body, 'extra_claims'): check_extra_claims(validated_body.extra_claims) token_data = TapisAccessToken.get_derived_values(validated_body) access_token = TapisAccessToken(**token_data) access_token.sign_token() result = {} result['access_token'] = access_token.serialize # refresh token -- if hasattr(validated_body, 'generate_refresh_token' ) and validated_body.generate_refresh_token: refresh_token = TokensResource.get_refresh_from_access_token_data( token_data, access_token) result['refresh_token'] = refresh_token.serialize return utils.ok(result=result, msg="Token generation successful.")
def post(self): logger.debug("top of POST /sites") validator = RequestValidator(utils.spec) result = validator.validate(FlaskOpenAPIRequest(request)) logger.debug(f"just got result {result.parameters}") if result.errors: logger.debug(f"error in results!!!!!!!!") raise errors.ResourceError(msg=f'Invalid POST data: {result.errors}') validated_params = result.parameters logger.debug('got validated params') validated_body = result.body logger.debug(f'got validated body {dir(validated_body)}') try: # request is trying to create the primary site: if validated_body.primary: logger.debug('checks for primary site') primary_site = Site.query.filter_by(primary=True).first() if primary_site: raise errors.ResourceError("Invalid site description: a primary site already exists.") if not validated_body.tenant_base_url_template: raise errors.ResourceError("Invalid site description: tenant_base_url_template is required for primary site.") site = Site(site_id=validated_body.site_id, primary=validated_body.primary, base_url=validated_body.base_url, tenant_base_url_template=validated_body.tenant_base_url_template, site_admin_tenant_id=validated_body.site_admin_tenant_id, services=validated_body.services, create_time=datetime.datetime.utcnow(), created_by=f'{g.username}@{g.tenant_id}', last_updated_by=f'{g.username}@{g.tenant_id}', last_update_time=datetime.datetime.utcnow()) # request if for an associate site: else: logger.debug(f'checks for associate site.') if hasattr(validated_body, 'tenant_base_url_template') and validated_body.tenant_base_url_template: raise errors.ResourceError("Invalid site description; " "the tenant_base_url_template property only applies to primary sites.") site = Site(site_id=validated_body.site_id, primary=False, base_url=validated_body.base_url, site_admin_tenant_id=validated_body.site_admin_tenant_id, services=validated_body.services, create_time=datetime.datetime.utcnow(), created_by=f'{g.username}@{g.tenant_id}', last_updated_by=f'{g.username}@{g.tenant_id}', last_update_time=datetime.datetime.utcnow()) logger.info(f'creating site {validated_body.site_id}') except Exception as e: raise errors.ResourceError(f"Invalid POST data; {e}") db.session.add(site) try: db.session.commit() except (sqlalchemy.exc.SQLAlchemyError, sqlalchemy.exc.DBAPIError) as e: msg = utils.get_message_from_sql_exc(e) raise errors.ResourceError(f"Invalid POST data; {msg}") logger.info(f"site {validated_body.site_id} saved in db.") return utils.ok(result=site.serialize, msg="Site object created successfully.")
def validate(req, resp, path, query): """Validate a query and its response.""" settings = utils.setup.Settings() warnings = [] if settings.openapi: # check that the query complies to the api spec validator = RequestValidator(settings.openapi) result = validator.validate(req) settings.query_warnings += map(str, result.errors) warnings.extend(['OpenApi: ' + str(x) for x in result.errors]) # check that the response complies to the api spec validator = ResponseValidator(settings.openapi) result = validator.validate(req, resp) settings.warnings += map(str, result.errors) for error in result.errors: if isinstance(error, InvalidSchemaValue): warning = f'OpenAPI:\n\tAt object {error.value}\n\t' warning += "\n\t".join(prettify_schemaerror(error)) warnings.append(warning) else: warnings.append(f'OpenAPI: {str(error)}') if settings.use_json_schemas: if path != '/' and query is not None: # validate query against jsons schemas q_warns = utils.jsonschemas.validate(query, 'query', settings) warnings.extend(q_warns) settings.query_warnings += q_warns # validate response against jsons schemas warns = utils.jsonschemas.validate(resp.data, 'response', settings, path) warnings.extend(warns) settings.warnings += warns for warning in warnings: logging.warning(warning) return json.loads(resp.data)
def post(self): validator = RequestValidator(utils.spec) result = validator.validate(FlaskOpenAPIRequest(request)) if result.errors: raise errors.ResourceError( msg=f'Invalid POST data: {result.errors}.') validated_body = result.body data = Client.get_derived_values(validated_body) client = Client(**data) db.session.add(client) db.session.commit() return utils.ok(result=client.serialize, msg="Client created successfully.")
def validate_request(req, path_pattern, path_params, spec, url): print('Validating URL: {}'.format(url)) counters['requests'] += 1 openapi_request = RequestsOpenAPIRequest(req, path_pattern, path_params) validator = RequestValidator(spec) result = validator.validate(openapi_request) request_errors = result.errors r = req.prepare() s = requests.Session() res = s.send(r) openapi_response = RequestsOpenAPIResponse(res) validator = ResponseValidator(spec) result = validator.validate(openapi_request, openapi_response) response_errors = result.errors print('Request errors: {}'.format(request_errors)) print('Response errors: {}'.format(response_errors)) if request_errors or response_errors: errors_count = len(request_errors) + len(response_errors) print( color(' [FAIL] {:d} errors found '.format(errors_count), fg='white', bg='red', style='bold')) print("Response body: {}".format(res.text)) else: errors_count = 0 print( color(' [PASS] No errors found ', fg='white', bg='green', style='bold')) print() return errors_count
class OpenApiSpecManager(SharedExtension): _loaded = Event() def setup(self): log.info(' ### OpenApiSpecManager.setup') super().setup() def load_spec(self, spec_file): log.debug('%s.load_spec: %s' % (self.__class__.__name__, spec_file)) # TODO: supporting loading from url instead of just file # TODO: How to handle/interpret/respect spec.servers[].url's? # TODO: Or should this be generated/injected into the spec_dict on startup? #spec_file = '/home/sar/vcs/nameko-openapi/petstore.yaml' spec_dict = yaml.safe_load(open(spec_file)) self.spec = openapi_core.create_spec(spec_dict) self.request_validator = RequestValidator(self.spec) self.response_validator = ResponseValidator(self.spec) self._loaded.send(self.spec) def wait_for_spec(self): """Allow other extensions to wait until the spec is loaded.""" return self._loaded.wait() def get_operation_by_id(self, operation_id): self.wait_for_spec() for path_name, path in six.iteritems(self.spec.paths): for http_method, operation in six.iteritems(path.operations): if operation.operation_id == operation_id: return operation def validate_request(self, request, raise_for_errors=True): result = self.request_validator.validate(request) if raise_for_errors: result.raise_for_errors() return result def validate_response(self, response, openapi_request, raise_for_errors=True): result = self.response_validator.validate(openapi_request, response) if raise_for_errors: result.raise_for_errors() return result
def put(self): logger.debug("top of PUT /tokens") validator = RequestValidator(utils.spec) validated = validator.validate(FlaskOpenAPIRequest(request)) if validated.errors: raise errors.ResourceError(msg=f'Invalid PUT data: {validated.errors}.') refresh_token = validated.body.refresh_token logger.debug(f"type(refresh_token) = {type(refresh_token)}") try: refresh_token_data = auth.validate_token(refresh_token) except errors.AuthenticationError: raise errors.ResourceError(msg=f'Invalid PUT data: {request}.') # get the original access_token data from within the decoded refresh_token token_data = refresh_token_data['tapis/access_token'] token_data.pop('tapis/token_type') token_data['exp'] = TapisAccessToken.compute_exp(token_data['ttl']) token_data['jti'] = str(uuid.uuid4()) # create a dictionary of data that can be used to instantiate access and refresh tokens; the constructors # require variable names that do not include the Tapis prefix, so we need to remove that - new_token_data = { 'jti': token_data.pop('jti'), 'iss': token_data.pop('iss'), 'sub': token_data.pop('sub'), 'tenant_id': token_data.pop('tapis/tenant_id'), 'username': token_data.pop('tapis/username'), 'account_type': token_data.pop('tapis/account_type'), 'ttl': token_data.pop('ttl'), 'exp': token_data.pop('exp'), 'delegation': token_data.pop('tapis/delegation'), 'delegation_sub': token_data.pop('tapis/delegation_sub', None), 'extra_claims': token_data } access_token = TapisAccessToken(**new_token_data) access_token.sign_token() # add the original refresh token's initial_ttl claim as the ttl for the new refresh token new_token_data['refresh_token_ttl'] = refresh_token_data['tapis/initial_ttl'] refresh_token = TokensResource.get_refresh_from_access_token_data(new_token_data, access_token) result = {'access_token': access_token.serialize, 'refresh_token': refresh_token.serialize } return utils.ok(result=result, msg="Token generation successful.")
def post(self): logger.debug(f"top of POST /owners") validator = RequestValidator(utils.spec) result = validator.validate(FlaskOpenAPIRequest(request)) if result.errors: raise errors.ResourceError(msg=f'Invalid POST data: {result.errors}.') validated_params = result.parameters validated_body = result.body owner = TenantOwner(name=validated_body.name, email=validated_body.email, institution=validated_body.institution, create_time=datetime.datetime.utcnow(), last_update_time=datetime.datetime.utcnow() ) db.session.add(owner) try: db.session.commit() except (sqlalchemy.exc.SQLAlchemyError, sqlalchemy.exc.DBAPIError) as e: msg = utils.get_message_from_sql_exc(e) raise errors.ResourceError(f"Invalid POST data; {msg}") return utils.ok(result=owner.serialize, msg="Owner object created successfully.")
def put(self, tenant_id): logger.debug(f"top of PUT /tenants/{tenant_id}") tenant = Tenant.query.filter_by(tenant_id=tenant_id).first() if not tenant: raise errors.ResourceError(msg=f'No tenant found with tenant_id {tenant_id}.') # additional authorization checks on update based on the tenant_id of the request: check_authz_tenant_update(tenant_id) validator = RequestValidator(utils.spec) result = validator.validate(FlaskOpenAPIRequest(request)) if result.errors: logger.debug(f"openapi_core validation failed. errors: {result.errors}") raise errors.ResourceError(msg=f'Invalid PUT data: {result.errors}.') validated_body = result.body logger.debug(f"initial openapi_core validation passed. validated_body: {dir(validated_body)}") # TODO -- # ------------------------- This DOES NOT WORK ------------------------------------ # the validated_body ONLY contains fields in the OAI spec; need to change this to look at the # request body itself if not getattr(validated_body, 'site_id', tenant.site_id) == tenant.site_id: raise errors.ResourceError(msg=f'Invalid PUT data: cannot change site_id.') if not getattr(validated_body, 'tenant_id', tenant.tenant_id) == tenant.tenant_id: raise errors.ResourceError(msg=f'Invalid PUT data: cannot change tenant_id.') if not getattr(validated_body, 'base_url', tenant.base_url) == tenant.base_url: raise errors.ResourceError(msg=f'Invalid PUT data: cannot change base_url.') # ------------------------------------------------------------------------------------ # validate the existence of the ldap and owner objects: if getattr(validated_body, 'owner', None): owner = TenantOwner.query.filter_by(email=validated_body.owner).first() if not owner: raise errors.ResourceError(msg=f'Invalid tenant description. Owner {validated_body.owner} not found.') logger.debug("owner was valid.") if getattr(validated_body, 'user_ldap_connection_id', None): ldap = LDAPConnection.query.filter_by(ldap_id=validated_body.user_ldap_connection_id).first() if not ldap: raise errors.ResourceError(msg=f'Invalid tenant description. ' f'LDAP {validated_body.user_ldap_connection_id} not found.') if getattr(validated_body, 'service_ldap_connection_id', None) and \ not validated_body.service_ldap_connection_id == getattr(validated_body, 'user_ldap_connection_id', None): ldap = LDAPConnection.query.filter_by(ldap_id=validated_body.service_ldap_connection_id).first() if not ldap: raise errors.ResourceError(msg=f'Invalid tenant description. ' f'LDAP {validated_body.service_ldap_connection_id} not found.') # overlay the tenant_current with the updates specified in the request body. changes_dict = {} # security_kernel new_security_kernel = getattr(validated_body, 'security_kernel', None) if new_security_kernel and not new_security_kernel == tenant.security_kernel: changes_dict['security_kernel'] = {'prev': tenant.security_kernel, 'new': new_security_kernel} tenant.security_kernel = new_security_kernel # token_service new_tokens_service = getattr(validated_body, 'token_service', None) if new_tokens_service and not new_tokens_service == tenant.token_service: changes_dict['tokens_service'] = {'prev': tenant.token_service, 'new': new_tokens_service} tenant.token_service = new_tokens_service # authenticator new_authenticator = getattr(validated_body, 'authenticator', None) if new_authenticator and not new_authenticator == tenant.authenticator: changes_dict['authenticator'] = {'prev': tenant.authenticator, 'new': new_authenticator} tenant.authenticator = new_authenticator # admin_user new_admin_user = getattr(validated_body, 'admin_user', None) if new_admin_user and not new_admin_user == tenant.admin_user: changes_dict['admin_user'] = {'prev': tenant.admin_user, 'new': new_admin_user} tenant.admin_user = new_admin_user # token_gen_services new_token_gen_services = getattr(validated_body, 'token_gen_services', None) if new_token_gen_services and not new_token_gen_services == tenant.token_gen_services: changes_dict['token_gen_services'] = {'prev': tenant.token_gen_services, 'new': new_token_gen_services} tenant.token_gen_services = new_token_gen_services # service_ldap_connection_id new_service_ldap_connection_id = getattr(validated_body, 'service_ldap_connection_id', None) if new_service_ldap_connection_id and not new_service_ldap_connection_id == tenant.service_ldap_connection_id: changes_dict['service_ldap_connection_id'] = {'prev': tenant.service_ldap_connection_id, 'new': new_service_ldap_connection_id} tenant.service_ldap_connection_id = new_service_ldap_connection_id # user_ldap_connection_id new_user_ldap_connection_id = getattr(validated_body, 'user_ldap_connection_id', None) if new_user_ldap_connection_id and not new_user_ldap_connection_id == tenant.user_ldap_connection_id: changes_dict['user_ldap_connection_id'] = {'prev': tenant.user_ldap_connection_id, 'new': new_user_ldap_connection_id} tenant.user_ldap_connection_id = new_user_ldap_connection_id # public_key new_public_key = getattr(validated_body, 'public_key', None) if new_public_key and not new_public_key == tenant.public_key: changes_dict['public_key'] = {'prev': tenant.public_key, 'new': new_public_key} tenant.public_key = new_public_key # status new_status = getattr(validated_body, 'status', None) if new_status and not new_status == tenant.status: changes_dict['status'] = {'prev': tenant.status.serialize, 'new': new_status.upper()} tenant.status = new_status # description new_description = getattr(validated_body, 'description', None) if new_description and not new_description == tenant.description: changes_dict['description'] = {'prev': tenant.description, 'new': new_description} tenant.description = new_description # owner new_owner = getattr(validated_body, 'owner', None) if new_owner and not new_owner == tenant.owner: changes_dict['owner'] = {'prev': tenant.owner, 'new': new_owner} tenant.owner = new_owner # last_update_time and last_updated_by update_time = datetime.datetime.utcnow() updated_by = f'{g.username}@{g.tenant_id}' tenant.last_update_time = update_time tenant.last_updated_by = updated_by # create the history record tenant_history = TenantHistory( tenant_id=tenant.tenant_id, update_time=update_time, updated_by=updated_by, updates_as_json=json.dumps(changes_dict) ) db.session.add(tenant_history) try: db.session.commit() logger.info(f"update to tenant committed to db. tenant object: {tenant}") except (sqlalchemy.exc.SQLAlchemyError, sqlalchemy.exc.DBAPIError) as e: logger.debug(f"got exception trying to commit updated tenant object to db. Exception: {e}") msg = utils.get_message_from_sql_exc(e) logger.debug(f"returning msg: {msg}") raise errors.ResourceError(f"Invalid PUT data; {msg}") logger.debug("returning serialized tenant object.") return utils.ok(result=tenant.serialize, msg="Tenant updated successfully.")
def post(self): logger.debug(f"top of POST /tenants") validator = RequestValidator(utils.spec) result = validator.validate(FlaskOpenAPIRequest(request)) if result.errors: logger.debug(f"openapi_core validation failed. errors: {result.errors}") raise errors.ResourceError(msg=f'Invalid POST data: {result.errors}.') validated_body = result.body logger.debug(f"initial openapi_core validation passed. validated_body: {dir(validated_body)}") # check reserved words "owners" and "ldaps" -- these cannot be tenant id's: try: if validated_body.tenant_id.lower() == 'owners': raise errors.ResourceError("Invalid tenant_id; 'owners' is a reserved keyword.") if validated_body.tenant_id.lower() == 'ldaps': raise errors.ResourceError("Invalid tenant_id; 'ldaps' is a reserved keyword.") if validated_body.tenant_id.lower() == 'ready': raise errors.ResourceError("Invalid tenant_id; 'ready' is a reserved keyword.") if validated_body.tenant_id.lower() == 'hello': raise errors.ResourceError("Invalid tenant_id; 'hello' is a reserved keyword.") except Exception as e: msg = f"Could not check tenant description for reserved words; Errors: {e}" logger.error(msg) raise errors.ResourceError(msg) logger.debug("got past the reserved words check.") # validate the existence of the site object: try: site_id = validated_body.site_id site = Site.query.filter_by(site_id=site_id).first() except Exception as e: logger.error(f"Got exception trying to retrieve site; e: {e}") raise errors.ResourceError(msg='Invalid tenant description; could not verify site_id.') if not site: raise errors.ResourceError(msg=f'Invalid tenant description. site {validated_body.site_id} not found.') logger.debug(f"site_id {site_id} is ok.") # validate the existence of the ldap and owner objects: owner = TenantOwner.query.filter_by(email=validated_body.owner).first() if not owner: raise errors.ResourceError(msg=f'Invalid tenant description. Owner {validated_body.owner} not found.') logger.debug("owner was valid.") # ldap objects are optional: if getattr(validated_body, 'user_ldap_connection_id', None): ldap = LDAPConnection.query.filter_by(ldap_id=validated_body.user_ldap_connection_id).first() if not ldap: raise errors.ResourceError(msg=f'Invalid tenant description. ' f'LDAP {validated_body.user_ldap_connection_id} not found.') if getattr(validated_body, 'service_ldap_connection_id', None) and \ not validated_body.service_ldap_connection_id == getattr(validated_body, 'user_ldap_connection_id', None): ldap = LDAPConnection.query.filter_by(ldap_id=validated_body.service_ldap_connection_id).first() if not ldap: raise errors.ResourceError(msg=f'Invalid tenant description. ' f'LDAP {validated_body.service_ldap_connection_id} not found.') logger.debug("ldap was valid; creating tenant record..") # create the tenant record -- tenant = Tenant(tenant_id=validated_body.tenant_id, base_url=validated_body.base_url, site_id=validated_body.site_id, status=validated_body.status, public_key=getattr(validated_body, 'public_key', None), token_service=validated_body.token_service, security_kernel=validated_body.security_kernel, authenticator=validated_body.authenticator, owner=validated_body.owner, admin_user=validated_body.admin_user, token_gen_services=validated_body.token_gen_services, service_ldap_connection_id=getattr(validated_body, 'service_ldap_connection_id', None), user_ldap_connection_id=getattr(validated_body, 'user_ldap_connection_id', None), description=getattr(validated_body, 'description', None), create_time=datetime.datetime.utcnow(), created_by=f'{g.username}@{g.tenant_id}', last_updated_by=f'{g.username}@{g.tenant_id}', last_update_time=datetime.datetime.utcnow()) db.session.add(tenant) try: db.session.commit() logger.info(f"new tenant committed to db. tenant object: {tenant}") except (sqlalchemy.exc.SQLAlchemyError, sqlalchemy.exc.DBAPIError) as e: logger.debug(f"got exception trying to commit new tenant object to db. Exception: {e}") msg = utils.get_message_from_sql_exc(e) logger.debug(f"returning msg: {msg}") raise errors.ResourceError(f"Invalid POST data; {msg}") logger.debug("returning serialized tenant object.") return utils.ok(result=tenant.serialize, msg="Tenant created successfully.")