def edit(request, entry_id): entry, error = get_obj_with_check_perm(request.user, Entry, entry_id, ACLType.Writable) if error: return error # prevent to show edit page under the creating processing if entry.get_status(Entry.STATUS_CREATING): return HttpResponse("Target entry is now under processing", status=400) if not entry.is_active: return _redirect_restore_entry(entry) context = { "entry": entry, "groups": Group.objects.filter(is_active=True), "attributes": entry.get_available_attrs(request.user, ACLType.Writable), "form_url": "/entry/do_edit/%s" % entry.id, "redirect_url": "/entry/show/%s" % entry.id, } if custom_view.is_custom("edit_entry", entry.schema.name): # show custom view return custom_view.call_custom("edit_entry", entry.schema.name, request, request.user, entry, context) else: return render(request, "edit_entry.html", context)
def test_get_obj_with_check_perm(self): for obj in [self.entity, self.entityattr, self.entry, self.attr]: target_obj, error = get_obj_with_check_perm( self.user, obj.__class__, obj.id, ACLType.Full ) self.assertEqual(target_obj, obj) self.assertIsNone(error)
def do_restore(request, entry_id, recv_data): entry, error = get_obj_with_check_perm(request.user, Entry, entry_id, ACLType.Full) if error: return error if entry.is_active: return JsonResponse( data={"msg": "Failed to get entry from specified parameter"}, status=400) # checks that a same name entry corresponding to the entity is existed, or not. dup_entry = Entry.objects.filter( schema=entry.schema.id, name=re.sub(r"_deleted_[0-9_]*$", "", entry.name), is_active=True, ).first() if dup_entry: return JsonResponse( data={ "msg": "", "entry_id": dup_entry.id, "entry_name": dup_entry.name }, status=400, ) entry.set_status(Entry.STATUS_CREATING) # Create a new job to restore deleted entry and run it job = Job.new_restore(request.user, entry) job.run() return HttpResponse("Success to queue a request to restore an entry")
def index(request, obj_id): aclbase_obj, error = get_obj_with_check_perm(request.user, ACLBase, obj_id, ACLType.Full) if error: return error target_obj = aclbase_obj.get_subclass_object() # Some type of objects needs object that refers target_obj (e.g. Attribute) # for showing breadcrumb navigation. parent_obj = None try: if isinstance(target_obj, Attribute): parent_obj = target_obj.parent_entry elif isinstance(target_obj, EntityAttr): parent_obj = target_obj.parent_entity except StopIteration: Logger.warning("failed to get related parent object") context = { "object": target_obj, "parent": parent_obj, "acltypes": [{"id": x.id, "name": x.label} for x in ACLType.all()], "roles": [ { "id": x.id, "name": x.name, "description": x.description, "current_permission": x.get_current_permission(target_obj), } for x in Role.objects.filter(is_active=True) if request.user.is_superuser or x.is_belonged_to(request.user) ], } return render(request, "edit_acl.html", context)
def do_edit(request, entity_id, recv_data): entity, error = get_obj_with_check_perm(request.user, Entity, entity_id, ACLType.Writable) if error: return error # validation checks for attr in recv_data["attrs"]: # formalize recv_data format if "ref_ids" not in attr: attr["ref_ids"] = [] if int(attr["type"]) & AttrTypeValue["object"] and not attr["ref_ids"]: return HttpResponse("Need to specify enabled referral ids", status=400) if any([not Entity.objects.filter(id=x).exists() for x in attr["ref_ids"]]): return HttpResponse("Specified referral is invalid", status=400) # duplication checks counter = collections.Counter( [ attr["name"] for attr in recv_data["attrs"] if "deleted" not in attr or not attr["deleted"] ] ) if len([v for v, count in counter.items() if count > 1]): return HttpResponse("Duplicated attribute names are not allowed", status=400) # prevent to show edit page under the processing if entity.get_status(Entity.STATUS_EDITING): return HttpResponse("Target entity is now under processing", status=400) if custom_view.is_custom("edit_entity"): resp = custom_view.call_custom( "edit_entity", None, entity, recv_data["name"], recv_data["attrs"] ) if resp: return resp # update status parameters if recv_data["is_toplevel"]: entity.set_status(Entity.STATUS_TOP_LEVEL) else: entity.del_status(Entity.STATUS_TOP_LEVEL) # update entity metatada informations to new ones entity.set_status(Entity.STATUS_EDITING) # Create a new job to edit entity and run it job = Job.new_edit_entity(request.user, entity, params=recv_data) job.run() new_name = recv_data["name"] return JsonResponse( { "entity_id": entity.id, "entity_name": new_name, "msg": 'Success to schedule to update Entity "%s"' % new_name, } )
def do_delete(request, entity_id, recv_data): entity, error = get_obj_with_check_perm(request.user, Entity, entity_id, ACLType.Full) if error: return error if not entity.is_active: return HttpResponse("Target entity is now under processing", status=400) if Entry.objects.filter(schema=entity, is_active=True).exists(): return HttpResponse( "cannot delete Entity because one or more Entries are not deleted", status=400, ) if custom_view.is_custom("delete_entity"): resp = custom_view.call_custom("delete_entity", None, entity) if resp: return resp ret = {} # save deleting target name before do it ret["name"] = entity.name # set deleted flag in advance because deleting processing takes long time entity.is_active = False entity.save(update_fields=["is_active"]) # Create a new job to delete entry and run it job = Job.new_delete_entity(request.user, entity) job.run() return JsonResponse(ret)
def copy(request, entry_id): entry, error = get_obj_with_check_perm(request.user, Entry, entry_id, ACLType.Writable) if error: return error # prevent to show edit page under the creating processing if entry.get_status(Entry.STATUS_CREATING) or entry.get_status( Entry.STATUS_EDITING): return HttpResponse("Target entry is now under processing", status=400) if not entry.is_active: return _redirect_restore_entry(entry) context = { "form_url": "/entry/do_copy/%s" % entry.id, "redirect_url": "/entry/%s" % entry.schema.id, "entry": entry, } if custom_view.is_custom("copy_entry", entry.schema.name): return custom_view.call_custom("copy_entry", entry.schema.name, request, request.user, entry, context) return render(request, "copy_entry.html", context)
def edit(request, entity_id): entity, error = get_obj_with_check_perm(request.user, Entity, entity_id, ACLType.Writable) if error: return error # when an entity in referral attribute is deleted # user should be able to select new entity or keep it unchanged # candidate entites for referral are: # - active(not deleted) entity # - current value of any attributes even if the entity has been deleted context = { "entity": entity, "attr_types": AttrTypes, "attributes": [ { "id": x.id, "name": x.name, "type": x.type, "is_mandatory": x.is_mandatory, "is_delete_in_chain": x.is_delete_in_chain, "referrals": x.referral.all(), } for x in entity.attrs.filter(is_active=True).order_by("index") if request.user.has_permission(x, ACLType.Writable) ], } return render(request, "edit_entity.html", context)
def test_get_obj_with_check_perm_with_invalid_param(self): target_obj, error = get_obj_with_check_perm(self.user, Entry, self.entity.id, ACLType.Full) self.assertIsNone(target_obj) self.assertEqual(error.content, b"Failed to get entity of specified id") self.assertEqual(error.status_code, 400)
def test_get_obj_with_check_perm_without_permission(self): self.entry.is_public = False self.entry.save() target_obj, error = get_obj_with_check_perm(self.user, Entry, self.entry.id, ACLType.Full) self.assertIsNone(target_obj) self.assertEqual(error.content, b"You don't have permission to access this object") self.assertEqual(error.status_code, 400)
def do_import_data(request, entity_id, context): user: User = request.user entity: Entity entity, error = get_obj_with_check_perm(user, Entity, entity_id, ACLType.Writable) if error: return error if not entity.is_active: return HttpResponse("Failed to get entity of specified id", status=400) try: data = yaml.load(context, Loader=yaml.FullLoader) except yaml.parser.ParserError: return HttpResponse("Couldn't parse uploaded file", status=400) except ValueError as e: return HttpResponse("Invalid value is found: %s" % e, status=400) except yaml.scanner.ScannerError: return HttpResponse("Couldn't scan uploaded file", status=400) except Exception as e: return HttpResponse("Unknown exception: %s" % e, status=500) if not Entry.is_importable_data(data): return HttpResponse( "Uploaded file has invalid data structure to import", status=400) for entity_name in data.keys(): import_entity: Entity = Entity.objects.filter(name=entity_name, is_active=True).first() if not import_entity: return HttpResponse("Specified entity does not exist (%s)" % entity_name, status=400) if not user.has_permission(import_entity, ACLType.Writable): return HttpResponse("You don't have permission to access (%s)" % entity_name, status=400) import_data = data[entity_name] if custom_view.is_custom("import_entry", entity_name): # import custom view import_data, err_msg = custom_view.call_custom( "import_entry", entity_name, user, import_entity, import_data) # If custom_view returns available response this returns it to user, # or continues default processing. if err_msg: return HttpResponse(err_msg, status=400) # create job to import data to create or update entries and run it job = Job.new_import(user, import_entity, text="Preparing to import data", params=import_data) job.run() return HttpResponseSeeOther("/entry/%s/" % entity_id)
def do_copy(request, entry_id, recv_data): entry, error = get_obj_with_check_perm(request.user, Entry, entry_id, ACLType.Full) if error: return error ret = [] params = { "new_name_list": [], "post_data": recv_data, } for new_name in [x for x in recv_data["entries"].split("\n") if x]: if (new_name in params["new_name_list"] or Entry.objects.filter( schema=entry.schema, name=new_name).exists()): ret.append({ "status": "fail", "msg": "A same named entry (%s) already exists" % new_name, }) continue if custom_view.is_custom("do_copy_entry", entry.schema.name): (is_continue, status, msg) = custom_view.call_custom( "do_copy_entry", entry.schema.name, request, entry, recv_data, request.user, new_name, ) if not is_continue: ret.append({ "status": "success" if status else "fail", "msg": msg, }) continue params["new_name_list"].append(new_name) ret.append({ "status": "success", "msg": "Success to create new entry '%s'" % new_name, }) # if there is no entry to copy, do not create a job. if params["new_name_list"]: # make a new job to copy entry and run it job = Job.new_copy(request.user, entry, text="Preparing to copy entry", params=params) job.run() return JsonResponse({"results": ret})
def do_edit(request, entry_id, recv_data): entry, error = get_obj_with_check_perm(request.user, Entry, entry_id, ACLType.Writable) if error: return error # checks that a same name entry corresponding to the entity is existed. query = Q(schema=entry.schema, name=recv_data["entry_name"]) & ~Q(id=entry.id) if Entry.objects.filter(query).exists(): return HttpResponse("Duplicate name entry is existed", status=400) # validate contexts of each attributes err = _validate_input(recv_data, entry) if err: return err if entry.get_status(Entry.STATUS_CREATING): return HttpResponse("Target entry is now under processing", status=400) if custom_view.is_custom("do_edit_entry", entry.schema.name): # resp is HttpReponse instance or its subclass (e.g. JsonResponse) resp = custom_view.call_custom("do_edit_entry", entry.schema.name, request, recv_data, request.user, entry) if resp: return resp # update name of Entry object. If name would be updated, the elasticsearch data of entries that # refers this entry also be updated by creating REGISTERED_REFERRALS task. job_register_referrals = None if entry.name != recv_data["entry_name"]: job_register_referrals = Job.new_register_referrals( request.user, entry) entry.name = recv_data["entry_name"] entry.save(update_fields=["name"]) # set flags that indicates target entry is under processing entry.set_status(Entry.STATUS_EDITING) # Create new jobs to edit entry and notify it to registered webhook endpoint if it's necessary job_edit_entry = Job.new_edit(request.user, entry, params=recv_data) job_edit_entry.run() # running job of re-register referrals because of chaning entry's name if job_register_referrals: job_register_referrals.dependent_job = job_edit_entry job_register_referrals.run() return JsonResponse({ "entry_id": entry.id, "entry_name": entry.name, })
def list_webhook(request, entity_id): entity, error = get_obj_with_check_perm(request.user, Entity, entity_id, ACLType.Full) if error: return error return render( request, "list_webhooks.html", { "entity": entity, "webhooks": entity.webhooks.all(), }, )
def index(request, entity_id): entity, error = get_obj_with_check_perm(request.user, Entity, entity_id, ACLType.Readable) if error: return error page = request.GET.get("page", 1) keyword = request.GET.get("keyword", None) if custom_view.is_custom("list_entry_without_context", entity.name): # show custom view without context resp = custom_view.call_custom("list_entry_without_context", entity.name, request, entity) if resp: return resp if keyword: name_pattern = prepend_escape_character( CONFIG.ESCAPE_CHARACTERS_ENTRY_LIST, keyword) entries = Entry.objects.order_by("name").filter( schema=entity, is_active=True, name__iregex=name_pattern) else: entries = Entry.objects.order_by("name").filter(schema=entity, is_active=True) p = Paginator(entries, CONFIG.MAX_LIST_ENTRIES) try: page_obj = p.page(page) except PageNotAnInteger: return HttpResponse("Invalid page number. It must be unsigned integer", status=400) except EmptyPage: return HttpResponse( "Invalid page number. The page doesn't have anything", status=400) context = { "entity": entity, "keyword": keyword, "page_obj": page_obj, } if custom_view.is_custom("list_entry", entity.name): # list custom view return custom_view.call_custom("list_entry", entity.name, request, entity, context) else: # list ordinal view return render(request, "list_entry.html", context)
def do_delete(request, entry_id, recv_data): entry, error = get_obj_with_check_perm(request.user, Entry, entry_id, ACLType.Full) if error: return error if custom_view.is_custom("do_delete_entry", entry.schema.name): # do_delete custom view resp = custom_view.call_custom("do_delete_entry", entry.schema.name, request, request.user, entry) # If custom_view returns available response this returns it to user, # or continues default processing. if resp: return resp # set deleted flag in advance because deleting processing taks long time entry.is_active = False entry.save(update_fields=["is_active"]) ret = {} # save deleting Entry name before do it ret["name"] = entry.name # register operation History for deleting entry request.user.seth_entry_del(entry) # Create a new job to delete entry and run it job_delete_entry = Job.new_delete(request.user, entry) job_notify_event = Job.new_notify_delete_entry(request.user, entry) # This prioritizes notifying job rather than deleting entry if job_delete_entry.dependent_job: job_notify_event.dependent_job = job_delete_entry.dependent_job job_notify_event.save(update_fields=["dependent_job"]) job_notify_event.run() # This update dependent job of deleting entry job job_delete_entry.dependent_job = job_notify_event job_delete_entry.save(update_fields=["dependent_job"]) job_delete_entry.run() return JsonResponse(ret)
def create(request, entity_id): entity, error = get_obj_with_check_perm(request.user, Entity, entity_id, ACLType.Writable) if error: return error if custom_view.is_custom("create_entry_without_context", entity.name): # show custom view return custom_view.call_custom("create_entry_without_context", entity.name, request, request.user, entity) context = { "entity": entity, "form_url": "/entry/do_create/%s/" % entity.id, "redirect_url": "/entry/%s" % entity.id, "groups": Group.objects.filter(is_active=True), "attributes": [{ "entity_attr_id": x.id, "id": x.id, "type": x.type, "name": x.name, "is_mandatory": x.is_mandatory, "is_readble": True if request.user.has_permission(x, ACLType.Writable) else False, } for x in entity.attrs.filter(is_active=True).order_by("index")], } if custom_view.is_custom("create_entry", entity.name): # show custom view return custom_view.call_custom("create_entry", entity.name, request, request.user, entity, context) else: return render(request, "create_entry.html", context)
def history(request, entry_id): entry, error = get_obj_with_check_perm(request.user, Entry, entry_id, ACLType.Readable) if error: return error if entry.get_status(Entry.STATUS_CREATING): return HttpResponse("Target entry is now under processing", status=400) if not entry.is_active: return _redirect_restore_entry(entry) context = { "entry": entry, "value_history": entry.get_value_history(request.user), "history_count": CONFIG.MAX_HISTORY_COUNT, } return render(request, "show_entry_history.html", context)
def restore(request, entity_id): entity, error = get_obj_with_check_perm(request.user, Entity, entity_id, ACLType.Full) if error: return error page = request.GET.get("page", 1) keyword = request.GET.get("keyword", None) # get all deleted entries that correspond to the entity, the specififcation of # 'status=0' is necessary to prevent getting entries that were under processing. if keyword: name_pattern = prepend_escape_character( CONFIG.ESCAPE_CHARACTERS_ENTRY_LIST, keyword) entries = Entry.objects.filter( schema=entity, status=0, is_active=False, name__iregex=name_pattern).order_by("-updated_time") else: entries = Entry.objects.filter( schema=entity, status=0, is_active=False).order_by("-updated_time") p = Paginator(entries, CONFIG.MAX_LIST_ENTRIES) try: page_obj = p.page(page) except PageNotAnInteger: return HttpResponse("Invalid page number. It must be unsigned integer", status=400) except EmptyPage: return HttpResponse( "Invalid page number. The page doesn't have anything", status=400) return render( request, "list_deleted_entry.html", { "entity": entity, "keyword": keyword, "page_obj": page_obj, }, )
def refer(request, entry_id): entry, error = get_obj_with_check_perm(request.user, Entry, entry_id, ACLType.Readable) if error: return error if entry.get_status(Entry.STATUS_CREATING): return HttpResponse("Target entry is now under processing", status=400) if not entry.is_active: return _redirect_restore_entry(entry) # get referred entries and count of them referred_objects = entry.get_referred_objects() context = { "entry": entry, "referred_objects": referred_objects[0:CONFIG.MAX_LIST_REFERRALS], "referred_total": referred_objects.count(), } return render(request, "show_entry_refer.html", context)
def do_create(request, entity_id, recv_data): # get objects to be referred in the following processing entity, error = get_obj_with_check_perm(request.user, Entity, entity_id, ACLType.Writable) if error: return error # checks that a same name entry corresponding to the entity is existed, or not. if Entry.objects.filter(schema=entity_id, name=recv_data["entry_name"]).exists(): return HttpResponse("Duplicate name entry is existed", status=400) # validate contexts of each attributes err = _validate_input(recv_data, entity) if err: return err if custom_view.is_custom("do_create_entry", entity.name): # resp is HttpReponse instance or its subclass (e.g. JsonResponse) resp = custom_view.call_custom("do_create_entry", entity.name, request, recv_data, request.user, entity) if resp: return resp # Create a new Entry object entry = Entry.objects.create( name=recv_data["entry_name"], created_user=request.user, schema=entity, status=Entry.STATUS_CREATING, ) # Create a new job to create entry and run it job_create_entry = Job.new_create(request.user, entry, params=recv_data) job_create_entry.run() return JsonResponse({ "entry_id": entry.id, "entry_name": entry.name, })
def show(request, entry_id): entry, error = get_obj_with_check_perm(request.user, Entry, entry_id, ACLType.Readable) if error: return error if entry.get_status(Entry.STATUS_CREATING): return HttpResponse("Target entry is now under processing", status=400) if not entry.is_active: return _redirect_restore_entry(entry) context = { "entry": entry, "attributes": entry.get_available_attrs(request.user), } if custom_view.is_custom("show_entry", entry.schema.name): # show custom view return custom_view.call_custom("show_entry", entry.schema.name, request, request.user, entry, context) else: # show ordinal view return render(request, "show_entry.html", context)
def set_webhook(request, entity_id, recv_data): entity, error = get_obj_with_check_perm(request.user, Entity, entity_id, ACLType.Full) if error: return error if not entity.is_active: return JsonResponse({"msg": "There is no entity for setting"}, status=400) # check specified parameters are valid validate = URLValidator() try: # This checks webhook_url is valid HTTP URL validate(recv_data["webhook_url"]) except ValidationError: return HttpResponse("Specified URL is invalid", status=400) if "id" in recv_data: # get Webhook instance and set values webhook = Webhook.objects.filter(id=recv_data["id"]).first() if not webhook: return HttpResponse("Invalid Webhook ID is specified", status=400) webhook.url = recv_data["webhook_url"] webhook.label = recv_data["label"] webhook.headers = recv_data.get("request_headers", []) webhook.is_enabled = recv_data["is_enabled"] else: # create Webhook instance and set values webhook = Webhook.objects.create( **{ "url": recv_data["webhook_url"], "label": recv_data["label"], "headers": recv_data.get("request_headers", []), "is_enabled": recv_data["is_enabled"], }) entity.webhooks.add(webhook) try: resp = requests.post( recv_data["webhook_url"], **{ "headers": { x["header_key"]: x["header_value"] for x in recv_data.get("request_headers", []) }, "data": json.dumps({}), "verify": False, }) # The is_verified parameter will be set True, # when requests received HTTP 200 from specifying endpoint. webhook.is_verified = resp.ok except ConnectionError: webhook.is_verified = False webhook.save() return JsonResponse({ "webhook_id": webhook.id, "msg": "Succeded in registering Webhook" })