Package com.almworks.jira.structure.api

Source Code of com.almworks.jira.structure.api.PermissionSubject

package com.almworks.jira.structure.api;

import com.almworks.jira.structure.util.JiraFunc;
import com.almworks.jira.structure.util.StructureUtil;
import com.atlassian.crowd.embedded.api.Group;
import com.atlassian.crowd.embedded.api.User;
import com.atlassian.jira.component.ComponentAccessor;
import com.atlassian.jira.project.Project;
import com.atlassian.jira.project.ProjectManager;
import com.atlassian.jira.security.roles.ProjectRoleManager;
import org.codehaus.jackson.annotate.JsonIgnore;
import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.annotate.JsonSubTypes;
import org.codehaus.jackson.annotate.JsonTypeInfo;
import org.jetbrains.annotations.Nullable;

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

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

/**
* <p><code>PermissionSubject</code> is an abstraction used to specify which users a particular permission
* is applicable to. All possible sub-classes of <code>PermissionSubject</code> are listed in this class, and other implementation are not
* supported because Structure needs to serialize and deserialize permission subjects.</p>
*
* @see PermissionSubject.JiraUser
* @see PermissionSubject.JiraGroup
* @see PermissionSubject.ProjectRole
* @see PermissionSubject.Anyone
* @see <a href="http://wiki.almworks.com/display/structure/Structure+Permissions">Structure Permissions (Structure Documentation)</a>
* @author Igor Sereda
*/
@XmlRootElement
@XmlSeeAlso({PermissionSubject.Anyone.class, PermissionSubject.JiraUser.class, PermissionSubject.JiraGroup.class,
  PermissionSubject.ProjectRole.class})
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonSubTypes({
    @JsonSubTypes.Type(value = PermissionSubject.Anyone.class, name = "anyone"),
    @JsonSubTypes.Type(value = PermissionSubject.JiraUser.class, name = "user"),
    @JsonSubTypes.Type(value = PermissionSubject.JiraGroup.class, name = "group"),
    @JsonSubTypes.Type(value = PermissionSubject.ProjectRole.class, name = "project.role")
})
public abstract class PermissionSubject implements Cloneable {

  /**
   * Used to check whether a user matches this subject.
   *
   * @param user user to check, null means anonymous
   * @return true if the user matches
   */
  public abstract boolean matches(@Nullable User user);

  /**
   * @return a string representation of this permission subject
   * @see #fromEncodedString
   */
  public abstract String toEncodedString();

  /**
   * Creates a <code>PermissionSubject</code> based on the string representation. Null parameter yields null result.
   * Use this method only if user keys were used to encode user permissions.
   *
   * @param s string representation of the PermissionSubject
   * @return decoded PermissionSubject, or null if s was null or an empty string.
   * @throws ParseException if s was not null neither empty, but the code failed to decipher the string
   * @see #fromEncodedString(String, boolean)
   */
  @Nullable
  public static PermissionSubject fromEncodedString(@Nullable String s) throws ParseException {
    return fromEncodedString(s, false);
  }

  /**
   * Creates a <code>PermissionSubject</code> based on the string representation. Null parameter yields null result.
   *
   * @param s string representation of the PermissionSubject
   * @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 decoded PermissionSubject, or null if s was null or an empty string.
   * @throws ParseException if s was not null neither empty, but the code failed to decipher the string
   */
  @Nullable
  public static PermissionSubject fromEncodedString(@Nullable String s, boolean usersAsUserNames) throws ParseException {
    if (s == null || s.length() == 0) return null;
    String p = removeStart(s, "anyone");
    if (!p.equals(s)) return new Anyone();
    p = removeStart(s, "user:");
    if (!p.equals(s)) {
      if (usersAsUserNames) p = StructureUtil.migrateUserNameToUserKey(p);
      return new JiraUser(p);
    }
    p = removeStart(s, "group:");
    if (!p.equals(s)) return new JiraGroup(p);
    p = removeStart(s, "role:");
    if (!p.equals(s)) {
      int k = p.indexOf(':');
      if (k < 0) return null;
      try {
        long project = Long.parseLong(p.substring(0, k));
        long role = Long.parseLong(p.substring(k + 1));
        return new ProjectRole(project, role);
      } catch (NumberFormatException e) {
        throw new ParseException(s, 0);
      }
    }
    throw new ParseException(s, 0);
  }

  /**
   * @return a clone of this subject
   */
  @SuppressWarnings( {"CloneDoesntDeclareCloneNotSupportedException"})
  public PermissionSubject clone() {
    // must be overridden - but defining here to get rid of CloneNotSupportedException
    try {
      return (PermissionSubject) super.clone();
    } catch (CloneNotSupportedException e) {
      throw new AssertionError(e);
    }
  }

  public String toString() {
    return toEncodedString();
  }

  /**
   * Calls an appropriate visitor method, passing this PermissionSubject and the carry parameter.
   * @return the result of the call
   * @since 7.2.0 (Structure 2.0)
   */
  @Nullable
  public abstract <T> T visit(Visitor<T> visitor, @Nullable T carry);

  /**
   * Represents "anyone", a subject that would match all users, even anonymous.
   */
  @XmlRootElement
  public static class Anyone extends PermissionSubject implements Serializable {
    public Anyone() {
    }

    public boolean matches(User user) {
      return true;
    }

    public String toEncodedString() {
      return "anyone";
    }

    public boolean equals(Object obj) {
      return obj instanceof Anyone;
    }

    @Override
    public <T> T visit(Visitor<T> visitor, T carry) {
      return visitor.onAnyone(this, carry);
    }

    public int hashCode() {
      return Anyone.class.hashCode();
    }
  }

  @Nullable
  public static PermissionSubject clone(@Nullable PermissionSubject owner) {
    return owner == null ? null : owner.clone();
  }


  /**
   * <p>Represents a specific user in JIRA, matching only that user.</p>
   *
   * <p>A user is identified by the user key, introduced in JIRA 6. This is different from structure-api 7.x, which
   * used user name as the ID. As a result, serialized permission subjects from JIRA 5.x may not correctly
   * deserialize with this new version of the API - migration is needed. See {@link StructureUtil#migrateUserNameToUserKey(String)}.</p>
   */
  @XmlRootElement(name = "user")
  @XmlType(name = "user")
  public static class JiraUser extends PermissionSubject {
    @Nullable
    private String myUserKey;

    public JiraUser() {
    }

    public JiraUser(@Nullable String userKey) {
      myUserKey = userKey;
    }

    public JiraUser(@Nullable User user) {
      myUserKey = StructureUtil.getUserKey(user);
    }

    @Nullable
    @XmlTransient
    @JsonIgnore
    public String getUserName() {
      return StructureUtil.getUserNameByKey(myUserKey);
    }

    @Nullable
    @XmlAttribute(name = "name")
    @JsonProperty("name")
    public String getUserKey() {
      return myUserKey;
    }

    public void setUserName(@Nullable String userName) {
      User user = StructureUtil.getUserByName(userName);
      myUserKey = StructureUtil.getUserKey(user);
    }

    public void setUserKey(@Nullable String userKey) {
      myUserKey = userKey;
    }

    public boolean matches(User user) {
      return myUserKey == null ? user == null : user != null && myUserKey.equals(StructureUtil.getUserKey(user));
    }

    public String toEncodedString() {
      return "user:" + myUserKey;
    }

    @Override
    public <T> T visit(Visitor<T> visitor, T carry) {
      return visitor.onUser(this, carry);
    }

    public boolean equals(Object o) {
      if (this == o) return true;
      if (o == null || getClass() != o.getClass()) return false;

      JiraUser jiraUser = (JiraUser) o;

      if (myUserKey != null ? !myUserKey.equals(jiraUser.myUserKey) : jiraUser.myUserKey != null) return false;

      return true;
    }

    public int hashCode() {
      return myUserKey != null ? myUserKey.hashCode() : 0;
    }
  }


  /**
   * Represents a specific group in JIRA, matching only the users that belong to that group.
   */
  @XmlRootElement(name = "group")
  @XmlType(name = "group")
  public static class JiraGroup extends PermissionSubject {
    @Nullable
    private String myGroupName;

    public JiraGroup() {
    }

    public JiraGroup(@Nullable String groupName) {
      myGroupName = groupName;
    }
   
    public JiraGroup(@Nullable Group group) {
      myGroupName = group == null ? null : group.getName();
    }

    @Nullable
    @XmlAttribute(name = "name")
    @JsonProperty("name")
    public String getGroupName() {
      return myGroupName;
    }

    public void setGroupName(@Nullable String groupName) {
      myGroupName = groupName;
    }

    public boolean matches(User user) {
      if(myGroupName == null || user == null) {
        return false;
      }
      SortedSet<Group> groups = ComponentAccessor.getUserUtil().getGroupsForUser(user.getName()); //j6 ok
      return JiraFunc.GROUP_NAME.find(groups, myGroupName) != null;
    }

    public String toEncodedString() {
      return "group:" + myGroupName;
    }

    @Override
    public <T> T visit(Visitor<T> visitor, T carry) {
      return visitor.onGroup(this, carry);
    }

    public boolean equals(Object o) {
      if (this == o) return true;
      if (o == null || getClass() != o.getClass()) return false;

      JiraGroup jiraGroup = (JiraGroup) o;

      if (myGroupName != null ? !myGroupName.equals(jiraGroup.myGroupName) : jiraGroup.myGroupName != null)
        return false;

      return true;
    }

    public int hashCode() {
      return myGroupName != null ? myGroupName.hashCode() : 0;
    }
  }


  /**
   * Represents a specific project role in a specific project, matching the users that belong to that project role
   * in that project.
   */
  @XmlRootElement(name = "role")
  @XmlType(name = "role")
  public static class ProjectRole extends PermissionSubject {
    private long myProjectId;
    private long myRoleId;

    public ProjectRole() {
    }

    public ProjectRole(long projectId, long roleId) {
      myProjectId = projectId;
      myRoleId = roleId;
    }

    @XmlAttribute
    @JsonProperty("project")
    public long getProjectId() {
      return myProjectId;
    }

    public void setProjectId(long projectId) {
      myProjectId = projectId;
    }

    @XmlAttribute
    @JsonProperty("role")
    public long getRoleId() {
      return myRoleId;
    }

    public void setRoleId(long roleId) {
      myRoleId = roleId;
    }

    public boolean matches(User user) {
      if (myProjectId == 0 || myRoleId == 0) {
        return false;
      }
      ProjectManager pm = ComponentAccessor.getProjectManager();
      Project project = pm.getProjectObj(myProjectId);
      if (project == null) {
        return false;
      }
      ProjectRoleManager rm = ComponentAccessor.getComponentOfType(ProjectRoleManager.class);
      com.atlassian.jira.security.roles.ProjectRole role = rm.getProjectRole(myRoleId);
      if (role == null) {
        return false;
      }
      return rm.isUserInProjectRole(user, role, project);
    }

    public String toEncodedString() {
      return "role:" + myProjectId + ":" + myRoleId;
    }

    @Override
    public <T> T visit(Visitor<T> visitor, T carry) {
      return visitor.onProjectRole(this, carry);
    }

    public boolean equals(Object o) {
      if (this == o) return true;
      if (o == null || getClass() != o.getClass()) return false;

      ProjectRole that = (ProjectRole) o;

      if (myProjectId != that.myProjectId) return false;
      if (myRoleId != that.myRoleId) return false;

      return true;
    }

    public int hashCode() {
      int result = (int) (myProjectId ^ (myProjectId >>> 32));
      result = 31 * result + (int) (myRoleId ^ (myRoleId >>> 32));
      return result;
    }
  }

  /**
   * Used to visit specific subtypes of {@link PermissionSubject}.
   *
   * @param <T> arbitrary type used to pass a value in and out of visitor (carry). Use {@link Visitor.NoCarry} if you
   * don't need it.
   */
  public interface Visitor<T> {
    T onAnyone(Anyone anyone, T carry);
    T onUser(JiraUser user, T carry);
    T onGroup(JiraGroup group, T carry);
    T onProjectRole(ProjectRole projectRole, T carry);

    public static abstract class NoCarry implements Visitor<Void> {
      public abstract void onAnyone(Anyone anyone);
      public abstract void onUser(JiraUser user);
      public abstract void onGroup(JiraGroup group);
      public abstract void onProjectRole(ProjectRole projectRole);

      @Override
      public Void onAnyone(Anyone anyone, Void carry) {
        onAnyone(anyone);
        return carry;
      }

      @Override
      public Void onUser(JiraUser user, Void carry) {
        onUser(user);
        return carry;
      }

      @Override
      public Void onGroup(JiraGroup group, Void carry) {
        onGroup(group);
        return carry;
      }

      @Override
      public Void onProjectRole(ProjectRole projectRole, Void carry) {
        onProjectRole(projectRole);
        return carry;
      }
    }
  }
}
TOP

Related Classes of com.almworks.jira.structure.api.PermissionSubject

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.