/**
* Copyright (c) 2009--2012 Red Hat, Inc.
*
* This software is licensed to you under the GNU General Public License,
* version 2 (GPLv2). There is NO WARRANTY for this software, express or
* implied, including the implied warranties of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
* along with this software; if not, see
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
*
* Red Hat trademarks are not licensed under GPLv2. No permission is
* granted to use or replicate Red Hat trademarks that are incorporated
* in this software or its documentation.
*/
package com.redhat.rhn.common.security.acl;
import com.redhat.rhn.domain.config.ConfigChannel;
import com.redhat.rhn.domain.config.ConfigRevision;
import com.redhat.rhn.domain.role.RoleFactory;
import com.redhat.rhn.domain.server.Server;
import com.redhat.rhn.domain.user.User;
import com.redhat.rhn.frontend.action.common.BadParameterException;
import com.redhat.rhn.manager.configuration.ConfigurationManager;
import com.redhat.rhn.manager.system.SystemManager;
import org.apache.commons.lang.StringUtils;
import java.util.Map;
/**
* Some acl implementation for configuration management
* @version $Rev$
*/
public class ConfigAclHandler extends BaseHandler {
/**
* Tell whether a file is a directory.
* @param ctx Our current context, containing a crid or cfid.
* @param params The parameters containing a config revision id or nothing.
* @return whether the found revision is a file.
*/
public boolean aclIsFile(Object ctx, String[] params) {
ConfigRevision revision = getRevision((Map) ctx, params);
//return whether or not this errata is published
return revision.isFile();
}
/**
* Tell whether the logged in user can edit the give channel.
* @param ctx Our current context, containing the user
* @param params The parameters containing a config channel id.
* @return whether the config channel and objects inside it are editable
* by the current user.
*/
public boolean aclConfigChannelEditable(Object ctx, String[] params) {
Map map = (Map)ctx;
User user = (User) ((Map)ctx).get("user");
ConfigChannel cc = getChannel(map, params);
//This happens if the channel is there but is not accessible.
if (cc == null) {
return false;
}
if (cc.isGlobalChannel()) {
return user.hasRole(RoleFactory.CONFIG_ADMIN);
}
//You have only gotton this far if you have access to the channel
//and it is not a global channel, which means that you administer the
//particular server that the channel is for.
return true;
}
/**
* Whether the config channel of the current context is of the given type.
* The possible types are the labels of the different <code>ConfigChannelType</code>
* statics found in com.redhat.rhn.domain.config.ConfigurationFactory.
* @param ctx The current context.
* @param params The params, must include desired channel type label.
* @return Whether the current channel is of the given type.
*/
public boolean aclConfigChannelType(Object ctx, String[] params) {
if (params.length < 1 || StringUtils.isEmpty(params[0])) {
throw new IllegalArgumentException("Parameters must include type.");
}
ConfigChannel cc = getChannel((Map)ctx, params);
//does the type in params match the channels type.
return cc.getConfigChannelType().getLabel().equalsIgnoreCase(params[0]);
}
/**
* Tell whether the config channel represented by a <code>ccid</code> in
* the current context has files.
* @param ctx The current context
* @param params The parameters unused.
* @return whether the config channel has files.
*/
public boolean aclConfigChannelHasFiles(Object ctx, String[] params) {
//We are using hibernate mappings to decide if the config channel
//has files. This makes things much easier, but it also could be
//somewhat slow because it loads up every file instead of justing
//finding one and then bailing.
ConfigChannel cc = getChannel((Map)ctx, params);
return (cc.getConfigFiles().size() > 0);
}
/**
* Tell us whether the selected channel has any subscribed systems or not
* @param ctx Current context
* @param params parameters (unused)
* @return true if there is at least one system subscribed to this channel
*/
public boolean aclConfigChannelHasSystems(Object ctx, String[] params) {
ConfigChannel cc = getChannel((Map)ctx, params);
User user = (User) ((Map)ctx).get("user");
return ConfigurationManager.getInstance().getSystemCount(user, cc) > 0;
}
/**
* Returns the revision. This really should be two methods with the same
* name, but different number of parameters. We can't do that because all
* acls are handled the same way. Therefore, we decide how we are being called
* by whether there is a parameter in the String[].
*
* Case 1:
* First it looks in the params, and if it finds one, it tries to parse it as
* a Long representing the config revision id.
*
* If there are no parameters, it gets the config revision id from the context.
* Case 2:
* The context can either have a crid representing the revision id or,
* Case 3: a cfid representing the config file, from which we will get
* the latest config revision.
* @param map Context that contains
* <ol>
* <li>Case 1: Nothing important</li>
* <li>Case 2: crid as a Long</li>
* <li>Case 3: cfid as a Long</li>
* </ol>
* @param params Parameters that contains
* <ol>
* <li>Case 1: A single revision id parsable as a Long</li>
* <li>Case 2: Nothing</li>
* <li>Case 3: Nothing</li>
* </ol>
* @return The revision found.
*/
private ConfigRevision getRevision(Map map, String[] params) {
Long crid;
User user = (User) map.get("user");
ConfigurationManager cm = ConfigurationManager.getInstance();
//Case 1:
if (params != null && params.length == 1) {
try {
crid = Long.valueOf(params[0]);
}
catch (NumberFormatException e) {
throw new IllegalArgumentException("Parameter must be a parsable long.");
}
return cm.lookupConfigRevision(user, crid);
}
crid = getAsLong(map.get("crid"));
//Case 2:
if (crid != null) {
return cm.lookupConfigRevision(user, crid);
}
Long cfid = getAsLong(map.get("cfid"));
if (cfid == null) {
throw new BadParameterException("Missing crid and cfid!");
}
return cm.lookupConfigFile(user, cfid).getLatestConfigRevision();
}
private ConfigChannel getChannel(Map map, String[] params) {
User user = (User) map.get("user");
Long ccid;
//Look for ccid from parameters
try {
ccid = getAsLong(params);
}
catch (NumberFormatException e) {
//We are catching this because it is possible that there is
// another parameter and the ccid is solely in the context map.
ccid = null;
}
//Look for ccid from parameters
if (ccid == null) {
ccid = getAsLong(map.get("ccid"));
}
//Finally, look for the revision, and figure out the config channel from that
if (ccid == null) {
ConfigRevision cr = getRevision(map, params);
if (cr != null) {
return cr.getConfigFile().getConfigChannel();
}
//else, ccid is still null and the following if will throw exception.
}
if (user == null || ccid == null) {
throw new IllegalArgumentException("Context must have a user" +
" and config channel id must be a parameter");
}
ConfigurationManager cm = ConfigurationManager.getInstance();
//check the user's access to channel. This will prevent a LookupException.
if (!cm.accessToChannel(user.getId(), ccid)) {
return null;
}
return cm.lookupConfigChannel(user, ccid);
}
/**
* Check if a System is config enabled
* @param ctx Context Map to pass in
* @param params Parameters to use to fetch from Context
* @return true if system is config enabled, false otherwise
*/
public boolean aclConfigEnabled(Object ctx, String[] params) {
Map map = (Map) ctx;
User user = (User) map.get("user");
Long sid = getAsLong(map.get("sid"));
if (user == null || sid == null) {
throw new IllegalArgumentException("Context must have a user" +
" and server id.");
}
Server server = SystemManager.lookupByIdAndUser(sid, user);
if (server == null) {
String format = "Server with sid [%s] could not be found in org [%s]";
throw new IllegalArgumentException(String.format(format,
sid, user.getOrg()));
}
ConfigurationManager cm = ConfigurationManager.getInstance();
return cm.isConfigEnabled(server, user);
}
}