/** * Copyright (c) 2009--2014 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.manager.token; import com.redhat.rhn.common.db.datasource.ModeFactory; import com.redhat.rhn.common.db.datasource.WriteMode; import com.redhat.rhn.common.hibernate.HibernateFactory; import com.redhat.rhn.common.hibernate.LookupException; import com.redhat.rhn.common.localization.LocalizationService; import com.redhat.rhn.common.validator.ValidatorError; import com.redhat.rhn.common.validator.ValidatorException; import com.redhat.rhn.common.validator.ValidatorResult; import com.redhat.rhn.domain.channel.Channel; import com.redhat.rhn.domain.channel.ChannelFactory; import com.redhat.rhn.domain.entitlement.Entitlement; import com.redhat.rhn.domain.kickstart.KickstartData; import com.redhat.rhn.domain.kickstart.KickstartSession; import com.redhat.rhn.domain.rhnpackage.PackageArch; import com.redhat.rhn.domain.rhnpackage.PackageName; import com.redhat.rhn.domain.role.RoleFactory; import com.redhat.rhn.domain.server.ManagedServerGroup; import com.redhat.rhn.domain.server.Server; import com.redhat.rhn.domain.server.ServerFactory; import com.redhat.rhn.domain.server.ServerGroup; import com.redhat.rhn.domain.server.ServerGroupType; import com.redhat.rhn.domain.token.ActivationKey; import com.redhat.rhn.domain.token.ActivationKeyFactory; import com.redhat.rhn.domain.user.User; import com.redhat.rhn.frontend.struts.Scrubber; import com.redhat.rhn.manager.channel.ChannelManager; import com.redhat.rhn.manager.entitlement.EntitlementManager; import com.redhat.rhn.manager.kickstart.cobbler.CobblerXMLRPCHelper; import com.redhat.rhn.manager.rhnpackage.PackageManager; import com.redhat.rhn.manager.system.SystemManager; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.cobbler.Profile; import org.hibernate.Session; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; /** * ActivationKeyManager */ public class ActivationKeyManager { private static Logger log = Logger.getLogger(ActivationKeyManager.class); private static ActivationKeyManager instance = new ActivationKeyManager(); //private constructor private ActivationKeyManager() { } /** * @return the static instance of this class. */ public static ActivationKeyManager getInstance() { return instance; } /** * Look up an ActivationKey object by server * @param server The server in question * @param user needed for authentication * @return Returns the activation key for the server or null if one isn't found. */ public List<ActivationKey> findByServer(Server server, User user) { List<ActivationKey> keys = ActivationKeyFactory.lookupByServer(server); Iterator i = keys.iterator(); while (i.hasNext()) { ActivationKey key = (ActivationKey) i.next(); validateCredentials(user, null, key); } return keys; } /** * Look up an ActivationKey object by it's key. * @param key The activation key we're searching for. * @param user needed for authentication.. * @return Returns the activation key for the given key. */ public ActivationKey lookupByKey(String key, User user) { ActivationKey ac = ActivationKeyFactory.lookupByKey(key); validateCredentials(user, key, ac); return ac; } /** * Create a new Re-ActivationKey object for a given user, server, and note * @param user The user creating the activation key * @param server The server for the activation key * @param note A note about the activation key * @param session the kickstart session associated with the key * @return Returns a newly created and filled out Activationkey */ public ActivationKey createNewReActivationKey(User user, Server server, String note, KickstartSession session) { return createNewReActivationKey(user, server, "", note, new Long(0), null, false, session); } /** * Create a new Re-ActivationKey object for a given user, server, and note * @param user The user creating the activation key * @param server The server for the activation key * @param note A note about the activation key * @return Returns a newly created and filled out Activationkey */ public ActivationKey createNewReActivationKey(User user, Server server, String note) { return createNewReActivationKey(user, server, "", note, new Long(0), null, false, null); } /** * Create a new Re-ActivationKey object for a given user, server, and note. * @param user The user creating the activation key * @param server the server to create the key for * @param key Key to use, empty string to have one auto-generated * @param note A note about the activation key * @param usageLimit Usage limit for the activation key * @param baseChannel Base channel for the activation key * @param universalDefault Whether or not this key should be set as the universal * default. * @param session the kickstart session to associate * @return Returns a newly created and filled out Activationkey */ public ActivationKey createNewReActivationKey(User user, Server server, String key, String note, Long usageLimit, Channel baseChannel, boolean universalDefault, KickstartSession session) { if ((server == null && session == null) || (server != null && SystemManager.lookupByIdAndUser(server.getId(), user) == null)) { throw new IllegalArgumentException("Either server or session can be null, " + "but not both, otherwise use createNewActivationKey"); } ActivationKey aKey = ActivationKeyFactory.createNewKey(user, server, key, note, usageLimit, baseChannel, universalDefault); aKey.setKickstartSession(session); return aKey; } /** * Create a new ActivationKey object for a given user, and note. If you are * tying the activation key to a system (reactivation key) use * createNewReActivationKey * @param user The user creating the activation key * @param note A note about the activation key * @return Returns a newly created and filled out Activationkey */ public ActivationKey createNewActivationKey(User user, String note) { return createNewActivationKey(user, "", note, new Long(0), null, false); } /** * Create a new ActivationKey object for a given user, and note. If you are * tying the activation key to a system (reactivation key) use * createNewReActivationKey * @param user The user creating the activation key * @param key Key to use, empty string to have one auto-generated * @param note A note about the activation key * @param usageLimit Usage limit for the activation key * @param baseChannel Base channel for the activation key * @param universalDefault Whether or not this key should be set as the universal * default. * @return Returns a newly created and filled out Activationkey */ public ActivationKey createNewActivationKey(User user, String key, String note, Long usageLimit, Channel baseChannel, boolean universalDefault) { if (user.hasRole(RoleFactory.ACTIVATION_KEY_ADMIN)) { return ActivationKeyFactory.createNewKey(user, null, key, note, usageLimit, baseChannel, universalDefault); } String msg = "Cannot create activation key with key = " + key + ". The user = " + user.getLogin() + " does not have requisite permissions to administer " + "the given key"; throw new IllegalArgumentException(msg); } /** * Update the given ActivationKey details. * @param target Key to update. * @param description New key description, null to leave unchanged. * @param baseChannel New base channel */ public void update(ActivationKey target, String description, Channel baseChannel) { if (description != null) { target.setNote((String)Scrubber.scrub(description)); } target.setBaseChannel(baseChannel); } /** * Add entitlements to an activation key. * @param key Activation key to be acted upon * @param entitlementLabels List of string entitlement labels for the activation key */ public void addEntitlements(ActivationKey key, List <String> entitlementLabels) { validateAddOnEntitlements(entitlementLabels, true); for (Iterator it = entitlementLabels.iterator(); it.hasNext();) { String label = (String)it.next(); ServerGroupType entitlement = ServerFactory.lookupServerGroupTypeByLabel(label); key.addEntitlement(entitlement); } } /** * Remove entitlements from an activation key. Entitlements the key does not actually * have will be ignored. * * @param key Activation key to be acted upon * @param entitlementLabels List of string entitlement labels for the activation key */ public void removeEntitlements(ActivationKey key, List <String> entitlementLabels) { validateAddOnEntitlements(entitlementLabels, false); for (String label : entitlementLabels) { ServerGroupType entitlement = ServerFactory.lookupServerGroupTypeByLabel(label); key.removeEntitlement(entitlement); } } /** * Add a channel to an activation key. * @param key Activation key to be acted upon * @param channel Channel to add */ public void addChannel(ActivationKey key, Channel channel) { key.addChannel(channel); } /** * Remove a channel from an activation key. * @param key Activation key to be acted upon * @param channel Channel to remove */ public void removeChannel(ActivationKey key, Channel channel) { key.removeChannel(channel); } /** * Add a ServerGroup to an activation key. * @param key Activation key to be acted upon * @param group ServerGroup to add */ public void addServerGroup(ActivationKey key, ManagedServerGroup group) { key.addServerGroup(group); } /** * Remove a ServerGroup from an activation key. * @param key Activation key to be acted upon * @param group ServerGroup to remove */ public void removeServerGroup(ActivationKey key, ServerGroup group) { key.removeServerGroup(group); } /** * Add a package to an activation key using the PackageName and PackageArch * provided. If desired, PackageArch may be null. * @param key Activation key to be acted upon * @param packageName PackageName to add * @param packageArch PackageArch to add */ public void addPackage(ActivationKey key, PackageName packageName, PackageArch packageArch) { key.addPackage(packageName, packageArch); } /** * Removes all packages from the activation key that match the PackageName * and PackageArch given. * @param key Activation key to be acted upon * @param packageName PackageName to remove * @param packageArch PackageArch to remove */ public void removePackage(ActivationKey key, PackageName packageName, PackageArch packageArch) { key.removePackage(packageName, packageArch); } /** * Finds all activation keys visible to user. * @param requester User requesting the list. * @return All activation keys visible to user. */ public List <ActivationKey> findAll(User requester) { Session session = null; session = HibernateFactory.getSession(); return session.getNamedQuery("ActivationKey.findByOrg") .setEntity("org", requester.getOrg()) .list(); } /** * Returns true if the the given user can * administer activation keys.. * This should be the baseline for us to load activation keys. * @param user the user to check on * @param key the activation key to authenticate. * @return true if a key can be administered. False otherwise. */ private boolean canAdministerKeys(User user, ActivationKey key) { return user != null && key != null && user.getOrg().equals(key.getOrg()) && user.hasRole(RoleFactory.ACTIVATION_KEY_ADMIN); } /** * validates that the given user can administer * the given activation key. Raises a permission exception * if the combination is invalid.. * @param user the user to authenticate * @param keyStr Key string used for lookup. Null if none was used. (i.e. lookup * by server) * @param key the key to authenticate */ public void validateCredentials(User user, String keyStr, ActivationKey key) { if (!canAdministerKeys(user, key)) { LocalizationService ls = LocalizationService.getInstance(); LookupException e; if (keyStr != null) { e = new LookupException("Could not find activation key: " + keyStr); } else { e = new LookupException("Could not find activation key"); } e.setLocalizedTitle(ls.getMessage("lookup.activationkey.title")); e.setLocalizedReason1(ls.getMessage("lookup.activationkey.reason1")); e.setLocalizedReason2(ls.getMessage("lookup.activationkey.reason2")); throw e; } } /** * Removes an activation key. The fact the an ActivationKey Object * was generated implies that the user credentials have been * verified... * @param key the key to remove * @param user TODO */ public void remove(ActivationKey key, User user) { changeCobblerProfileKey(key, key.getKey(), "", user); ActivationKeyFactory.removeKey(key); } /** * Validate the requested entitlements. At this juncture only the add-on entitlements * are to be set via the API. * * @param entitlements List of string entitlement labels to be validated. * @param adding True if adding entitlements, false if removing. */ public void validateAddOnEntitlements(List <String> entitlements, boolean adding) { ValidatorResult ve = new ValidatorResult(); for (String entitlementLabel : entitlements) { Entitlement ent = EntitlementManager.getByName(entitlementLabel); if (ent == null || ent.isBase()) { ve.addError(new ValidatorError( "system.entitle.invalid_addon_entitlement", entitlementLabel)); } } if (ve.getErrors().size() > 0) { throw new ValidatorException(ve); } } /** * Renames a given key to new key. This operation * is crucial when we are doing things like renaming * and activation key after edit by prepending its org_id to it. * @param newKey the key to rename to * @param key the key object to be renamed * @param user TODO */ public void changeKey(String newKey, ActivationKey key, User user) { String oldKey = key.getKey(); if (!newKey.equals(key.getKey())) { ActivationKeyFactory.validateKeyName(newKey); WriteMode m = ModeFactory.getWriteMode("General_queries", "update_activation_key"); Map<String, Object> params = new HashMap<String, Object>(); params.put("old_key", key.getKey()); params.put("new_key", newKey); m.executeUpdate(params); } changeCobblerProfileKey(key, oldKey, newKey, user); } /** * helper method to change an activation keys' key. * This loops through all associated kickstart profiles and makes * the change in cobbler */ private static void changeCobblerProfileKey(ActivationKey key, String oldKey, String newKey, User user) { List<KickstartData> kss = ActivationKeyFactory.listAssociatedKickstarts(key); for (KickstartData ks : kss) { if (ks.getCobblerId() != null) { Profile prof = Profile.lookupById(CobblerXMLRPCHelper.getConnection(user), ks.getCobblerId()); Set oldSet = new HashSet(); if (!StringUtils.isEmpty(oldKey)) { oldSet.add(oldKey); } Set newSet = new HashSet(); if (!StringUtils.isEmpty(newKey)) { newSet.add(newKey); } prof.syncRedHatManagementKeys(oldSet, newSet); prof.save(); } } } /** * Subscribe an activation key to the first child channel * of its base channel that contains * the packagename passed in. Returns false if it can't be subscribed. * * @param key activationKey to be subbed * @param packageName to use to lookup the channel with. * @return true if subscription was successful false otherwise */ private boolean subscribeToChildChannelWithPackageName( ActivationKey key, String packageName) { log.debug("subscribeToChildChannelWithPackageName: " + key.getId() + " name: " + packageName); /* * null base channel implies Red Hat default * so we have to subscribe all the child channels * with the package name */ List <Channel> channels = new ArrayList<Channel>(); if (key.getBaseChannel() == null) { List <Long> cids = ChannelManager.findChildChannelsWithPackage(packageName, key.getOrg()); for (Long cid : cids) { channels.add(ChannelFactory.lookupById(cid)); } } else { Long bcid = key.getBaseChannel().getId(); log.debug("found basechannel: " + bcid); // check, whether the package is available in the base channel already // f.e. libvirt available in RHEL5VT (child channel), // but in RHEL6Server (base channel) if (ChannelManager.getLatestPackageEqual(bcid, packageName) == null) { List <Long> cids = ChannelManager.findChildChannelsWithPackage(key.getOrg(), bcid, packageName, false); Collections.sort(cids); log.warn("sorted cids: " + cids.toString()); if (cids.isEmpty()) { // nothing to do log.warn("No child channel of " + bcid + " contains " + packageName); } else if (cids.size() > 1) { // if there're more channels, just do some harakiri to pick one List<Channel> chs = new ArrayList<Channel>(); for (Long cid : cids) { chs.add(ChannelFactory.lookupById(cid)); } Class[] args = {List.class}; List<Method> removeMethods = new ArrayList<Method>(); try { removeMethods.add(this.getClass().getDeclaredMethod("removeCloned", args)); removeMethods.add(this.getClass().getDeclaredMethod("removeCustom", args)); for (Method m : removeMethods) { Channel last = (Channel) m.invoke(this, chs); if (chs.isEmpty()) { channels.add(last); break; } else if (chs.size() == 1) { channels.addAll(chs); break; } } } // catch NoSuchMethodException, IllegalAccessException, // InvocationTargetException catch (Exception e) { // nothing bad happened, we'll just pick the first one } if (chs.size() > 1) { // just pick the first one channels.add(chs.get(0)); } } else { channels.add(ChannelFactory.lookupById(cids.get(0))); } } } for (Channel c : channels) { key.addChannel(c); } return !channels.isEmpty(); } private void addConfigMgmtPackages(ActivationKey key) { String [] names = { PackageManager.RHNCFG, PackageManager.RHNCFG_CLIENT, PackageManager.RHNCFG_ACTIONS}; for (String name : names) { PackageName pn = PackageManager.lookupPackageName(name); if (pn != null) { key.addPackage(pn, null); } } } /** * setups auto deployment of config files * basically does things like adding config mgmt packages * and subscribing to config channels... * @param key the activation key to be updated. */ public void setupAutoConfigDeployment(ActivationKey key) { if (subscribeToChildChannelWithPackageName(key, ChannelManager.TOOLS_CHANNEL_PACKAGE_NAME)) { addConfigMgmtPackages(key); } } /** * Enables the activation key to be virtualization ready * Adds the virt channel, the tools channel * and adds the rn-virtualization-host package * @param key the activation key to be updated. */ public void setupVirtEntitlement(ActivationKey key) { if (subscribeToChildChannelWithPackageName(key, ChannelManager.RHN_VIRT_HOST_PACKAGE_NAME)) { key.addPackage(PackageManager.lookupPackageName(ChannelManager. RHN_VIRT_HOST_PACKAGE_NAME), null); } subscribeToChildChannelWithPackageName(key, ChannelManager.VIRT_CHANNEL_PACKAGE_NAME); } }