def _validate_set_ldap_request(self): if not self._only_system_reserved_users_in_manager(): raise MethodNotAllowedError('LDAP Configuration may be set only on' ' a clean manager.') if not premium_enabled: raise MethodNotAllowedError('LDAP is only supported in the ' 'Cloudify premium edition.') ldap_config = rest_utils.get_json_and_verify_params({ 'ldap_server': {}, 'ldap_username': { 'optional': True }, 'ldap_password': { 'optional': True }, 'ldap_domain': {}, 'ldap_is_active_directory': { 'optional': True }, 'ldap_dn_extra': {}, 'ldap_ca_cert': { 'optional': True }, }) # Not allowing empty username or password ldap_config['ldap_username'] = ldap_config.get('ldap_username', '') ldap_config['ldap_password'] = ldap_config.get('ldap_password', '') ldap_config['ldap_is_active_directory'] = \ rest_utils.verify_and_convert_bool( 'ldap_is_active_directory', ldap_config.get('ldap_is_active_directory') or False ) if ldap_config['ldap_server'].startswith('ldaps://'): if 'ldap_ca_cert' not in ldap_config: raise BadParametersError( 'A CA certificate must be provided to use ldaps.') elif ldap_config['ldap_server'].startswith('ldap://'): if 'ldap_ca_cert' in ldap_config: raise BadParametersError( 'CA certificate cannot be provided when not using ldaps.') else: raise BadParametersError( 'ldap_server must specify protocol and should specify port, ' 'e.g. ldap://192.0.2.1:389 or ldaps://192.0.2.45:636') if ((ldap_config['ldap_username'] and not ldap_config['ldap_password']) or (ldap_config['ldap_password'] and not ldap_config['ldap_username'])): raise BadParametersError( 'Must supply either both username and password or neither. ' 'Note that an empty username or password is invalid') return ldap_config
def post(self): ldap_config = self._validate_set_ldap_request() from cloudify_premium.multi_tenancy.ldap_authentication \ import LdapAuthentication # update current configuration for key, value in ldap_config.iteritems(): setattr(config.instance, key, value) # assert LDAP configuration is valid. authenticator = LdapAuthentication() authenticator.configure_ldap() try: authenticator.authenticate_user(ldap_config.get('ldap_username'), ldap_config.get('ldap_password')) except UnauthorizedError: # reload previous configuration. config.instance.load_configuration() raise BadParametersError( 'Failed setting LDAP authenticator: Invalid parameters ' 'provided.') config.reset(config.instance, write=True) # Restart the rest service so that each the LDAP configuration # be loaded to all flask processes. rest_utils.set_restart_task() ldap_config.pop('ldap_password') return ldap_config
def post(self, username, multi_tenancy): """ Set password/role for a certain user """ request_dict = rest_utils.get_json_and_verify_params() password = request_dict.get('password') role_name = request_dict.get('role') if password: if role_name: raise BadParametersError('Both `password` and `role` provided') password = rest_utils.validate_and_decode_password(password) return multi_tenancy.set_user_password(username, password) elif role_name: return multi_tenancy.set_user_role(username, role_name) else: raise BadParametersError('Neither `password` nor `role` provided')
def _validate_detach_site(self, site_name): detach_site = request.json.get('detach_site') if (site_name and detach_site) or (not site_name and not detach_site): raise BadParametersError( "Must provide either a `site_name` of a valid site or " "`detach_site` with true value for detaching the current " "site of the given deployment")
def post(self): ldap_config = self._validate_set_ldap_request() from cloudify_premium.authentication.ldap_authentication \ import LdapAuthentication # update current configuration config.instance.update_db(ldap_config) # assert LDAP configuration is valid (if credential supplied). if ldap_config['ldap_username']: auth = LdapAuthentication() auth.configure(current_app.logger) try: auth.authenticate_user(ldap_config.get('ldap_username'), ldap_config.get('ldap_password')) except UnauthorizedError: # reload previous configuration. config.instance.load_configuration() raise BadParametersError( 'Failed setting LDAP authenticator: Invalid parameters ' 'provided.') # Restart the rest service so that each the LDAP configuration # be loaded to all flask processes. rest_utils.set_restart_task() ldap_config.pop('ldap_password') return ldap_config
def post(self, maintenance_action, **_): maintenance_file_path = get_maintenance_file_path() if maintenance_action == 'activate': if os.path.isfile(maintenance_file_path): state = utils.read_json_file(maintenance_file_path) return state, 304 now = utils.get_formatted_timestamp() try: user = current_user.username except AttributeError: user = '' remaining_executions = get_running_executions() status = MAINTENANCE_MODE_ACTIVATING \ if remaining_executions else MAINTENANCE_MODE_ACTIVATED activated_at = '' if remaining_executions else now utils.mkdirs(config.instance.maintenance_folder) new_state = prepare_maintenance_dict( status=status, activation_requested_at=now, activated_at=activated_at, remaining_executions=remaining_executions, requested_by=user) utils.write_dict_to_json_file(maintenance_file_path, new_state) return new_state if maintenance_action == 'deactivate': if not os.path.isfile(maintenance_file_path): return prepare_maintenance_dict( MAINTENANCE_MODE_DEACTIVATED), 304 os.remove(maintenance_file_path) return prepare_maintenance_dict(MAINTENANCE_MODE_DEACTIVATED) valid_actions = ['activate', 'deactivate'] raise BadParametersError('Invalid action: {0}, Valid action ' 'values are: {1}'.format( maintenance_action, valid_actions))
def post(self, maintenance_action, **_): state = get_maintenance_state() if maintenance_action == 'activate': if state: return state, 304 remaining_executions = get_running_executions() status = MAINTENANCE_MODE_ACTIVATING \ if remaining_executions else MAINTENANCE_MODE_ACTIVATED now = datetime.utcnow() state = store_maintenance_state( status=status, activation_requested_at=now, activated_at=None if remaining_executions else now, requested_by=current_user) state['remaining_executions'] = remaining_executions, return state elif maintenance_action == 'deactivate': if not state: return {'status': MAINTENANCE_MODE_DEACTIVATED}, 304 return remove_maintenance_state() else: valid_actions = ['activate', 'deactivate'] raise BadParametersError('Invalid action: {0}, Valid action ' 'values are: {1}'.format( maintenance_action, valid_actions))
def post(self, group_name, multi_tenancy): """ Set role for a certain group """ request_dict = rest_utils.get_json_and_verify_params() role_name = request_dict.get('role') if not role_name: raise BadParametersError('`role` not provided') rest_utils.verify_role(role_name, is_system_role=True) return multi_tenancy.set_group_role(group_name, role_name)
def post(self, tenant_name, multi_tenancy): """ Create a tenant """ rest_utils.validate_inputs({'tenant_name': tenant_name}) if tenant_name in ('users', 'user-groups'): raise BadParametersError( '{0!r} is not allowed as a tenant name ' "because it wouldn't be possible to remove it later due to " 'a conflict with the remove {0} from tenant endpoint'.format( str(tenant_name))) return multi_tenancy.create_tenant(tenant_name)
def post(self, multi_tenancy): """ Create a group """ request_dict = rest_utils.get_json_and_verify_params() group_name = request_dict['group_name'] ldap_group_dn = request_dict.get('ldap_group_dn') role = request_dict.get('role', constants.DEFAULT_SYSTEM_ROLE) rest_utils.verify_role(role, is_system_role=True) rest_utils.validate_inputs({'group_name': group_name}) if group_name == 'users': raise BadParametersError( '{0!r} is not allowed as a user group name ' "because it wouldn't be possible to remove it later due to " 'a conflict with the remove {0} from user group endpoint'. format(str(group_name))) return multi_tenancy.create_group(group_name, ldap_group_dn, role)
def post(self, group_id, **kwargs): request_dict = get_json_and_verify_params({'action'}) action = request_dict['action'] valid_actions = ['cancel', 'force-cancel', 'kill', 'resume', 'force-resume'] if action not in valid_actions: raise BadParametersError( 'Invalid action: {0}, Valid action values are: {1}'.format( action, valid_actions)) sm = get_storage_manager() group = sm.get(models.ExecutionGroup, group_id) if action in ('cancel', 'force-cancel', 'kill'): self._cancel_group(sm, group, action) if action in ('resume', 'force-resume'): self._resume_group(sm, group, action) return group
def _validate_set_ldap_request(self): if not self._only_admin_in_manager(): raise MethodNotAllowedError('LDAP Configuration may be set only on' ' a clean manager.') if not premium_enabled: raise MethodNotAllowedError('LDAP is only supported in the ' 'Cloudify premium edition.') ldap_config = rest_utils.get_json_and_verify_params({ 'ldap_server': {}, 'ldap_username': { 'optional': True }, 'ldap_password': { 'optional': True }, 'ldap_domain': {}, 'ldap_is_active_directory': { 'optional': True }, 'ldap_dn_extra': {} }) # Not allowing empty username or password ldap_config['ldap_username'] = ldap_config.get('ldap_username', '') ldap_config['ldap_password'] = ldap_config.get('ldap_password', '') ldap_config['ldap_is_active_directory'] = \ rest_utils.verify_and_convert_bool( 'ldap_is_active_directory', ldap_config.get('ldap_is_active_directory') or False ) if ((ldap_config['ldap_username'] and not ldap_config['ldap_password']) or (ldap_config['ldap_password'] and not ldap_config['ldap_username'])): raise BadParametersError( 'Must supply either both username and password or neither. ' 'Note that an empty username or password is invalid') return ldap_config
def _verify_role(role): valid_roles = [r['name'] for r in config.instance.authorization_roles] if role not in valid_roles: raise BadParametersError( 'Invalid role: `{0}`. Valid roles are: {1}'.format( role, valid_roles))
def delete(self, filters=None, pagination=None, all_tenants=None): request_dict = get_json_and_verify_params({ 'keep_last': {'optional': True, 'type': int}, 'to_datetime': {'optional': True} }) if 'keep_last' in request_dict: if 'to_datetime' in request_dict: raise BadParametersError( "Must provide either a `to_datetime` timestamp or a " "`keep_last` number of executions to keep" ) if request_dict['keep_last'] <= 0: raise BadParametersError( "`keep_last` must be an integer greater than 0. got {} " "instead.".format(request_dict['keep_last']) ) requested_time = None if 'to_datetime' in request_dict: requested_time = parse_datetime_multiple_formats( request_dict['to_datetime']) if 'status' in filters: if filters['status'] not in ExecutionState.END_STATES: raise BadParametersError( 'Can\'t filter by execution status `{0}`. ' 'Allowed statuses are: {1}'.format( filters['status'], ExecutionState.END_STATES) ) else: filters['status'] = ExecutionState.END_STATES sm = get_storage_manager() executions = sm.list(models.Execution, filters=filters, all_tenants=all_tenants, get_all_results=True) dep_creation_execs = {} for execution in executions: if execution.workflow_id == 'create_deployment_environment' and \ execution.status == 'terminated': dep_creation_execs[execution.deployment_id] = \ dep_creation_execs.get(execution.deployment_id, 0) + 1 deleted_count = 0 if requested_time: for execution in executions: creation_time = datetime.strptime(execution.created_at, '%Y-%m-%dT%H:%M:%S.%fZ') if creation_time < requested_time and \ self._can_delete_execution(execution, dep_creation_execs): sm.delete(execution) deleted_count += 1 else: if request_dict.get('keep_last'): max_to_delete = len(executions) - request_dict['keep_last'] for execution in executions: if self._can_delete_execution(execution, dep_creation_execs): sm.delete(execution) deleted_count += 1 if request_dict.get('keep_last') and deleted_count >= \ max_to_delete: break return ListResult([{'count': deleted_count}], {'pagination': pagination})
def patch(self, blueprint_id, **kwargs): """ Update a blueprint. Used for updating the blueprint's state (and error) while uploading, and updating the blueprint's other attributes upon a successful upload. This method is for internal use only. """ if not request.json: raise IllegalActionError('Update a blueprint request must include ' 'at least one parameter to update') request_schema = { 'plan': { 'type': dict, 'optional': True }, 'description': { 'type': text_type, 'optional': True }, 'main_file_name': { 'type': text_type, 'optional': True }, 'visibility': { 'type': text_type, 'optional': True }, 'state': { 'type': text_type, 'optional': True }, 'error': { 'type': text_type, 'optional': True }, 'error_traceback': { 'type': text_type, 'optional': True }, 'labels': { 'type': list, 'optional': True } } request_dict = rest_utils.get_json_and_verify_params(request_schema) invalid_params = set(request_dict.keys()) - set(request_schema.keys()) if invalid_params: raise BadParametersError("Unknown parameters: {}".format( ','.join(invalid_params))) sm = get_storage_manager() rm = get_resource_manager() blueprint = sm.get(models.Blueprint, blueprint_id) # if finished blueprint validation - cleanup DB entry # and uploaded blueprints folder if blueprint.state == BlueprintUploadState.VALIDATING: uploaded_blueprint_path = join( config.instance.file_server_root, FILE_SERVER_UPLOADED_BLUEPRINTS_FOLDER, blueprint.tenant.name, blueprint.id) remove(uploaded_blueprint_path) sm.delete(blueprint) return blueprint # set blueprint visibility visibility = request_dict.get('visibility') if visibility: if visibility not in VisibilityState.STATES: raise BadParametersError( "Invalid visibility: `{0}`. Valid visibility's values " "are: {1}".format(visibility, VisibilityState.STATES)) blueprint.visibility = visibility # set other blueprint attributes. if 'plan' in request_dict: blueprint.plan = request_dict['plan'] if 'description' in request_dict: blueprint.description = request_dict['description'] if 'main_file_name' in request_dict: blueprint.main_file_name = request_dict['main_file_name'] provided_labels = request_dict.get('labels') if request_dict.get('plan'): dsl_labels = request_dict['plan'].get('labels', {}) csys_obj_parents = dsl_labels.get('csys-obj-parent') if csys_obj_parents: dep_parents = csys_obj_parents['values'] missing_parents = rm.get_missing_deployment_parents( dep_parents) if missing_parents: raise DeploymentParentNotFound( 'Blueprint {0}: is referencing deployments' ' using label `csys-obj-parent` that does not exist, ' 'make sure that deployment(s) {1} exist before ' 'creating blueprint'.format(blueprint.id, ','.join(missing_parents))) # set blueprint state state = request_dict.get('state') if state: if state not in BlueprintUploadState.STATES: raise BadParametersError( "Invalid state: `{0}`. Valid blueprint state values are: " "{1}".format(state, BlueprintUploadState.STATES)) if (state != BlueprintUploadState.UPLOADED and provided_labels is not None): raise ConflictError( 'Blueprint labels can be created only if the provided ' 'blueprint state is {0}'.format( BlueprintUploadState.UPLOADED)) blueprint.state = state blueprint.error = request_dict.get('error') blueprint.error_traceback = request_dict.get('error_traceback') # On finalizing the blueprint upload, extract archive to file # server if state == BlueprintUploadState.UPLOADED: UploadedBlueprintsManager(). \ extract_blueprint_archive_to_file_server( blueprint_id=blueprint_id, tenant=blueprint.tenant.name) _create_blueprint_labels(blueprint, provided_labels) # If failed for any reason, cleanup the blueprint archive from # server elif state in BlueprintUploadState.FAILED_STATES: UploadedBlueprintsManager(). \ cleanup_blueprint_archive_from_file_server( blueprint_id=blueprint_id, tenant=blueprint.tenant.name) else: # Updating the blueprint not as part of the upload process if provided_labels is not None: if blueprint.state != BlueprintUploadState.UPLOADED: raise ConflictError( 'Blueprint labels can only be updated if the blueprint' ' was uploaded successfully') rm = get_resource_manager() labels_list = rest_utils.get_labels_list(provided_labels) rm.update_resource_labels(models.BlueprintLabel, blueprint, labels_list) blueprint.updated_at = get_formatted_timestamp() return sm.update(blueprint)
def _validate_set_ldap_request(self): if not premium_enabled: raise MethodNotAllowedError('LDAP is only supported in the ' 'Cloudify premium edition.') base_substitutions = ['base_dn', 'domain_dn', 'group_dn'] ldap_config = rest_utils.get_json_and_verify_params({ 'ldap_server': {}, 'ldap_domain': {}, 'ldap_username': {'optional': True}, 'ldap_password': {'optional': True}, 'ldap_is_active_directory': {'optional': True}, 'ldap_dn_extra': {'optional': True}, 'ldap_ca_cert': {'optional': True}, 'ldap_nested_levels': {'optional': True}, 'ldap_bind_format': { 'optional': True, 'allowed_substitutions': [ 'username', 'domain'] + base_substitutions, }, 'ldap_group_dn': { 'optional': True, 'allowed_substitutions': ['base_dn', 'domain_dn'], }, 'ldap_base_dn': {'optional': True}, 'ldap_group_member_filter': { 'optional': True, 'allowed_substitutions': ['object_dn'] }, 'ldap_user_filter': { 'optional': True, 'allowed_substitutions': ['username'] + base_substitutions, }, 'ldap_attribute_email': {'optional': True}, 'ldap_attribute_first_name': {'optional': True}, 'ldap_attribute_last_name': {'optional': True}, 'ldap_attribute_uid': {'optional': True}, 'ldap_attribute_group_membership': {'optional': True}, }) if ldap_config.get('ldap_nested_levels') is None: ldap_config['ldap_nested_levels'] = 1 else: ldap_config['ldap_nested_levels'] = rest_utils.convert_to_int( ldap_config['ldap_nested_levels']) for attr in ldap_config: if ldap_config[attr] is None: # Otherwise we try to set None on the config entry, which is # not a string. ldap_config[attr] = '' ldap_config['ldap_is_active_directory'] = \ rest_utils.verify_and_convert_bool( 'ldap_is_active_directory', ldap_config.get('ldap_is_active_directory') or False ) if ldap_config['ldap_server'].startswith('ldaps://'): if 'ldap_ca_cert' not in ldap_config: raise BadParametersError( 'A CA certificate must be provided to use ldaps.' ) elif ldap_config['ldap_server'].startswith('ldap://'): if 'ldap_ca_cert' in ldap_config: raise BadParametersError( 'CA certificate cannot be provided when not using ldaps.' ) else: raise BadParametersError( 'ldap_server must specify protocol and should specify port, ' 'e.g. ldap://192.0.2.1:389 or ldaps://192.0.2.45:636' ) user = ldap_config.get('ldap_username') password = ldap_config.get('ldap_password') if (user or password) and not (user and password): raise BadParametersError( 'Must supply either both username and password or neither. ' 'Note that an empty username or password is invalid') return ldap_config