All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.almworks.jira.structure.api.permissions.PermissionRule Maven / Gradle / Ivy

There is a newer version: 17.25.3
Show newest version
package com.almworks.jira.structure.api.permissions;

import com.almworks.jira.structure.api.util.La;
import com.almworks.jira.structure.api.util.StructureUtil;
import com.atlassian.jira.user.ApplicationUser;
import org.codehaus.jackson.annotate.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.xml.bind.annotation.*;
import java.text.ParseException;
import java.util.*;

import static org.apache.commons.lang.StringUtils.removeStart;

/**
 * 

A list of PermissionRules is used to define a {@link PermissionLevel} * for a given user. All possible sub-classes of PermissionRule are listed here as * the inner classes.

* * @see PermissionRule.SetLevel * @see PermissionRule.ApplyStructure * @see Structure Permissions (Structure Documentation) * @author Igor Sereda */ @XmlRootElement @XmlSeeAlso({PermissionRule.ApplyStructure.class, PermissionRule.SetLevel.class}) @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type") @JsonSubTypes({ @JsonSubTypes.Type(value = PermissionRule.SetLevel.class, name = "set"), @JsonSubTypes.Type(value = PermissionRule.ApplyStructure.class, name = "apply") }) public abstract class PermissionRule implements Cloneable { private static final Logger logger = LoggerFactory.getLogger(PermissionRule.class); /** * @return a string representation of this permission rule * @see #fromEncodedString */ public abstract String toEncodedString(); /** *

Apply permission rule and return the result.

* *

Normally you should not call this method directly - call * {@link com.almworks.jira.structure.api.structure.Structure#getEffectivePermission} instead.

* * @param user the user, null means anonymous * @param pass the default value, which is returned in case this rule does not apply * @param callStack auxiliary container for objects used to check for recursive rules * @param resolver auxiliary function that converts structure ID into associated list of permission rules - used by {@link ApplyStructure}. If null, {@link ApplyStructure} will not be able to apply and return pass value. * @return permission level for the passed user */ @NotNull public abstract PermissionLevel apply(@Nullable ApplicationUser user, @NotNull PermissionLevel pass, @Nullable List callStack, @Nullable La> resolver); /** * Restores permission rule from its encoded String form. In case the string is null or empty, * returns null. * Use this method only if user keys were used to encode user permissions. * * @param s encoded string * @return the encoded rule, or null if the string is null or empty * @throws ParseException if the string is not empty, but cannot be translated back to a rule * @see #fromEncodedString(String, boolean) */ @Nullable public static PermissionRule fromEncodedString(@Nullable String s) throws ParseException { return fromEncodedString(s, false); } /** * Restores permission rule from its encoded String form. In case the string is null or empty, * returns null. * * @param s encoded string * @param usersAsUserNames true if user names were used to encode user permissions (Structure version was less than 2.3), * false if user keys were used instead. * @return the encoded rule, or null if the string is null or empty * @throws ParseException if the string is not empty, but cannot be translated back to a rule */ @Nullable public static PermissionRule fromEncodedString(@Nullable String s, boolean usersAsUserNames) throws ParseException { try { if (s == null || s.length() == 0) return null; String p = removeStart(s, "apply:"); if (!p.equals(s)) { return new ApplyStructure(Long.parseLong(p)); } p = removeStart(s, "set:"); if (!p.equals(s)) { int k = p.indexOf(':'); if (k < 0) throw new ParseException(s, 0); // accept both number and name for level String levelCode = p.substring(0, k); PermissionLevel level; try { int levelInt = Integer.parseInt(levelCode); level = PermissionLevel.fromSerial(levelInt); } catch (NumberFormatException e) { try { level = PermissionLevel.valueOf(levelCode); } catch (IllegalArgumentException ee) { throw e; } } PermissionSubject subject = PermissionSubject.fromEncodedString(p.substring(k + 1), usersAsUserNames); if (subject == null) { throw new ParseException(s, 0); } return new SetLevel(subject, level); } throw new ParseException(s, 0); } catch (NumberFormatException e) { throw new ParseException(s, 0); } } /** * Utility method to encode a list of PermissionRules. * * @param permissions a list of permissions * @return a string with encoded permissions, separated by comma * @see #toEncodedString() */ @NotNull public static String encodePermissions(@Nullable List permissions) { StringBuilder r = new StringBuilder(); if (permissions != null) { for (PermissionRule permission : permissions) { if (permission != null) { if (r.length() > 0) r.append(','); r.append(permission.toEncodedString()); } } } return r.toString(); } /** * Utility method to decode a list of PermissionRules. * * @param s encoded list of permissions, delimited by comma * @return a restored list of rules * @throws ParseException in case any of the parts used to encode a permission rule failed to decode * @see #fromEncodedString(String) */ @NotNull public static List decodePermissions(@Nullable String s) throws ParseException { if (s == null || s.length() == 0) return Collections.emptyList(); String[] elements = s.split(","); List list = new ArrayList(elements.length); for (String element : elements) { PermissionRule rule = PermissionRule.fromEncodedString(element); if (rule != null) { list.add(rule); } } return list; } /** * @return a cloned version of this rule */ @SuppressWarnings({"CloneDoesntDeclareCloneNotSupportedException"}) public PermissionRule clone() { try { return (PermissionRule) super.clone(); } catch (CloneNotSupportedException e) { throw new AssertionError(e); } } public String toString() { return toEncodedString(); } /** * This rules applies a list of rules taken from a Structure, identified by the structure ID. * * @see com.almworks.jira.structure.api.structure.Structure */ @XmlRootElement(name = "apply") public static class ApplyStructure extends PermissionRule { private Long myStructureId; public ApplyStructure() { } public ApplyStructure(Long structureId) { myStructureId = structureId; } @XmlAttribute @JsonProperty("structure") public Long getStructureId() { return myStructureId; } public void setStructureId(Long structureId) { myStructureId = structureId; } public String toEncodedString() { return "apply:" + myStructureId; } @NotNull public PermissionLevel apply(ApplicationUser user, @NotNull PermissionLevel pass, List callStack, La> resolver) { Long id = myStructureId; if (id == null || resolver == null) return pass; if (callStack != null && callStack.contains(id)) { logger.error("permissions dependency cycle " + callStack); return pass; } List permissions = resolver.la(id); if (callStack != null) callStack.add(id); PermissionLevel r = StructureUtil.applyPermissions(permissions, user, callStack, resolver, pass); if (callStack != null) callStack.remove(id); return r; } public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ApplyStructure that = (ApplyStructure) o; if (myStructureId != null ? !myStructureId.equals(that.myStructureId) : that.myStructureId != null) return false; return true; } public int hashCode() { return myStructureId != null ? myStructureId.hashCode() : 0; } } /** * This rule sets the permission level to a specific value in case the user matches PermissionSubject. * * @see PermissionSubject */ @XmlRootElement(name = "set") public static class SetLevel extends PermissionRule { private PermissionSubject mySubject; private PermissionLevel myLevel; public SetLevel() { } public SetLevel(PermissionSubject subject, PermissionLevel level) { mySubject = subject; myLevel = level; } public SetLevel clone() { SetLevel r = (SetLevel) super.clone(); if (mySubject != null) r.mySubject = mySubject.clone(); return r; } @NotNull public PermissionLevel apply(ApplicationUser user, @NotNull PermissionLevel pass, List callStack, La> resolver) { PermissionSubject subject = mySubject; return subject != null && subject.matches(user) ? myLevel : pass; } @XmlElementRef public PermissionSubject getSubject() { return mySubject; } public void setSubject(PermissionSubject subject) { mySubject = subject; } @XmlAttribute public PermissionLevel getLevel() { return myLevel; } public void setLevel(PermissionLevel level) { myLevel = level; } public String toEncodedString() { return "set:" + (myLevel == null ? "0" : myLevel.getSerial()) + ":" + (mySubject == null ? "" : mySubject.toEncodedString()); } public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; SetLevel setLevel = (SetLevel) o; if (myLevel != setLevel.myLevel) return false; if (mySubject != null ? !mySubject.equals(setLevel.mySubject) : setLevel.mySubject != null) return false; return true; } public int hashCode() { int result = mySubject != null ? mySubject.hashCode() : 0; result = 31 * result + (myLevel != null ? myLevel.hashCode() : 0); return result; } } }