/*
* The MIT License
*
* Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Yahoo! Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
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 = Class.forName(id.substring(0,idx),true,Hudson.getInstance().getPluginManager().uberClassLoader);
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);
}