/******************************************************************************* * * Copyright (c) 2004-2009 Oracle Corporation. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * * Kohsuke Kawaguchi, Yahoo! Inc. * * *******************************************************************************/ package hudson.security; import hudson.model.*; import net.sf.json.util.JSONUtils; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import org.jvnet.localizer.Localizable; /** * Permission, which represents activity that requires a security privilege. * * <p> Each permission is represented by a specific instance of * {@link Permission}. * * @author Kohsuke Kawaguchi * @see * http://wiki.hudson-ci.org/display/HUDSON/Making+your+plugin+behave+in+secured+Hudson */ public final class Permission { /** * Comparator that orders {@link Permission} objects based on their ID. */ public static final Comparator<Permission> ID_COMPARATOR = new Comparator<Permission>() { /** * {@inheritDoc} */ // break eclipse compilation //Override public int compare(Permission one, Permission two) { return one.getId().compareTo(two.getId()); } }; //TODO: review and check whether we can do it private public final Class owner; //TODO: review and check whether we can do it private public final PermissionGroup group; /** * Human readable ID of the permission. * * <p> This name should uniquely determine a permission among its owner * class. The name must be a valid Java identifier. <p> The expected naming * convention is something like "BrowseWorkspace". */ //TODO: review and check whether we can do it private public final String name; /** * Human-readable description of this permission. Used as a tooltip to * explain this permission, so this message should be a couple of sentences * long. * * <p> If null, there will be no description text. */ //TODO: review and check whether we can do it private public final Localizable description; /** * Bundled {@link Permission} that also implies this permission. * * <p> This allows us to organize permissions in a hierarchy, so that for * example we can say "view workspace" permission is implied by the * (broader) "read" permission. * * <p> The idea here is that for most people, access control based on such * broad permission bundle is good enough, and those few that need finer * control can do so. */ //TODO: review and check whether we can do it private public final Permission impliedBy; /** * Whether this permission is available for use. * * <p> This allows us to dynamically enable or disable the visibility of * permissions, so administrators can control the complexity of their * permission matrix. * * @since 1.325 */ //TODO: review and check whether we can do it private public boolean enabled; /** * Defines a new permission. * * @param group Permissions are grouped per classes that own them. Specify * the permission group created for that class. The idiom is: * * <pre> * class Foo { * private static final PermissionGroup PERMISSIONS = new PermissionGroup(Foo.class,...); * public static final Permission ABC = new Permisison(PERMISSION,...) ; * } * </pre> * * Because of the classloading problems and the difficulty for Hudson to * enumerate them, the permission constants really need to be static field * of the owner class. * * @param name See {@link #name}. * @param description See {@link #description}. * @param impliedBy See {@link #impliedBy}. */ public Permission(PermissionGroup group, String name, Localizable description, Permission impliedBy, boolean enable) { if (!JSONUtils.isJavaIdentifier(name)) { throw new IllegalArgumentException(name + " is not a Java identifier"); } this.owner = group.owner; this.group = group; this.name = name; this.description = description; this.impliedBy = impliedBy; this.enabled = enable; group.add(this); ALL.add(this); } public Permission(PermissionGroup group, String name, Localizable description, Permission impliedBy) { this(group, name, description, impliedBy, true); } /** * @deprecated since 1.257. Use * {@link #Permission(PermissionGroup, String, Localizable, Permission)} */ public Permission(PermissionGroup group, String name, Permission impliedBy) { this(group, name, null, impliedBy); } private Permission(PermissionGroup group, String name) { this(group, name, null, null); } /** * Returns the string representation of this {@link Permission}, which can * be converted back to {@link Permission} via the {@link #fromId(String)} * method. * * <p> This string representation is suitable for persistence. * * @see #fromId(String) */ public String getId() { return owner.getName() + '.' + name; } public Class getOwner() { return owner; } public PermissionGroup getGroup() { return group; } public String getName() { return name; } public Localizable getDescription() { return description; } public Permission getImpliedBy() { return impliedBy; } public boolean isEnabled() { return enabled; } /** * Convert the ID representation into {@link Permission} object. * * @return null if the conversion failed. * @see #getId() */ public static Permission fromId(String id) { int idx = id.lastIndexOf('.'); if (idx < 0) { return null; } try { // force the initialization so that it will put all its permissions into the list. Class cl; if (Hudson.getInstance() != null) { cl = Class.forName(id.substring(0, idx), true, Hudson.getInstance().getPluginManager().uberClassLoader); } else { // Hudson may not be yet initialized - to support Initial Setup cl = Class.forName(id.substring(0, idx)); } PermissionGroup g = PermissionGroup.get(cl); if (g == null) { return null; } return g.find(id.substring(idx + 1)); } catch (ClassNotFoundException e) { return null; } } @Override public String toString() { return "Permission[" + owner + ',' + name + ']'; } public void setEnabled(boolean enable) { enabled = enable; } public boolean getEnabled() { return enabled; } /** * Returns all the {@link Permission}s available in the system. * * @return always non-null. Read-only. */ public static List<Permission> getAll() { return ALL_VIEW; } /** * All permissions in the system but in a single list. */ private static final List<Permission> ALL = new CopyOnWriteArrayList<Permission>(); private static final List<Permission> ALL_VIEW = Collections.unmodifiableList(ALL); // // // Because of the initialization order issue, these two fields need to be defined here, // even though they logically belong to Hudson. // /** * {@link PermissionGroup} for {@link Hudson}. * * @deprecated since 2009-01-23. Access {@link Hudson#PERMISSIONS} instead. */ public static final PermissionGroup HUDSON_PERMISSIONS = new PermissionGroup(Hudson.class, hudson.model.Messages._Hudson_Permissions_Title()); /** * {@link Permission} that represents the God-like access. Equivalent of * Unix root. * * <p> All permissions are eventually * {@linkplain Permission#impliedBy implied by} this permission. * * @deprecated since 2009-01-23. Access {@link Hudson#ADMINISTER} instead. */ public static final Permission HUDSON_ADMINISTER = new Permission(HUDSON_PERMISSIONS, "Administer", hudson.model.Messages._Hudson_AdministerPermission_Description(), null); // // // Root Permissions. // // These permisisons are meant to be used as the 'impliedBy' permission for other more specific permissions. // The intention is to allow a simplified AuthorizationStrategy implementation agnostic to // specific permissions. public static final PermissionGroup GROUP = new PermissionGroup(Permission.class, Messages._Permission_Permissions_Title()); /** * Historically this was separate from {@link #HUDSON_ADMINISTER} but such a * distinction doesn't make sense any more, so deprecated. * * @deprecated since 2009-01-23. Use {@link Hudson#ADMINISTER}. */ public static final Permission FULL_CONTROL = new Permission(GROUP, "FullControl", HUDSON_ADMINISTER); /** * Generic read access. */ public static final Permission READ = new Permission(GROUP, "GenericRead", null, HUDSON_ADMINISTER); /** * Generic write access. */ public static final Permission WRITE = new Permission(GROUP, "GenericWrite", null, HUDSON_ADMINISTER); /** * Generic create access. */ public static final Permission CREATE = new Permission(GROUP, "GenericCreate", null, WRITE); /** * Generic update access. */ public static final Permission UPDATE = new Permission(GROUP, "GenericUpdate", null, WRITE); /** * Generic delete access. */ public static final Permission DELETE = new Permission(GROUP, "GenericDelete", null, WRITE); /** * Generic configuration access. */ public static final Permission CONFIGURE = new Permission(GROUP, "GenericConfigure", null, UPDATE); }