
hudson.security.AuthorizationMatrixProperty Maven / Gradle / Ivy
Show all versions of hudson-core Show documentation
/*******************************************************************************
*
* Copyright (c) 2004-2010 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., Peter Hayes, Tom Huybrechts
*
*
*******************************************************************************/
package hudson.security;
import hudson.diagnosis.OldDataMonitor;
import hudson.model.AbstractProject;
import hudson.model.Item;
import hudson.model.Job;
import hudson.model.JobProperty;
import hudson.model.JobPropertyDescriptor;
import hudson.model.Hudson;
import hudson.model.Run;
import hudson.Extension;
import hudson.util.FormValidation;
import hudson.util.RobustReflectionConverter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Collections;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.io.IOException;
import net.sf.json.JSONObject;
import org.springframework.security.acls.sid.Sid;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.AncestorInPath;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import javax.servlet.ServletException;
/**
* {@link JobProperty} to associate ACL for each project.
*
*
* Once created (and initialized), this object becomes immutable.
*/
public class AuthorizationMatrixProperty extends JobProperty> {
private transient SidACL acl = new AclImpl();
/**
* List up all permissions that are granted.
*
* Strings are either the granted authority or the principal, which is not
* distinguished.
*/
private final Map> grantedPermissions = new HashMap>();
private Set sids = new HashSet();
private AuthorizationMatrixProperty() {
}
public AuthorizationMatrixProperty(Map> grantedPermissions) {
// do a deep copy to be safe
for (Entry> e : grantedPermissions.entrySet())
this.grantedPermissions.put(e.getKey(),new HashSet(e.getValue()));
}
public Set getGroups() {
return sids;
}
/**
* Returns all SIDs configured in this matrix, minus "anonymous"
*
* @return Always non-null.
*/
public List getAllSIDs() {
Set r = new HashSet();
for (Set set : grantedPermissions.values())
r.addAll(set);
r.remove("anonymous");
String[] data = r.toArray(new String[r.size()]);
Arrays.sort(data);
return Arrays.asList(data);
}
/**
* Returns all the (Permission,sid) pairs that are granted, in the multi-map form.
*
* @return
* read-only. never null.
*/
public Map> getGrantedPermissions() {
return Collections.unmodifiableMap(grantedPermissions);
}
/**
* Adds to {@link #grantedPermissions}. Use of this method should be limited
* during construction, as this object itself is considered immutable once
* populated.
*/
protected void add(Permission p, String sid) {
Set set = grantedPermissions.get(p);
if (set == null)
grantedPermissions.put(p, set = new HashSet());
set.add(sid);
sids.add(sid);
}
@Extension
public static class DescriptorImpl extends JobPropertyDescriptor {
@Override
public JobProperty> newInstance(StaplerRequest req, JSONObject formData) throws FormException {
formData = formData.getJSONObject("useProjectSecurity");
if (formData.isNullObject())
return null;
AuthorizationMatrixProperty amp = new AuthorizationMatrixProperty();
for (Map.Entry r : (Set>) formData.getJSONObject("data").entrySet()) {
String sid = r.getKey();
if (r.getValue() instanceof JSONObject) {
for (Map.Entry e : (Set>) ((JSONObject) r
.getValue()).entrySet()) {
if (e.getValue()) {
Permission p = Permission.fromId(e.getKey());
amp.add(p, sid);
}
}
}
}
return amp;
}
@Override
public boolean isApplicable(Class extends Job> jobType) {
// only applicable when ProjectMatrixAuthorizationStrategy is in charge
return Hudson.getInstance().getAuthorizationStrategy() instanceof ProjectMatrixAuthorizationStrategy;
}
@Override
public String getDisplayName() {
return "Authorization Matrix";
}
public List getAllGroups() {
return Arrays.asList(PermissionGroup.get(Item.class),PermissionGroup.get(Run.class));
}
public boolean showPermission(Permission p) {
return p.getEnabled() && p!=Item.CREATE;
}
public FormValidation doCheckName(@AncestorInPath Job project, @QueryParameter String value) throws IOException, ServletException {
return GlobalMatrixAuthorizationStrategy.DESCRIPTOR.doCheckName(value, project, AbstractProject.CONFIGURE);
}
}
private final class AclImpl extends SidACL {
protected Boolean hasPermission(Sid sid, Permission p) {
if (AuthorizationMatrixProperty.this.hasPermission(toString(sid),p))
return true;
return null;
}
}
public SidACL getACL() {
return acl;
}
/**
* Checks if the given SID has the given permission.
*/
public boolean hasPermission(String sid, Permission p) {
for (; p != null; p = p.impliedBy) {
Set set = grantedPermissions.get(p);
if (set != null && set.contains(sid))
return true;
}
return false;
}
/**
* Checks if the permission is explicitly given, instead of implied through {@link Permission#impliedBy}.
*/
public boolean hasExplicitPermission(String sid, Permission p) {
Set set = grantedPermissions.get(p);
return set != null && set.contains(sid);
}
/**
* Works like {@link #add(Permission, String)} but takes both parameters
* from a single string of the form PERMISSIONID:sid
*/
private void add(String shortForm) {
int idx = shortForm.indexOf(':');
Permission p = Permission.fromId(shortForm.substring(0, idx));
if (p==null)
throw new IllegalArgumentException("Failed to parse '"+shortForm+"' --- no such permission");
add(p, shortForm.substring(idx + 1));
}
/**
* Persist {@link ProjectMatrixAuthorizationStrategy} as a list of IDs that
* represent {@link ProjectMatrixAuthorizationStrategy#grantedPermissions}.
*/
public static final class ConverterImpl implements Converter {
public boolean canConvert(Class type) {
return type == AuthorizationMatrixProperty.class;
}
public void marshal(Object source, HierarchicalStreamWriter writer,
MarshallingContext context) {
AuthorizationMatrixProperty amp = (AuthorizationMatrixProperty) source;
for (Entry> e : amp.grantedPermissions
.entrySet()) {
String p = e.getKey().getId();
for (String sid : e.getValue()) {
writer.startNode("permission");
writer.setValue(p + ':' + sid);
writer.endNode();
}
}
}
public Object unmarshal(HierarchicalStreamReader reader,
final UnmarshallingContext context) {
AuthorizationMatrixProperty as = new AuthorizationMatrixProperty();
String prop = reader.peekNextChild();
if (prop!=null && prop.equals("useProjectSecurity")) {
reader.moveDown();
reader.getValue(); // we used to use this but not any more.
reader.moveUp();
}
while (reader.hasMoreChildren()) {
reader.moveDown();
try {
as.add(reader.getValue());
} catch (IllegalArgumentException ex) {
Logger.getLogger(AuthorizationMatrixProperty.class.getName())
.log(Level.WARNING,"Skipping a non-existent permission",ex);
RobustReflectionConverter.addErrorInContext(context, ex);
}
reader.moveUp();
}
if (GlobalMatrixAuthorizationStrategy.migrateHudson2324(as.grantedPermissions))
OldDataMonitor.report(context, "1.301");
return as;
}
}
}