def check_clone(self, raise_=True): """ .. versionadded:: 1.1 Checks that an object can be cloned. If *raise_* is True: :raise: :exc:`.PermissionError` if the object is not readable :raise: :exc:`.PermissionError` if the object can not be read :raise: :exc:`.PermissionError` if the object is not cloneable If *raise_* is False: Returns True if all previous tests has been succesfully passed, False otherwise. """ res = self.check_readable(raise_=False) if (not res) and raise_: raise PermissionError( "You can not clone this object : you shouldn't see it.") res = res and self._user.profile.is_contributor if (not res) and raise_: raise PermissionError( "You can not clone this object since you are not a contributor." ) res = res and self.is_cloneable if (not res) and raise_: raise PermissionError("This object can not be cloned") return res
def check_cancel(self, raise_=True): """ .. versionadded:: 1.1 Checks that an object can be cancelled. If *raise_* is True: :raise: :exc:`.PermissionError` if the object is not draft :raise: :exc:`.PermissionError` if the object has related previous or next revision :raise: :exc:`.PermissionError` if the user has not owner rights on an object If *raise_* is False: Returns True if all previous tests has been succesfully passed, False otherwise. """ res = self.is_draft if (not res) and raise_: raise PermissionError("Invalid state: the object is not draft") res = res and self.check_permission("owner", raise_=False) if (not res) and raise_: raise PermissionError("You are not allowed to cancel this object") res = res and len(self.get_all_revisions()) == 1 if (not res) and raise_: raise PermissionError("This object has more than 1 revision") return res
def check_cancel(self, raise_=True): res = super(DocumentController, self).check_cancel(raise_=raise_) if res: res = res and not self.get_attached_parts() if (not res) and raise_: raise PermissionError("This document is related to a part.") return res
def delete_file(self, doc_file): """ Deletes *doc_file*, the file attached to *doc_file* is physically removed. :exceptions raised: * :exc:`ValueError` if *doc_file*.creator is not self.object * :exc:`plmapp.exceptions.DeleteFileError` if *doc_file* is locked * :exc:`.PermissionError` if :attr:`_user` is not the owner of :attr:`object` :param doc_file: the file to be deleted :type doc_file: :class:`.PrivateFile` """ self.check_permission("owner") if doc_file.creator != self.object: raise PermissionError("Not your file") path = os.path.realpath(doc_file.file.path) if not path.startswith(settings.DOCUMENTS_DIR): raise DeleteFileError("Bad path : %s" % path) os.chmod(path, 0700) os.remove(path) doc_file.delete()
def create_from_form(cls, form, user, *args, **kwargs): obj = super(DocumentController, cls).create_from_form(form, user, *args, **kwargs) if type(obj.object).ACCEPT_FILES: private_files = form.cleaned_data.get("pfiles", []) if any(pf.creator != user for pf in private_files): raise PermissionError("Not your file") for pf in private_files: doc_file = models.DocumentFile.objects.create( filename=pf.filename, size=pf.size, file=pf.file.path, document=obj.object) generate_thumbnail.delay(doc_file.id) # django < 1.2.5 deletes the file when pf is deleted pf.file = "" pf.delete() template = form.cleaned_data["template"] if not private_files and template: if template.type == obj.type and template.is_official: obj.copy_files(template) else: raise ValueError("invalid template") for df in obj.files: obj.handle_added_file(df) return obj
def check_readable(self, raise_=True): if self._user.profile.restricted: if self._user.id != self.object.id: if raise_: raise PermissionError("You can not see this user account") return False return True
def remove_signer(self, signer, role): """ .. versionadded:: 1.2 Removes *signer* to the list of signers for role *role*. :param signer: the user who would be no more signer :type signer: :class:`~django.contrib.auth.models.User` :raise: :exc:`.PermissionError` if: * user is not the owner * one signer has approved the promotion * there is only one signer :raise: :exc:`ObjectDoesNotExist` if *signer* is not a signer """ self.check_edit_signer() if not role.startswith(models.ROLE_SIGN): raise ValueError("Not a sign role") if self.users.now().filter(role=role).count() <= 1: raise PermissionError( "Can not remove signer, there is only one signer.") link = models.PLMObjectUserLink.current_objects.get( plmobject=self.object, user=signer, role=role) link.end() details = u"user: %s 's signature is no longer necessary" % signer self._save_histo("removed %s" % role, details, roles=(role, ))
def add_notified(self, new_notified): """ Adds *new_notified* to the list of users notified when :attr:`object` changes. :param new_notified: the new user who would be notified :type new_notified: :class:`~django.contrib.auth.models.User` :raise: :exc:`IntegrityError` if *new_notified* is already notified when :attr:`object` changes .. versionchanged:: 1.0.1 :raise: :exc:`.PermissionError` if *new_notified* does not belong to the object's group. """ if new_notified != self._user: self.check_permission("owner") if not new_notified.is_active: raise PermissionError(u"%s's account is inactive" % new_notified) self.check_in_group(new_notified) models.PLMObjectUserLink.objects.create(plmobject=self.object, user=new_notified, role="notified") details = u"user: %s to be notified" % new_notified self._save_histo("added new notification right", details)
def resend_sponsor_mail(self, new_user): try: link = models.DelegationLink.current_objects.get( delegator=self._user, delegatee=new_user, role=models.ROLE_SPONSOR) except models.DelegationLink.DoesNotExist: raise PermissionError("You did not sponsored %s" % new_user.username) # checks that new_user did not logged on openPLM to not # reset its password if new_user.last_login >= link.ctime: raise ValueError("Can not resend a sponsor mail:" "%s has already logged on openPLM" % new_user) # generate a new password password = generate_password() new_user.set_password(password) new_user.save() # send a mail ctx = { "new_user": new_user, "sponsor": self._user, "password": password, } self._send_mail(send_mail, self.get_sponsor_subject(new_user), [new_user], ctx, "mails/new_account")
def check_unpublish(self, raise_=True): """ .. versionadded:: 1.1 Checks that an object can be unpublished. If *raise_* is True: :raise: :exc:`.PermissionError` if the user is not allowed to unpublish an object (see :attr:`.UserProfile.can_publish`) :raise: :exc:`.PermissionError` if the user does not belong to the object's group :raise: :exc:`.ValueError` if the object is unpublished If *raise_* is False: Returns True if all previous tests has been succesfully passed, False otherwise. """ res = self._user.profile.can_publish if (not res) and raise_: raise PermissionError("You are not allowed to unpublish an object") res = res and self.check_in_group(self._user, raise_=raise_) res = res and self.published if (not res) and raise_: raise ValueError("Object not published") return res
def import_csv_init(request, target="csv"): """ Manage page to import a csv file. """ if not request.user.profile.is_contributor: raise PermissionError("You are not a contributor.") obj, ctx = get_generic_data(request) if request.method == "POST": csv_form = forms.CSVForm(request.POST, request.FILES) if csv_form.is_valid(): f = request.FILES["file"] prefix = "openplmcsv" + request.user.username tmp = tempfile.NamedTemporaryFile(prefix=prefix, delete=False) for chunk in f.chunks(): tmp.write(chunk) name = os.path.split(tmp.name)[1][len(prefix):] tmp.close() encoding = csv_form.cleaned_data["encoding"] return HttpResponseRedirect("/import/%s/%s/%s/" % (target, name, encoding)) else: csv_form = forms.CSVForm() ctx["csv_form"] = csv_form ctx["step"] = 1 ctx["target"] = target return r2r("import/csv.html", ctx, request)
def change_user_password(request, obj_ref): """ Manage html page for the modification of the selected :class:`~django.contrib.auth.models.User` password. It computes a context dictionary based on :param request: :class:`django.http.QueryDict` :param obj_ref: :attr:`~django.contrib.auth.models.User.username` :return: a :class:`django.http.HttpResponse` """ if request.user.username == 'test': return HttpResponseRedirect("/user/%s/attributes/" % request.user) obj, ctx = get_generic_data(request, "User", obj_ref) if obj.object != request.user: raise PermissionError("You are not the user") if request.method == 'POST' and request.POST: modification_form = PasswordChangeForm(obj, request.POST) if modification_form.is_valid(): obj.set_password(modification_form.cleaned_data['new_password2']) obj.save() messages.info(request, _(u"Your password has been modified successfully.")) return HttpResponseRedirect("/user/%s/" % obj.username) else: modification_form = PasswordChangeForm(obj) ctx["modification_form"] = modification_form return r2r('users/password.html', ctx, request)
def check_readable(self, raise_=True): """ Returns ``True`` if the user can read (is allowed to) this object. Raises a :exc:`.PermissionError` if the user cannot read the object and *raise_* is ``True`` (the default). """ if not self._user.is_active: raise PermissionError(u"%s's account is inactive" % self._user) if not self._user.profile.restricted: return True else: if self.owner_id == self._user.id: return True if raise_: raise PermissionError("You can not see this object.") return False
def delegate(self, user, role): """ Delegates role *role* to *user*. Possible values for *role* are: ``'notified'`` valid for all users ``'owner'`` valid only for contributors and administrators :samp:``'sign_{x}_level'`` valid only for contributors and administrators ``'sign*'`` valid only for contributors and administrators, means all sign roles that :attr:`object` has. :raise: :exc:`.PermissionError` if *user* can not have the role *role* :raise: :exc:`ValueError` if *user* is :attr:`object` """ if user == self.object: raise ValueError("Bad delegatee (self)") if not user.is_active: raise ValueError("User account is inactive") if self._user.profile.restricted: raise PermissionError( "A restricted account can not delegate a right") if user.profile.restricted: raise PermissionError("%s can not have role %s" % (user, role)) if user.profile.is_viewer and role != 'notified': raise PermissionError("%s can not have role %s" % (user, role)) if self.object.profile.is_viewer and role != 'notified': raise PermissionError("%s can not have role %s" % (self.object, role)) if role == "sign*": qset = models.PLMObjectUserLink.current_objects.filter( user=self.object, role__startswith="sign_").only("role") roles = set(link.role for link in qset) else: roles = [role] for r in roles: models.DelegationLink.current_objects.get_or_create( delegator=self.object, delegatee=user, role=r) details = "%(delegator)s delegated the role %(role)s to %(delegatee)s" details = details % dict( role=role, delegator=self.object, delegatee=user) self._save_histo(models.DelegationLink.ACTION_NAME, details)
def check_contributor(self, user=None): """ This method checks if *user* is a contributor. If not, it raises :exc:`.PermissionError`. If *user* is None (the default), :attr:`_user` is used. """ if not user: user = self._user if not user.is_active: raise PermissionError(u"%s's account is inactive" % user) profile = user.profile if not (profile.is_contributor or profile.is_administrator): raise PermissionError(u"%s is not a contributor" % user) if profile.restricted: # should not be possible, but an admin may have done a mistake raise PermissionError(u"%s is not a contributor" % user)
def add_reader(self, new_reader): if not self.is_official: raise ValueError("Object is not official") if not new_reader.is_active: raise PermissionError(u"%s's account is inactive" % new_reader) ECRUserLink.objects.create(ecr=self.object, user=new_reader, role=models.ROLE_READER) details = "user: %s" % new_reader self._save_histo("New reader", details)
def check_permission(self, role, raise_=True): if self._user.username == settings.COMPANY: # the company is like a super user return True if not self.group.user_set.filter(id=self._user.id).exists(): if raise_: raise PermissionError("action not allowed for %s" % self._user) else: return False return super(PLMObjectController, self).check_permission(role, raise_)
def import_csv_apply(request, target, filename, encoding): """ View that display a preview of an uploaded csv file. """ obj, ctx = get_generic_data(request) if not request.user.profile.is_contributor: raise PermissionError("You are not a contributor.") ctx["encoding_error"] = False ctx["io_error"] = False Importer = csvimport.IMPORTERS[target] Formset = forms.get_headers_formset(Importer) try: path = os.path.join(tempfile.gettempdir(), "openplmcsv" + request.user.username + filename) with open(path, "rb") as csv_file: importer = Importer(csv_file, request.user, encoding) preview = importer.get_preview() if request.method == "POST": headers_formset = Formset(request.POST) if headers_formset.is_valid(): headers = headers_formset.headers try: with open(path, "rb") as csv_file: importer = Importer(csv_file, request.user, encoding) importer.import_csv(headers) except csvimport.CSVImportError as exc: ctx["errors"] = exc.errors.iteritems() else: os.remove(path) return HttpResponseRedirect("/import/done/") else: initial = [{ "header": header } for header in preview.guessed_headers] headers_formset = Formset(initial=initial) ctx.update({ "preview": preview, "preview_data": itertools.izip((f["header"] for f in headers_formset.forms), preview.headers, *preview.rows), "headers_formset": headers_formset, }) except UnicodeError: ctx["encoding_error"] = True except (IOError, csv.Error): ctx["io_error"] = True ctx["has_critical_error"] = ctx["io_error"] or ctx["encoding_error"] \ or "errors" in ctx ctx["csv_form"] = forms.CSVForm(initial={"encoding": encoding}) ctx["step"] = 2 ctx["target"] = target return r2r("import/csv.html", ctx, request)
def check_readable(self, raise_=True): """ Returns ``True`` if the user can read (is allowed to) this object. Raises a :exc:`.PermissionError` if the user cannot read the object and *raise_* is ``True`` (the default). """ if not self._user.is_active: raise PermissionError(u"%s's account is inactive" % self._user) if not self._user.profile.restricted: if self.is_official or self.is_deprecated or self.is_cancelled: return True if self._user.username == settings.COMPANY: # the company is like a super user return True if self.owner_id == self._user.id: return True if self.group.user_set.filter(id=self._user.id).exists(): return True if raise_: raise PermissionError("You can not see this object.") return False
def add_reader(self, new_reader): if not self.is_official: raise ValueError("Object is not official") if not new_reader.profile.restricted: raise ValueError("Not a restricted account") if not new_reader.is_active: raise PermissionError(u"%s's account is inactive" % new_reader) self.check_in_group(self._user) models.PLMObjectUserLink.objects.create(plmobject=self.object, user=new_reader, role=models.ROLE_READER) details = "user: %s is a reader" % new_reader self._save_histo("added new reader", details)
def check_restricted_readable(self, raise_=True): """ Returns ``True`` if the user can read (is allowed to) the restricted data of this object. Raises a :exc:`.PermissionError` if the user cannot read the object and *raise_* is ``True`` (the default). """ if not self._user.is_active: raise PermissionError(u"%s's account is inactive" % self._user) if not self._user.profile.restricted: return self.check_readable(raise_) return super(PLMObjectController, self).check_permission(models.ROLE_READER, raise_)
def add_notified(self, new_notified): """ Adds *new_notified* to the list of users notified when :attr:`object` changes. :param new_notified: the new user who would be notified :type new_notified: :class:`~django.contrib.auth.models.User` :raise: :exc:`IntegrityError` if *new_notified* is already notified when :attr:`object` changes """ if new_notified != self._user: self.check_permission("owner") if not new_notified.is_active: raise PermissionError(u"%s's account is inactive" % new_notified) ECRUserLink.objects.create(ecr=self.object, user=new_notified, role="notified") details = u"user: %s" % new_notified self._save_histo("New notified", details)
def create_from_form(cls, form, user, *args, **kwargs): obj = super(DocumentController, cls).create_from_form(form, user, *args, **kwargs) if type(obj.object).ACCEPT_FILES: private_files = form.cleaned_data.get("pfiles", []) if any(pf.creator != user for pf in private_files): raise PermissionError("Not your file") for pf in private_files: doc_file = models.DocumentFile.objects.create( filename=pf.filename, size=pf.size, file=pf.file.path, document=obj.object) obj.handle_added_file(doc_file) generate_thumbnail.delay(doc_file.id) # django < 1.2.5 deletes the file when pf is deleted pf.file = "" pf.delete() return obj
def sponsor(self, new_user, is_contributor=True, restricted=False): self.check_contributor() if is_contributor and restricted: raise ValueError( "An restricted account can not be a contributor account") email = new_user.email try: # checks *email* if settings.RESTRICT_EMAIL_TO_DOMAINS: # i don't know if a domain can contains a '@' domain = email.rsplit("@", 1)[1] if domain not in Site.objects.values_list("domain", flat=True): raise PermissionError("Email's domain not valid") except AttributeError: # restriction disabled if the setting is not set pass password = generate_password() new_user.set_password(password) new_user.save() new_user.profile.is_contributor = is_contributor new_user.profile.restricted = restricted new_user.profile.save() link = models.DelegationLink(delegator=self._user, delegatee=new_user, role=models.ROLE_SPONSOR) link.save() ctx = { "new_user": new_user, "sponsor": self._user, "password": password, } update_index.delay("auth", "user", new_user.pk) self._send_mail(send_mail, self.get_sponsor_subject(new_user), [new_user], ctx, "mails/new_account") models.UserHistory.objects.create(action="Create", user=self._user, plmobject=self._user, details="New user: %s" % new_user.username) models.UserHistory.objects.create(action="Create", user=self._user, plmobject=new_user, details="Account created")
def check_edit_signer(self, raise_=True): """ .. versionadded:: 1.2 Checks that the current user can edit the signers of the object: * He must own the object * No user should have approved the promotion :raise: :exc:`.PermissionError` if *raise_* is True and one of the above conditions is not met :return: True if the user can edit the signers """ r = self.check_permission("owner", raise_=raise_) if r and self.approvals.now().exists(): if raise_: raise PermissionError("One user has appproved a promotion.") return False return r
def check_clone(self, raise_=True): """ .. versionadded:: 1.1 Checks that an object can be cloned. If *raise_* is True: :raise: :exc:`.PermissionError` if the object is not readable :raise: :exc:`.PermissionError` if the object can not be read :raise: :exc:`.PermissionError` if the object is not cloneable If *raise_* is False: Returns True if all previous tests has been succesfully passed, False otherwise. """ if raise_: raise PermissionError() return False
def check_in_group(self, user, raise_=True): """ .. versionadded:: 1.0.1 Checks that *user* belongs to the object's group. Returns True if the user belongs to the group. Otherwise, returns False if *raise_* is False or raises a :exc:`.PermissionError` if *raise_* is True. Note that it always returns True if *user* is the company. """ if user.username == settings.COMPANY: return True if not self.group.user_set.filter(id=user.id).exists(): if raise_: raise PermissionError( "The user %s does not belong to the group." % user.username) else: return False return True
def check_permission(self, role, raise_=True): """ This method checks if :attr:`_user` has permissions implied by *role*. For example, *role* can be *owner* or *notified*. If the check succeeds, **True** is returned. Otherwise, if *raise_* is **True** (the default), a :exc:`.PermissionError` is raised and if *raise_* is **False**, **False** is returned. .. admonition:: Implementation details This method keeps a cache, so that you dont have to worry about multiple calls to this method. """ if role in self.__permissions: ok = self.__permissions[role] else: ok = self.has_permission(role) self.__permissions[role] = ok if not ok and raise_: raise PermissionError("action not allowed for %s" % self._user) return ok
def create(cls, reference, type, revision, user, data={}, block_mails=False, no_index=False): u""" This method builds a new :class:`.PLMObject` of type *class_* and return a :class:`PLMObjectController` associated to the created object. Raises :exc:`ValueError` if *reference*, *type* or *revision* are empty. Raises :exc:`ValueError` if *type* is not valid. :param reference: reference of the objet :param type: type of the object :param revision: revision of the object :param user: user who creates/owns the object :param data: a dict<key, value> with informations to add to the plmobject :rtype: :class:`PLMObjectController` """ profile = user.profile if not (profile.is_contributor or profile.is_administrator): raise PermissionError("%s is not a contributor" % user) if not user.is_active: raise PermissionError(u"%s's account is inactive" % user) if profile.restricted: raise PermissionError( "Restricted account can not create a part or document.") if not reference or not type or not revision: raise ValueError( "Empty value not permitted for reference/type/revision") validate_reference(reference) validate_revision(revision) try: class_ = models.get_all_plmobjects()[type] except KeyError: raise ValueError("Incorrect type") # create an object reference_number = parse_reference_number(reference, class_) obj = class_(reference=reference, type=type, revision=revision, owner=user, creator=user, reference_number=reference_number) if no_index: obj.no_index = True if data: for key, value in data.iteritems(): if key not in [ "reference", "type", "revision", "auto", "pfiles" ]: setattr(obj, key, value) obj.state = models.get_default_state(obj.lifecycle) obj.save() res = cls(obj, user) if block_mails: res.block_mails() # record creation in history infos = {"type": type, "reference": reference, "revision": revision} infos.update(data) details = u" / ".join(u"%s : %s" % (k, v) for k, v in infos.items() if k not in ("auto", "pfiles", "type", "reference", "revision", "name")) res._save_histo("created", details) # add links (bulk create) ctime = obj.ctime links = [ models.PLMObjectUserLink(plmobject=obj, user=user, role="owner", ctime=ctime) ] try: l = models.DelegationLink.current_objects.select_related( "delegator").get(delegatee=user, role=models.ROLE_SPONSOR) sponsor = l.delegator if sponsor.username == settings.COMPANY: sponsor = user elif not res.check_in_group(sponsor, False): sponsor = user except models.DelegationLink.DoesNotExist: sponsor = user # the user can promote to the next state links.append( models.PLMObjectUserLink(plmobject=obj, user=user, role=level_to_sign_str(0), ctime=ctime)) # from the next state, only the sponsor can promote this object for i in range(1, obj.lifecycle.nb_states - 1): links.append( models.PLMObjectUserLink(plmobject=obj, user=sponsor, role=level_to_sign_str(i), ctime=ctime)) models.PLMObjectUserLink.objects.bulk_create(links) res._update_state_history() return res
def check_editable(self): """ Raises a :exc:`.PermissionError` if :attr:`object` is not editable. """ if not self.object.is_editable: raise PermissionError("The object is not editable")