/** * 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.frontend.action.channel.manage.repo; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.redhat.rhn.domain.channel.ContentSourceType; import com.redhat.rhn.frontend.xmlrpc.channel.repo.InvalidRepoTypeException; import org.apache.commons.lang.StringUtils; import org.apache.struts.action.ActionErrors; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; import org.apache.struts.action.ActionMessage; import org.apache.struts.action.ActionMessages; import org.apache.struts.action.DynaActionForm; import org.apache.struts.util.LabelValueBean; import com.redhat.rhn.common.client.InvalidCertificateException; import com.redhat.rhn.common.localization.LocalizationService; 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.channel.ContentSource; import com.redhat.rhn.domain.channel.ContentSourceFilter; import com.redhat.rhn.domain.channel.SslContentSource; import com.redhat.rhn.domain.kickstart.KickstartFactory; import com.redhat.rhn.domain.kickstart.crypto.SslCryptoKey; import com.redhat.rhn.domain.org.Org; import com.redhat.rhn.frontend.struts.RequestContext; import com.redhat.rhn.frontend.struts.RhnAction; import com.redhat.rhn.frontend.struts.RhnHelper; import com.redhat.rhn.frontend.struts.RhnValidationHelper; import com.redhat.rhn.frontend.xmlrpc.InvalidParameterException; import com.redhat.rhn.frontend.xmlrpc.channel.repo.InvalidRepoLabelException; import com.redhat.rhn.frontend.xmlrpc.channel.repo.InvalidRepoUrlException; import com.redhat.rhn.manager.channel.repo.BaseRepoCommand; import com.redhat.rhn.manager.channel.repo.CreateRepoCommand; import com.redhat.rhn.manager.channel.repo.EditRepoCommand; /** * CobblerSnippetDetailsAction * @version $Rev$ */ public class RepoDetailsAction extends RhnAction { public static final String CREATE_MODE = "create_mode"; public static final String REPO = "repo"; public static final String URL = "url"; public static final String LABEL = "label"; public static final String TYPE = "contenttype"; public static final String SSL_CA_CERT = "sslcacert"; public static final String SSL_CLIENT_CERT = "sslclientcert"; public static final String SSL_CLIENT_KEY = "sslclientkey"; public static final String SOURCEID = "sourceid"; public static final String FILTERS = "filters"; private static final String VALIDATION_XSD = "/com/redhat/rhn/frontend/action/channel/" + "manage/repo/validation/repoForm.xsd"; /** {@inheritDoc} */ public ActionForward execute(ActionMapping mapping, ActionForm formIn, HttpServletRequest request, HttpServletResponse response) { DynaActionForm form = (DynaActionForm) formIn; RequestContext ctx = new RequestContext(request); Map<String, Object> params = makeParamMap(request); String cid = null; request.setAttribute(mapping.getParameter(), Boolean.TRUE); if (ctx.hasParam("cid")) { cid = ctx.getParam("cid", false); request.setAttribute("cid", cid); } if (ctx.isSubmitted()) { ValidatorResult result = RhnValidationHelper.validate(this.getClass(), makeValidationMap(form), null, VALIDATION_XSD); if (!result.isEmpty()) { getStrutsDelegate().saveMessages(request, result); RhnValidationHelper.setFailedValidation(request); } else { try { ActionErrors errors = new ActionErrors(); ContentSource repo = submit(request, errors, form); if (!errors.isEmpty()) { addErrors(request, errors); setupContentTypes(ctx); setupCryptoKeys(ctx); bindRepo(request, repo); return mapping.findForward(RhnHelper.DEFAULT_FORWARD); } if (isCreateMode(request)) { createSuccessMessage(request, "repos.jsp.create.success", repo.getLabel()); } else { createSuccessMessage(request, "repos.jsp.update.success", repo.getLabel()); } request.removeAttribute(CREATE_MODE); /* If cid isn't specified, repo will be created as usual. Otherwise repo will be created and will be automatically assigned to channel cid with redirection to channel repo page */ if (cid != null) { params.put("cid", cid); Channel chan = ChannelFactory.lookupById(Long.parseLong(cid)); Set<ContentSource> sources = chan.getSources(); sources.add(repo); ChannelFactory.save(chan); createSuccessMessage(request, "channel.edit.repo.updated", chan.getLabel()); return getStrutsDelegate().forwardParams( mapping.findForward("channelSub"), params); } setupRepo(request, form, repo); params.put("id", repo.getId()); return getStrutsDelegate().forwardParams( mapping.findForward("success"), params); } catch (ValidatorException ve) { getStrutsDelegate().saveMessages(request, ve.getResult()); RhnValidationHelper.setFailedValidation(request); } } } setup(request, form, isCreateMode(request)); return getStrutsDelegate().forwardParams(mapping.findForward(RhnHelper .DEFAULT_FORWARD), params); } private Map<String, String> makeValidationMap(DynaActionForm form) { Map<String, String> map = new HashMap<String, String>(); map.put(LABEL, form.getString(LABEL)); map.put(URL, form.getString(URL)); return map; } private boolean isCreateMode(HttpServletRequest request) { return Boolean.TRUE.equals(request.getAttribute(CREATE_MODE)); } private void setup(HttpServletRequest request, DynaActionForm form, boolean createMode) { RequestContext context = new RequestContext(request); setupContentTypes(context); setupCryptoKeys(context); if (!createMode) { request.setAttribute("id", context.getParamAsLong("id")); setupRepo(request, form, ChannelFactory.lookupContentSource( context.getParamAsLong("id"), context.getCurrentUser().getOrg())); } } private void setupContentTypes(RequestContext context) { List<LabelValueBean> contentTypes = new ArrayList<LabelValueBean>(); for (ContentSourceType ct : ChannelFactory.listContentSourceTypes()) { contentTypes.add(lv(ct.getLabel(), ct.getLabel())); } context.getRequest().setAttribute("contenttypes", contentTypes); } private void setupCryptoKeys(RequestContext context) { List<LabelValueBean> sslCrytpoKeyOptions = new ArrayList<LabelValueBean>(); sslCrytpoKeyOptions.add(lv(LocalizationService.getInstance(). getMessage("generic.jsp.none"), "")); for (Iterator<SslCryptoKey> iter = KickstartFactory.lookupSslCryptoKeys( context.getCurrentUser().getOrg()).iterator(); iter.hasNext();) { SslCryptoKey sck = iter.next(); sslCrytpoKeyOptions.add(lv(sck.getDescription(), sck.getId().toString())); } context.getRequest().setAttribute("sslcryptokeys", sslCrytpoKeyOptions); } private void setupRepo(HttpServletRequest request, DynaActionForm form, ContentSource repo) { form.set(LABEL, repo.getLabel()); form.set(URL, repo.getSourceUrl()); form.set(SOURCEID, repo.getId()); form.set(TYPE, repo.getType().getLabel()); Set<SslContentSource> repoSslSets = repo.getSslSets(); if (!repoSslSets.isEmpty()) { SslContentSource sslRepo = repoSslSets.iterator().next(); form.set(SSL_CA_CERT, getStringId(sslRepo.getCaCert())); form.set(SSL_CLIENT_CERT, getStringId(sslRepo.getClientCert())); form.set(SSL_CLIENT_KEY, getStringId(sslRepo.getClientKey())); } // Filters // The goal here is to transform the db filter representation to // something user-friendly. String currentFlag = ""; String filterGroup = ""; List<ContentSourceFilter> filters = ChannelFactory .lookupContentSourceFiltersById(repo.getId()); List<String> filterGroups = new ArrayList<String>(); for (ContentSourceFilter filter : filters) { String flag = filter.getFlag(); if (currentFlag.equals(flag)) { filterGroup = filterGroup + "," + filter.getFilter(); } else { if (!filterGroup.isEmpty()) { filterGroups.add(filterGroup); } filterGroup = flag + filter.getFilter(); currentFlag = flag; } } // finally add the last one filterGroups.add(filterGroup); form.set(FILTERS, StringUtils.join(filterGroups.toArray(), ' ')); bindRepo(request, repo); } /** * Method to bind the repo to a request * @param request the servlet request * @param repo content source */ public static void bindRepo(HttpServletRequest request, ContentSource repo) { request.setAttribute(REPO, repo); } private List<ContentSourceFilter> processFilters(String formFilters) throws InvalidParameterException { List<ContentSourceFilter> ret = new ArrayList<ContentSourceFilter>(); if (!formFilters.isEmpty()) { // split on whitespace String[] filters = formFilters.split("\\s+"); String flag = ""; int order = 0; for (String filter : filters) { if (filter.isEmpty()) { continue; // ignore whitespace at beginning of field } if (filter.startsWith("+") || filter.startsWith("-")) { flag = filter.substring(0, 1); filter = filter.substring(1); } else if (flag.equals("")) { // the first filter must have a flag throw new InvalidParameterException("repos.jsp.filters.error"); } // else assume the flag has not changed String[] rpms = filter.split(","); for (String rpm : rpms) { if (rpm.isEmpty()) { continue; // ignore extra commas or whitespace around commas } ContentSourceFilter f = new ContentSourceFilter(); f.setFlag(flag); f.setFilter(rpm); f.setSortOrder(order); ret.add(f); order++; } } } return ret; } private ContentSource submit(HttpServletRequest request, ActionErrors errors, DynaActionForm form) { RequestContext context = new RequestContext(request); String url = form.getString(URL); String label = form.getString(LABEL); String type = form.getString(TYPE); String sfilters = form.getString(FILTERS); Org org = context.getCurrentUser().getOrg(); BaseRepoCommand repoCmd = null; if (isCreateMode(request)) { repoCmd = new CreateRepoCommand(org); } else { repoCmd = new EditRepoCommand(context.getCurrentUser(), context.getParamAsLong(SOURCEID)); } repoCmd.setLabel(label); repoCmd.setUrl(url); repoCmd.setType(type); try { // Add SSL // FIXME: Allow to set multiple SSL sets per custom repo - new page? repoCmd.deleteAllSslSets(); repoCmd.addSslSet(parseIdFromForm(form, SSL_CA_CERT), parseIdFromForm(form, SSL_CLIENT_CERT), parseIdFromForm(form, SSL_CLIENT_KEY)); // Process filters List<ContentSourceFilter> lresult = processFilters(sfilters); // Store Repo repoCmd.store(); // Store Filters Long repoid = repoCmd.getRepo().getId(); ChannelFactory.clearContentSourceFilters(repoid); for (ContentSourceFilter filter : lresult) { filter.setSourceId(repoid); ChannelFactory.save(filter); } } catch (InvalidRepoUrlException e) { errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage( "edit.channel.repo.repourlinuse", null)); } catch (InvalidRepoLabelException e) { errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage( "edit.channel.repo.repolabelinuse", repoCmd.getLabel())); } catch (InvalidCertificateException e) { errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage( "edit.channel.repo.clientcertmissing")); } catch (InvalidRepoTypeException e) { errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage( "edit.channel.repo.invalidrepotype", repoCmd.getType())); } catch (InvalidParameterException e) { errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage(e.getMessage())); } return repoCmd.getRepo(); } private Long parseIdFromForm(DynaActionForm form, String stringName) { Long id = null; try { id = Long.parseLong(form.getString(stringName)); } catch (NumberFormatException nfe) { // empty } return id; } private String getStringId(SslCryptoKey sck) { String strId = ""; if (sck != null) { Long id = sck.getId(); if (id != null) { strId = id.toString(); } } return strId; } }