Hello, the attached patch set implements checking of signed metadata before mirroring with spacewalk-repo-sync. This patch adds a checkbox in "Manage Repositories". If this checkbox is checked, spacewalk-repo-sync expects, that the repo metadata are signed and try to verify the signature with an installed gpg key. If a matching gpg key is not found and the new commandile parameter --non-interactive is given, spacewalk-repo-sync abort with an error. if --non-interactive is not given, spacewalk-repo-sync try to download the key file from the server, display the values and ask the user to accept the key. If the user agree, the key is installed and the signature will be verified.
-- MFG Michael Calmer -------------------------------------------------------------------------- Michael Calmer SUSE LINUX Products GmbH, Maxfeldstr. 5, D-90409 Nuernberg T: +49 (0) 911 74053 0 F: +49 (0) 911 74053575 - e-mail: michael.cal...@suse.com -------------------------------------------------------------------------- SUSE LINUX Products GmbH, GF: Markus Rex, HRB 16746 (AG Nürnberg)
From 79e5e782c657e40f6a5fd18f4b16bd36ceb14855 Mon Sep 17 00:00:00 2001 From: Michael Calmer <m...@suse.de> Date: Sun, 6 Mar 2011 14:12:34 +0100 Subject: [PATCH 1/2] add column metadata_signed to rhnContentSource table --- .../spacewalk/common/tables/rhnContentSource.sql | 4 ++++ .../019-add-metadata_signed.sql | 7 +++++++ 2 files changed, 11 insertions(+), 0 deletions(-) create mode 100644 schema/spacewalk/upgrade/spacewalk-schema-1.3-to-spacewalk-schema-1.4/019-add-metadata_signed.sql diff --git a/schema/spacewalk/common/tables/rhnContentSource.sql b/schema/spacewalk/common/tables/rhnContentSource.sql index dc93a27..df768ee 100644 --- a/schema/spacewalk/common/tables/rhnContentSource.sql +++ b/schema/spacewalk/common/tables/rhnContentSource.sql @@ -29,6 +29,10 @@ rhnContentSource references rhnContentSourceType(id), source_url varchar2(512) NOT NULL, label varchar2(64) NOT NULL, + metadata_signed CHAR(1) + DEFAULT ('Y') NOT NULL + CONSTRAINT rhn_cs_ms_ck + CHECK (metadata_signed in ( 'Y' , 'N' )), created date default(sysdate) NOT NULL, modified date default(sysdate) NOT NULL ) diff --git a/schema/spacewalk/upgrade/spacewalk-schema-1.3-to-spacewalk-schema-1.4/019-add-metadata_signed.sql b/schema/spacewalk/upgrade/spacewalk-schema-1.3-to-spacewalk-schema-1.4/019-add-metadata_signed.sql new file mode 100644 index 0000000..ae0f1f0 --- /dev/null +++ b/schema/spacewalk/upgrade/spacewalk-schema-1.3-to-spacewalk-schema-1.4/019-add-metadata_signed.sql @@ -0,0 +1,7 @@ + +alter table rhnContentSource +add metadata_signed char(1) default('Y') + constraint rhn_cs_ms_nn not null + constraint rhn_cs_ms_ck + check (metadata_signed in ('Y','N')); + -- 1.7.1
From 4fa0bd3b2561ded494877bcbe668dd6dbfb7a7c1 Mon Sep 17 00:00:00 2001 From: Michael Calmer <m...@suse.de> Date: Mon, 25 Oct 2010 11:20:29 +0100 Subject: [PATCH 2/2] implement metadata signature checking for repositories expect not signed metadata with spacewalk-repo-sync if url is given on the commandline fix changing "hasSignedMetadata" style fixes --- backend/satellite_tools/repo_plugins/yum_src.py | 165 +++++++++++++++++++- backend/satellite_tools/reposync.py | 44 +++++- .../rhn/domain/channel/ContentSource.hbm.xml | 1 + .../redhat/rhn/domain/channel/ContentSource.java | 17 ++ .../channel/manage/repo/RepoDetailsAction.java | 20 +++ .../channel/manage/repo/validation/repoForm.xsd | 2 +- .../frontend/strings/jsp/StringResource_en_US.xml | 3 + .../rhn/manager/channel/repo/BaseRepoCommand.java | 21 +++ .../redhat/rhn/taskomatic/task/RepoSyncTask.java | 1 + .../pages/channel/manage/repo/repodetails.jsp | 8 + java/code/webapp/WEB-INF/struts-config.xml | 1 + 11 files changed, 271 insertions(+), 12 deletions(-) diff --git a/backend/satellite_tools/repo_plugins/yum_src.py b/backend/satellite_tools/repo_plugins/yum_src.py index 782d49d..3b52d0a 100644 --- a/backend/satellite_tools/repo_plugins/yum_src.py +++ b/backend/satellite_tools/repo_plugins/yum_src.py @@ -16,10 +16,18 @@ import yum import shutil import sys +import os from yum import config from yum.update_md import UpdateMetadata -from spacewalk.satellite_tools.reposync import ContentPackage from spacewalk.common import CFG, initCFG +from spacewalk.satellite_tools.reposync import ContentPackage, ChannelException +from urlgrabber.grabber import URLGrabber +import urlgrabber +from yum import misc, Errors +from urlgrabber.grabber import default_grabber +from yum.i18n import to_unicode, to_utf8 +from rpmUtils.transaction import initReadOnlyTransaction +import subprocess class YumWarnings: def write(self, s): @@ -35,9 +43,11 @@ class ContentSource: name = None repo = None cache_dir = '/var/cache/rhn/reposync/' - def __init__(self, url, name): + def __init__(self, url, name, insecure=False, interactive=True): self.url = url self.name = name + self.insecure = insecure + self.interactive = interactive self._clean_cache(self.cache_dir + name) # read the proxy configuration in /etc/rhn/rhn.conf @@ -65,14 +75,22 @@ class ContentSource: if self.proxy_url is not None: repo.proxy = self.proxy_url + if self.insecure: + repo.repo_gpgcheck = False + else: + repo.repo_gpgcheck = True warnings = YumWarnings() warnings.disable() repo.baseurlSetup() warnings.restore() - - repo.setup(False) + for burl in repo.baseurl: + repo.gpgkey = [burl + '/repodata/repomd.xml.key'] + + repo.setup(False, None, gpg_import_func=self.getKeyForRepo, confirm_func=self.askImportKey) + self.initgpgdir( repo.gpgdir ) sack = repo.getPackageSack() sack.populate(repo, 'metadata', None, 0) + list = sack.returnPackages() to_return = [] for pack in list: @@ -106,3 +124,142 @@ class ContentSource: um = UpdateMetadata() um.add(self.repo, all=True) return um.notices + + def getKeyForRepo(self, repo, callback=None): + """ + Retrieve a key for a repository If needed, prompt for if the key should + be imported using callback + + @param repo: Repository object to retrieve the key of. + @param callback: Callback function to use for asking for verification + of a key. Takes a dictionary of key info. + """ + keyurls = [] + if self.interactive: + keyurls = repo.gpgkey + key_installed = False + for keyurl in keyurls: + keys = self._retrievePublicKey(keyurl, repo) + for info in keys: + # Check if key is already installed + if info['keyid'] in misc.return_keyids_from_pubring(repo.gpgdir): + #print('GPG key at %s (0x%s) is already imported' % ( + # keyurl, info['hexkeyid'])) + continue + + # Try installing/updating GPG key + #print('Importing GPG key 0x%s "%s" from %s' % + # (info['hexkeyid'], + # to_unicode(info['userid']), + # keyurl.replace("file://",""))) + rc = False + if callback: + rc = callback({'repo':repo, 'userid':info['userid'], + 'hexkeyid':info['hexkeyid'], 'keyurl':keyurl, + 'fingerprint':info['fingerprint'], + 'timestamp':info['timestamp']}) + + + if not rc: + #raise ChannelException, "Not installing key for repo %s" % repo + continue + + # Import the key + result = misc.import_key_to_pubring(info['raw_key'], info['hexkeyid'], gpgdir=repo.gpgdir) + if not result: + raise ChannelException, 'Key import failed' + result = self.import_key_to_rpmdb(info['raw_key'], info['hexkeyid'], gpgdir=repo.gpgdir) + if not result: + raise ChannelException, 'Key import failed' + + #print('Key imported successfully') + key_installed = True + + if not key_installed: + raise ChannelException, 'The GPG keys listed for the "%s" repository are ' \ + 'already installed but they are not correct.\n' \ + 'Check that the correct key URLs are configured for ' \ + 'this repository.' % (repo) + + + def _retrievePublicKey(self, keyurl, repo=None): + """ + Retrieve a key file + @param keyurl: url to the key to retrieve + Returns a list of dicts with all the keyinfo + """ + key_installed = False + + #print( 'Retrieving GPG key from %s') % keyurl + + # Go get the GPG key from the given URL + try: + url = misc.to_utf8(keyurl) + if repo is None: + rawkey = urlgrabber.urlread(url, limit=9999) + else: + # If we have a repo. use the proxy etc. configuration for it. + # In theory we have a global proxy config. too, but meh... + # external callers should just update. + ug = URLGrabber(bandwidth = repo.bandwidth, + retry = repo.retries, + throttle = repo.throttle, + progress_obj = repo.callback, + proxies=repo.proxy_dict) + ug.opts.user_agent = default_grabber.opts.user_agent + rawkey = ug.urlread(url, text=repo.id + "/gpgkey") + + except urlgrabber.grabber.URLGrabError, e: + raise ChannelException('GPG key retrieval failed: ' + + to_unicode(str(e))) + # Parse the key + keys_info = misc.getgpgkeyinfo(rawkey, multiple=True) + keys = [] + for keyinfo in keys_info: + thiskey = {} + for info in ('keyid', 'timestamp', 'userid', + 'fingerprint', 'raw_key'): + if not keyinfo.has_key(info): + raise ChannelException, \ + 'GPG key parsing failed: key does not have value %s' % info + thiskey[info] = keyinfo[info] + thiskey['keyid'] = str("%16x" % (thiskey['keyid'] & 0xffffffffffffffffL)).upper() + thiskey['hexkeyid'] = misc.keyIdToRPMVer(keyinfo['keyid']).upper() + keys.append(thiskey) + + return keys + + def askImportKey(self, d ): + if self.interactive: + print 'Do you want to import the GPG key 0x%s "%s" from %s? [y/n]:' % (d['hexkeyid'], to_unicode(d['userid']), d['keyurl'],) + yn = sys.stdin.readline() + yn = yn.strip() + + if yn in ['y', 'Y', 'j', 'J']: + return True + + return False + + def initgpgdir(self, gpgdir): + if not os.path.exists(gpgdir): + os.makedirs(gpgdir) + + ts = initReadOnlyTransaction("/") + for hdr in ts.dbMatch('name', 'gpg-pubkey'): + if hdr['description'] != "": + misc.import_key_to_pubring(hdr['description'], hdr['version'], gpgdir=gpgdir) + + def import_key_to_rpmdb(self, raw, keyid, gpgdir): + if not os.path.exists(gpgdir): + os.makedirs(gpgdir) + tmpfile = os.path.join(gpgdir, keyid) + fp = open(tmpfile, 'w') + fp.write(raw) + fp.close() + cmd = ['/bin/rpm', '--import', tmpfile] + p = subprocess.Popen(cmd) + sts = os.waitpid(p.pid, 0)[1] + os.remove(tmpfile) + if sts == 0: + return True + return False diff --git a/backend/satellite_tools/reposync.py b/backend/satellite_tools/reposync.py index 3fcded2..2849b56 100644 --- a/backend/satellite_tools/reposync.py +++ b/backend/satellite_tools/reposync.py @@ -29,10 +29,25 @@ from spacewalk.server.importlib.backendOracle import SQLBackend from spacewalk.common.rhn_mpm import InvalidPackageError from spacewalk.server.importlib.errataImport import ErrataImport from spacewalk.server import taskomatic +from yum import Errors +from yum.i18n import to_unicode, to_utf8 default_log_location = '/var/log/rhn/reposync/' default_hash = 'sha256' +class ChannelException(Exception): + """ + Channel Error. + """ + def __init__(self, value=None): + Exception.__init__(self) + self.value = value + def __str__(self): + return "%s" %(self.value,) + + def __unicode__(self): + return '%s' % to_unicode(self.value) + class RepoSync: parser = None @@ -43,6 +58,7 @@ class RepoSync: fail = False quiet = False regen = False + noninteractive = False def main(self): initCFG('server') @@ -68,7 +84,7 @@ class RepoSync: if not options.url: if options.channel_label: # TODO:need to look at user security across orgs - h = rhnSQL.prepare("""select s.source_url + h = rhnSQL.prepare("""select s.source_url, s.metadata_signed from rhnContentSource s, rhnChannelContentSource cs, rhnChannel c @@ -78,12 +94,12 @@ class RepoSync: h.execute(label=options.channel_label) source_urls = h.fetchall_dict() or [] if source_urls: - self.urls = [row['source_url'] for row in source_urls] + self.urls = source_urls else: quit = True self.error_msg("Channel has no URL associated") else: - self.urls = [options.url] + self.urls = [{'source_url':options.url, 'metadata_signed' : 'N'}] if not options.channel_label: quit = True self.error_msg("--channel must be specified") @@ -100,15 +116,28 @@ class RepoSync: self.fail = options.fail self.quiet = options.quiet self.channel = self.load_channel() + self.noninteractive = options.noninteractive if not self.channel or not rhnChannel.isCustomChannel(self.channel['id']): print "Channel does not exist or is not custom" sys.exit(1) - for url in self.urls: - plugin = self.load_plugin()(url, self.channel_label) - self.import_packages(plugin, url) - self.import_updates(plugin, url) + for data in self.urls: + url = data['source_url'] + insecure = False; + if data['metadata_signed'] == 'N': + insecure = True; + try: + plugin = self.load_plugin()(url, self.channel_label, insecure, (not self.noninteractive)) + self.import_packages(plugin, url) + self.import_updates(plugin, url) + except ChannelException, e: + self.print_msg("ChannelException: %s" % e) + sys.exit(1) + except Errors.YumGPGCheckError, e: + self.print_msg("YumGPGCheckError: : %s" % e) + sys.exit(1) + if self.regen: taskomatic.add_to_repodata_queue_for_channel_package_subscription( [self.channel_label], [], "server.app.yumreposync") @@ -131,6 +160,7 @@ class RepoSync: self.parser.add_option('-t', '--type', action='store', dest='type', help='The type of repo, currently only "yum" is supported', default='yum') self.parser.add_option('-f', '--fail', action='store_true', dest='fail', default=False , help="If a package import fails, fail the entire operation") self.parser.add_option('-q', '--quiet', action='store_true', dest='quiet', default=False, help="Print no output, still logs output") + self.parser.add_option('-n', '--non-interactive', action='store_true', dest='noninteractive', default=False, help="Do not ask anything, use default answers automatically.") return self.parser.parse_args() def load_plugin(self): diff --git a/java/code/src/com/redhat/rhn/domain/channel/ContentSource.hbm.xml b/java/code/src/com/redhat/rhn/domain/channel/ContentSource.hbm.xml index 79ad517..39bf85f 100644 --- a/java/code/src/com/redhat/rhn/domain/channel/ContentSource.hbm.xml +++ b/java/code/src/com/redhat/rhn/domain/channel/ContentSource.hbm.xml @@ -17,6 +17,7 @@ PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" <property name="created" type="timestamp" column="created" insert="false" update="false" /> <property name="modified" type="timestamp" column="modified" insert="false" update="false" /> <property name="label" type="string" column="label"/> + <property name="metadataSigned" type="yes_no" column="metadata_signed"/> <many-to-one name="org" column="org_id" diff --git a/java/code/src/com/redhat/rhn/domain/channel/ContentSource.java b/java/code/src/com/redhat/rhn/domain/channel/ContentSource.java index 54115b5..eb991ed 100644 --- a/java/code/src/com/redhat/rhn/domain/channel/ContentSource.java +++ b/java/code/src/com/redhat/rhn/domain/channel/ContentSource.java @@ -12,6 +12,9 @@ * granted to use or replicate Red Hat trademarks that are incorporated * in this software or its documentation. */ +/* + * Copyright (c) 2010 SUSE LINUX Products GmbH, Nuernberg, Germany. + */ package com.redhat.rhn.domain.channel; import com.redhat.rhn.domain.BaseDomainHelper; @@ -32,6 +35,7 @@ public class ContentSource extends BaseDomainHelper implements Identifiable { private ContentSourceType type; private String sourceUrl; private String label; + private boolean metadataSigned; private Set<Channel> channels = new HashSet<Channel>(); /** * @return Returns the label. @@ -78,6 +82,19 @@ public class ContentSource extends BaseDomainHelper implements Identifiable { this.id = idIn; } + /** + * @return Returns metadataSigned + */ + public boolean getMetadataSigned() { + return this.metadataSigned; + } + + /** + * @param md set metadataSigned + */ + public void setMetadataSigned(boolean md) { + this.metadataSigned = md; + } /** * @return Returns the type. diff --git a/java/code/src/com/redhat/rhn/frontend/action/channel/manage/repo/RepoDetailsAction.java b/java/code/src/com/redhat/rhn/frontend/action/channel/manage/repo/RepoDetailsAction.java index 029e55b..eeb2973 100644 --- a/java/code/src/com/redhat/rhn/frontend/action/channel/manage/repo/RepoDetailsAction.java +++ b/java/code/src/com/redhat/rhn/frontend/action/channel/manage/repo/RepoDetailsAction.java @@ -12,6 +12,9 @@ * granted to use or replicate Red Hat trademarks that are incorporated * in this software or its documentation. */ +/* + * Copyright (c) 2010 SUSE LINUX Products GmbH, Nuernberg, Germany. + */ package com.redhat.rhn.frontend.action.channel.manage.repo; import java.util.HashMap; @@ -42,6 +45,7 @@ import com.redhat.rhn.manager.channel.repo.BaseRepoCommand; import com.redhat.rhn.manager.channel.repo.CreateRepoCommand; import com.redhat.rhn.manager.channel.repo.EditRepoCommand; +import org.apache.log4j.Logger; /** * CobblerSnippetDetailsAction @@ -54,10 +58,14 @@ public class RepoDetailsAction extends RhnAction { public static final String URL = "url"; public static final String LABEL = "label"; public static final String SOURCEID = "sourceid"; + public static final String METADATA_SIGNED = "metadataSigned"; private static final String VALIDATION_XSD = "/com/redhat/rhn/frontend/action/channel/" + "manage/repo/validation/repoForm.xsd"; + + private static Logger logger = Logger.getLogger(RepoDetailsAction.class); + /** {@inheritDoc} */ public ActionForward execute(ActionMapping mapping, ActionForm formIn, @@ -112,6 +120,10 @@ public class RepoDetailsAction extends RhnAction { else if (!isCreateMode(request)) { setup(request, form); } + else if (isCreateMode(request)) { + // default for has signed metadata should be true + form.set(METADATA_SIGNED, new Boolean(true)); + } return mapping.findForward(RhnHelper.DEFAULT_FORWARD); } @@ -140,6 +152,7 @@ public class RepoDetailsAction extends RhnAction { form.set(LABEL, repo.getLabel()); form.set(URL, repo.getSourceUrl()); form.set(SOURCEID, repo.getId()); + form.set(METADATA_SIGNED, new Boolean(repo.getMetadataSigned())); bindRepo(request, repo); } @@ -157,6 +170,12 @@ public class RepoDetailsAction extends RhnAction { RequestContext context = new RequestContext(request); String url = form.getString(URL); String label = form.getString(LABEL); + Boolean metadataSigned = (Boolean) form.get(METADATA_SIGNED); + if (metadataSigned == null) { + // disabled checkbox doesn't return a value, so result is null + // set it to false + metadataSigned = Boolean.FALSE; + } Org org = context.getLoggedInUser().getOrg(); BaseRepoCommand repoCmd = null; if (isCreateMode(request)) { @@ -169,6 +188,7 @@ public class RepoDetailsAction extends RhnAction { repoCmd.setLabel(label); repoCmd.setUrl(url); + repoCmd.setMetadataSigned(metadataSigned.booleanValue()); try { repoCmd.store(); diff --git a/java/code/src/com/redhat/rhn/frontend/action/channel/manage/repo/validation/repoForm.xsd b/java/code/src/com/redhat/rhn/frontend/action/channel/manage/repo/validation/repoForm.xsd index 46a630b..ad75385 100644 --- a/java/code/src/com/redhat/rhn/frontend/action/channel/manage/repo/validation/repoForm.xsd +++ b/java/code/src/com/redhat/rhn/frontend/action/channel/manage/repo/validation/repoForm.xsd @@ -15,4 +15,4 @@ </simpleType> </attribute> -</schema> \ No newline at end of file +</schema> diff --git a/java/code/src/com/redhat/rhn/frontend/strings/jsp/StringResource_en_US.xml b/java/code/src/com/redhat/rhn/frontend/strings/jsp/StringResource_en_US.xml index dfdb91c..375952f 100644 --- a/java/code/src/com/redhat/rhn/frontend/strings/jsp/StringResource_en_US.xml +++ b/java/code/src/com/redhat/rhn/frontend/strings/jsp/StringResource_en_US.xml @@ -9855,6 +9855,9 @@ Please note that some manual configuration of these scripts may still be require <trans-unit id="repos.jsp.create.url"> <source>Repository URL</source> </trans-unit> + <trans-unit id="repos.jsp.create.metadataSigned"> + <source>Has Signed Metadata?</source> + </trans-unit> <trans-unit id="repos.jsp.create.submit"> <source>Create Repository</source> </trans-unit> diff --git a/java/code/src/com/redhat/rhn/manager/channel/repo/BaseRepoCommand.java b/java/code/src/com/redhat/rhn/manager/channel/repo/BaseRepoCommand.java index 849459e..0f6be98 100644 --- a/java/code/src/com/redhat/rhn/manager/channel/repo/BaseRepoCommand.java +++ b/java/code/src/com/redhat/rhn/manager/channel/repo/BaseRepoCommand.java @@ -12,6 +12,9 @@ * granted to use or replicate Red Hat trademarks that are incorporated * in this software or its documentation. */ +/* + * Copyright (c) 2010 SUSE LINUX Products GmbH, Nuernberg, Germany. + */ package com.redhat.rhn.manager.channel.repo; import java.util.List; @@ -34,6 +37,7 @@ public class BaseRepoCommand { private String label; private String url; private Org org; + private boolean metadata_signed; private List<ValidatorError> errors; /** @@ -91,6 +95,22 @@ public class BaseRepoCommand { } /** + * + * @return true if metadata should be signed + */ + public boolean getMetadataSigned() { + return metadata_signed; + } + + /** + * + * @param md set if metadata are signed + */ + public void setMetadataSigned(boolean md) { + this.metadata_signed = md; + } + + /** * Check for errors and store Org to db. * @throws InvalidRepoUrlException in case repo wih given url already exists * in the org @@ -115,6 +135,7 @@ public class BaseRepoCommand { } repo.setSourceUrl(this.url); } + repo.setMetadataSigned(this.metadata_signed); ChannelFactory.save(repo); } diff --git a/java/code/src/com/redhat/rhn/taskomatic/task/RepoSyncTask.java b/java/code/src/com/redhat/rhn/taskomatic/task/RepoSyncTask.java index dfc6ad3..32e4dd6 100644 --- a/java/code/src/com/redhat/rhn/taskomatic/task/RepoSyncTask.java +++ b/java/code/src/com/redhat/rhn/taskomatic/task/RepoSyncTask.java @@ -75,6 +75,7 @@ public class RepoSyncTask extends RhnJavaJob { cmd.add(c.getLabel()); cmd.add("--type"); cmd.add(ChannelFactory.CONTENT_SOURCE_TYPE_YUM.getLabel()); + cmd.add("--non-interactive"); return cmd; } } diff --git a/java/code/webapp/WEB-INF/pages/channel/manage/repo/repodetails.jsp b/java/code/webapp/WEB-INF/pages/channel/manage/repo/repodetails.jsp index 28ac129..da600ee 100644 --- a/java/code/webapp/WEB-INF/pages/channel/manage/repo/repodetails.jsp +++ b/java/code/webapp/WEB-INF/pages/channel/manage/repo/repodetails.jsp @@ -55,6 +55,14 @@ <html:text property="url"/> </td> </tr> + <tr> + <th> + <bean:message key="repos.jsp.create.metadataSigned"/> + </th> + <td> + <html:checkbox property="metadataSigned" /> + </td> + </tr> </table> diff --git a/java/code/webapp/WEB-INF/struts-config.xml b/java/code/webapp/WEB-INF/struts-config.xml index a0e89ad..a964afc 100644 --- a/java/code/webapp/WEB-INF/struts-config.xml +++ b/java/code/webapp/WEB-INF/struts-config.xml @@ -859,6 +859,7 @@ <form-property name="url" type="java.lang.String"/> <form-property name="sourceid" type="java.lang.Long" /> <form-property name="label" type="java.lang.String"/> + <form-property name="metadataSigned" type="java.lang.Boolean"/> </form-bean> <form-bean name="cobblerSnippetsForm" -- 1.7.1
signature.asc
Description: This is a digitally signed message part.
_______________________________________________ Spacewalk-devel mailing list Spacewalk-devel@redhat.com https://www.redhat.com/mailman/listinfo/spacewalk-devel