Page tree
Skip to end of metadata
Go to start of metadata

File(s): conf/relying-party.xml, conf/idp.properties

Format: Native Spring / Deprecated Custom Schema

Legacy V2 File(s): conf/relying-party.xml

Overview

The relying-party.xml file is used to specify the SAML (or other) functions you want the IdP to support (these are termed "profiles"), and to customize IdP or profile settings based on the identity or other characteristics of a relying party service.

You might modify this file to:

  • control which profiles are supported for particular partners (or for anonymous requests)
  • alter default profile or global settings
  • define and attach custom security configurations at various levels, such as:
    • turning off encryption of assertions and other content
    • changing which layer signing is performed (responses vs. assertions)
    •  supporting multiple signing credentials
    • customizing algorithms

The goal of any deployment is to reduce the number of customizations used, so the ideal is to rarely have to modify this configuration.

When using the deprecated syntax for compatibility with V2, refer to the older documentation. For those with an understanding of that syntax, you will find a significant attempt to overlap/emulate that structure, hopefully with some simplifications that Spring's more general syntax makes possible.

While the native Spring syntax can ultimately be used to wire up any beans you choose to define, we have abstracted this to some degree in the default configuration files by making use of conventions, parent beans, and fixed bean IDs to limit the amount of wiring you have to perform to make useful changes.

You MUST define three beans:

  • shibboleth.UnverifiedRelyingParty
  • shibboleth.DefaultRelyingParty
  • shibboleth.RelyingPartyOverrides

All three are defined for you by default. The first two must define beans that derive from the RelyingPartyConfiguration class, but this is automatically done by inheriting (via the parent attribute) from the bean named RelyingParty. The last one is a list bean for any overrides, again of that type. Those beans will generally contain an activationCondition property that determines whether one applies to a request or not, such that the first applicable override bean is used. More on that below.

The "unverified" RP bean controls how to handle requests from peers with no metadata or other mechanism in place to authenticate/verify the request. By default, no profiles are enabled, signified by the empty profileConfigurations list.

The "default" RP bean applies to requests from peers that do not fit the conditions attached to any overrides, and are thus handled with default settings. By default, this relies on a variety of built-in settings, and activates a number of the usual profiles.

Overrides

In V2, there were essentially two kinds of <RelyingParty> overrides, both sharing a Name attribute; matching was done based on the entityID of the SP, or by walking SAML metadata to locate matching containing <EntitiesDescriptor> groups.

With V3, this is generalized to allow for arbitrary conditions attached to an override definition. The arbitrary nature of these conditions are the main reason we've avoided the use of custom XML schema in various places in the IdP; it would be impossible to express everything without a great deal of work, and it would make the job of creating new conditions much harder for developers. But the downside is that defining and attaching a condition can require more than a few lines of Spring wiring in some cases.

To simplify this in a few common scenarios, we have included additional template beans that can be inherited from to more easily express some conditions. Right now, we have three such examples, emulating the two conditions supported by V2 and adding a third:

  • RelyingPartyByName
  • RelyingPartyByGroup
  • RelyingPartyByTag

Examples of how to use these follow:

RelyingPartyByName Example
<!-- Example matching one SP -->
<bean parent="RelyingPartyByName" c:relyingPartyIds="https://sp.example.org">
    <property name="profileConfigurations">
        <list>
		<!-- Your refs or beans here. -->
        </list>
    </property>
</bean>

<!-- Example matching two SPs -->
<bean parent="RelyingPartyByName" c:relyingPartyIds="#{{'https://sp.example.org', 'https://another.example.org'}}">
    <property name="profileConfigurations">
        <list>
		<!-- Your refs or beans here. -->
        </list>
    </property>
</bean>

(Note that the second example above uses a Spring Expression Language inline list.)

The following example notwithstanding, it's not advisable to create policy based on groups because those hierarchies are very dependent on metadata aggregation and publishing approaches that are going to change over time and are very limiting.

RelyingPartyByGroup Example
<bean parent="RelyingPartyByGroup" c:groupNames="urn:mace:incommon">
	<property name="profileConfigurations">
		<list>
		<!-- Your refs or beans here. -->
		</list>
	</property>
</bean>

V3.4 introduces support for group matching based on a SAML metadata feature called an <AffiliationDescriptor>, and you are able to create locally (or even remotely) expressed group memberships using that technique, by supplying supplemental metadata to the system.

An alternative grouping strategy is based on <EntityAttributes> metadata extension tags.

RelyingPartyByTag Example
<bean parent="RelyingPartyByTag">
	<constructor-arg name="candidates">
		<list>
			<bean parent="TagCandidate" c:name="http://macedir.org/entity-category"
				p:values="http://refeds.org/category/research-and-scholarship"/>
		</list>
	</constructor-arg>
	<property name="profileConfigurations">
		<list>
		<!-- Your refs or beans here. -->
		</list>
	</property>
</bean>

Finally, the most general example involves defining your own bean and a custom condition. An example using a regular expression:

Custom RelyingParty Example
<bean id="exampleOrgRegex" class="java.util.regex.Pattern" factory-method="compile"
	c:_0="^https://sp[\d].example\.org/shibboleth$" />

<bean id="exampleOrgRegexPredicate"
	class="com.google.common.base.Predicates" factory-method="contains"
	c:_0-ref="exampleOrgRegex" />

<bean id="custom.RelyingPartyCondition"
	class="net.shibboleth.idp.profile.logic.RelyingPartyIdPredicate"
	c:_0-ref="exampleOrgRegexPredicate" />

<bean id="CustomRelyingParty" parent="RelyingParty">
	<property name="activationCondition" ref="custom.RelyingPartyCondition" />
	<property name="profileConfigurations">
		<list>
			<!-- Your refs or beans here. -->
		</list>
	</property>
</bean>

Multiple Overrides

If more than one override is defined, then it is possible for the activation conditions of multiple overrides to be satisfied by a request. Overrides have "first one wins" match semantics whose ordering is determined by their position in the shibboleth.RelyingPartyOverrides list bean in relying-party.xml. This leads to a rule of thumb for overrides: more general matches (i.e. by group) should be placed after more specific matches (i.e. by name).

The first override with an ActivationCondition that returns true will be in effect. Multiple matching overrides are not merged or combined in any way. However, it's possible to combine different "dimensions" of conditions by using dynamically-derived profile settings, a feature discussed in the next section.

Profile Configurations

Every relying party configuration (default or override) has a profileConfigurations property whose value is a list of ProfileConfiguration beans that determine which profiles can be used. Any profile not explicitly listed will be disabled and requests for it will fail internally with an error.

Profiles are activated within relying party configurations in most cases by referencing beans defined within the system that use default settings for each profile. This wouldn't apply to custom profiles defined in extensions, but it does apply to the built-in feature set. We've pre-defined beans for the built-in profiles as follows:

They are all active by default because they are included in the shibboleth.DefaultRelyingParty bean's profileConfigurations property, by reference. You can turn them on or off using comments or by deleting them.

Additional profile beans are available to enable  the CAS protocol, see CasProtocolConfiguration.

As with all relying party settings, an override does not inherit the profiles enabled by default. An override essentially turns everything off unless its own profileConfigurations property enables it.

Overriding Default Settings Statically

To override a default profile option, you can replace a <ref> element with a bean definition like so:

<bean parent="SAML2.SSO" p:nameIDFormatPrecedence="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" />

The parent attribute lets you pull in the pre-defined bean definition for a profile and just override what you want to. You can mix these beans with <ref> elements that rely on default behavior with other profiles.

Overriding Default Settings Dynamically 3.3

With V3.3 and above, most profile settings can be derived at runtime using Java functions or scripts, termed "lookup strategies", instead of declaring them statically. This can be done for default or overridden relying party configurations, and provides a powerful way of combining different kinds of rules.

This is a useful trick to use if you want to apply "cross-cutting" conditions to get around the limitation that overrides don't get merged. For example, consider the following use cases:

  • You want to enable consent for attribute release for a specific set of relying parties.
  • You want to downgrade to the use of SHA-1 for a specific set of relying parties.

Of course, if these two sets don't overlap, and you have nothing else unusual to specify, you could create two overrides for each set individually. But what if the two sets overlap, with some relying parties in one, some in the other, and some in both? Now you need three overrides. Now consider that a third set requires an additional non-default setting and overlaps with some of the first two sets. The number of overrides will get out of hand quickly and start to get very confusing to manage.

As an example, let's tackle the cases above by using scripts to derive the settings involved. This can potentially be done with no overrides at all, as below, though that's a matter of style.

Use of scripts to derive profile settings
<!-- Whether to run attribute release interceptor. -->
<bean id="InterceptorScript" parent="shibboleth.ContextFunctions.Scripted" factory-method="inlineScript">
    <constructor-arg>
        <value>
<![CDATA[
		interceptors = null;
		rpid = "";
		rpCtx = input.getSubcontext("net.shibboleth.idp.profile.context.RelyingPartyContext");
		if (rpCtx != null) {
			rpid = rpCtx.getRelyingPartyId();
        }

		if (rpid.equals("https://sp1.example.org/shibboleth") ||
			rpid.equals("https://sp2.example.org/shibboleth") ||
			rpid.equals("https://sp3.example.org/shibboleth")) {

			listType =  Java.type("java.util.ArrayList");
			interceptors = new listType(1);
			interceptors.add("attribute-release");
		}

		interceptors;
]]>
        </value>
    </constructor-arg>
</bean>

<!-- Map of security configurations for use by next script. -->
<util:map id="SecurityConfigMap">
	<entry key="SHA2" value-ref="shibboleth.DefaultSecurityConfiguration"/>
	<entry key="SHA1">
		<bean parent="shibboleth.DefaultSecurityConfiguration"
		    p:signatureSigningConfiguration-ref="shibboleth.SigningConfiguration.SHA1" />
 </entry>
</util:map>

<!-- Whether to use SHA-1. -->
<bean id="SecurityConfigScript" parent="shibboleth.ContextFunctions.Scripted"
	factory-method="inlineScript"
	p:customObject-ref="SecurityConfigMap">
    <constructor-arg>
        <value>
<![CDATA[
		rpid = "";
		rpCtx = input.getSubcontext("net.shibboleth.idp.profile.context.RelyingPartyContext");
		if (rpCtx != null) {
			rpid = rpCtx.getRelyingPartyId();
        }

		securityConfig = custom["SHA2"];

		if (rpid.equals("https://sp2.example.org/shibboleth") ||
			rpid.equals("https://sp3.example.org/shibboleth") ||
			rpid.equals("https://sp4.example.org/shibboleth")) {

			securityConfig = custom["SHA1"];
		}

		securityConfig;
]]>
        </value>
    </constructor-arg>
</bean>

<!-- Apply the scripts to derive settings. -->
<bean id="shibboleth.DefaultRelyingParty" parent="RelyingParty">
	<property name="profileConfigurations">
		<list>
			<bean parent="Shibboleth.SSO"
				p:postAuthenticationFlowsLookupStrategy-ref="InterceptorScript"
				p:securityConfigurationLookupStrategy-ref="SecurityConfigScript" />
			<bean parent="SAML2.SSO"
				p:postAuthenticationFlowsLookupStrategy-ref="InterceptorScript"
				p:securityConfigurationLookupStrategy-ref="SecurityConfigScript" />
			<ref bean="SAML2.ECP"
				p:securityConfigurationLookupStrategy-ref="SecurityConfigScript" />
			<ref bean="SAML2.Logout"
				p:securityConfigurationLookupStrategy-ref="SecurityConfigScript" />
		</list>
	</property>
</bean>

Obviously the example above is somewhat contrived. It's longer than just creating three overrides, but it illustrates the general idea and once you get comfortable using scripts, it isn't as bad as it looks. It's also possible to put scripts in separate files, which makes the XML much shorter.

This becomes much more powerful when combined with other techniques, particularly the use of tag-based conditions based on <EntityAttribute> extensions in SAML metadata, which can be applied by metadata registrars or locally using a metadata filter. In particular, it becomes possible to create discrete (even centrally maintained) scripts to check for tags and return particular settings, and then apply all the scripts together to a single relying party configuration, once, and then never modify the scripts or the relying party configuration on a routine basis. Instead, you apply a setting by attaching a tag to metadata, which may be manageable upstream of the IdP by a federation or locally using some kind of metadata management tool.

In the long run, building tools to manage metadata or a single configuration file like the metadata configuration is more practical than building tools to manage many different files, and there is more potential for standardization and use across software products.

As an example, the same case above handled using tags might look something like this:

Use of scripts to derive profile settings using metadata tags
<bean id="ConsentTagCondition" parent="shibboleth.Conditions.EntityDescriptor">
    <constructor-arg name="pred">
        <bean class="org.opensaml.saml.common.profile.logic.EntityAttributesPredicate">
            <constructor-arg name="candidates">
                <list>
                    <bean parent="TagCandidate" c:name="http://example.org/entity-category"
                        p:values="http://example.org/category/requires-consent" />
                </list>
            </constructor-arg>
        </bean>
    </constructor-arg>
</bean>

<!-- Whether to run attribute release interceptor. -->
<bean id="InterceptorScript" parent="shibboleth.ContextFunctions.Scripted" factory-method="inlineScript"
        p:customObject-ref="ConsentTagCondition">
    <constructor-arg>
        <value>
<![CDATA[
        interceptors = null;
        
        if (custom.apply(input)) {
            listType =  Java.type("java.util.ArrayList");
            interceptors = new listType(1);
            interceptors.add("attribute-release");
        }
 
        interceptors;
]]>
        </value>
    </constructor-arg>
</bean>

<bean id="SHA1TagCondition" parent="shibboleth.Conditions.EntityDescriptor">
    <constructor-arg name="pred">
        <bean class="org.opensaml.saml.common.profile.logic.EntityAttributesPredicate">
            <constructor-arg name="candidates">
                <list>
                    <bean parent="TagCandidate" c:name="http://example.org/entity-category"
                        p:values="http://example.org/category/requires-sha1" />
                </list>
            </constructor-arg>
        </bean>
    </constructor-arg>
</bean>

<!-- Map of security configurations for use by next script. -->
<util:map id="SecurityConfigMap">
    <entry key="condition" value-ref="SHA1TagCondition" />
    <entry key="SHA2" value-ref="shibboleth.DefaultSecurityConfiguration" />
    <entry key="SHA1">
        <bean parent="shibboleth.DefaultSecurityConfiguration"
            p:signatureSigningConfiguration-ref="shibboleth.SigningConfiguration.SHA1" />
 </entry>
</util:map>
 
<!-- Whether to use SHA-1. -->
<bean id="SecurityConfigScript" parent="shibboleth.ContextFunctions.Scripted" factory-method="inlineScript"
    p:customObject-ref="SecurityConfigMap">
    <constructor-arg>
        <value>
<![CDATA[ 
        custom[custom["condition"].apply(input) ? "SHA1" : "SHA2"];
]]>
        </value>
    </constructor-arg>
</bean>

<!-- Apply the scripts to derive settings. -->
<bean id="shibboleth.DefaultRelyingParty" parent="RelyingParty">
	<property name="profileConfigurations">
		<list>
			<bean parent="Shibboleth.SSO"
				p:postAuthenticationFlowsLookupStrategy-ref="InterceptorScript"
				p:securityConfigurationLookupStrategy-ref="SecurityConfigScript" />
			<bean parent="SAML2.SSO"
				p:postAuthenticationFlowsLookupStrategy-ref="InterceptorScript"
				p:securityConfigurationLookupStrategy-ref="SecurityConfigScript" />
			<ref bean="SAML2.ECP"
				p:securityConfigurationLookupStrategy-ref="SecurityConfigScript" />
			<ref bean="SAML2.Logout"
				p:securityConfigurationLookupStrategy-ref="SecurityConfigScript" />
		</list>
	</property>
</bean

It doesn't appear vastly simpler or shorter, but the scripts are a bit simpler and more importantly, changing the set of entities the scripts apply to can be managed through metadata and not by updating the scripts or conditions.

Custom Profile Defaults

If custom profile defaults are needed for several categories of relying party, it is helpful to define top-level profile beans and reference them in the relying parties instead of the system default beans. This approach reduces duplication and produces a more readable configuration. A concrete example is instructive: suppose that an IdP needs to declare custom NameID precedence for SSO profiles for the default relying party and several overrides. The following configuration excerpt demonstrates the approach applied to that case.

Custom Profile Default Beans Example
    <bean id="Shibboleth.SSO.custom" parent="Shibboleth.SSO"
          p:nameIDFormatPrecedence="#{{
            'urn:mace:shibboleth:1.0:nameIdentifier',
            'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress',
            'urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified'}}" />
    <bean id="SAML2.SSO.custom" parent="SAML2.SSO"
          p:nameIDFormatPrecedence="#{{
            'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent',
            'urn:oasis:names:tc:SAML:2.0:nameid-format:transient',
            'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress',
            'urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified'}}" />
 
    <bean id="shibboleth.DefaultRelyingParty" parent="RelyingParty">
        <property name="profileConfigurations">
            <util:list>
                <ref bean="Shibboleth.SSO.custom" />
                <ref bean="SAML1.AttributeQuery" />
                <ref bean="SAML1.ArtifactResolution" />
                <ref bean="SAML2.SSO.custom" />
                <ref bean="SAML2.ECP" />
                <ref bean="SAML2.Logout" />
                <ref bean="SAML2.AttributeQuery" />
                <ref bean="SAML2.ArtifactResolution" />
            </util:list>
        </property>
    </bean>
 
    <util:list id="shibboleth.RelyingPartyOverrides">
        <bean parent="RelyingPartyByName"
              c:relyingPartyIds="#{{'https://a.example.com/shibboleth', 'https://b.example.com/shibboleth'}}">
            <property name="profileConfigurations">
                <list>
                    <bean parent="SAML2.SSO.custom" p:encryptAssertions="false" />
                </list>
            </property>
        </bean>
        <bean parent="RelyingPartyByName"
              c:relyingPartyIds="#{{'https://x.example.com/shibboleth', 'https://y.example.com/shibboleth'}}">
            <property name="profileConfigurations">
                <list>
                    <bean parent="Shibboleth.SSO.custom"
                          p:signAssertions="true"
                          p:signResponses="false" />
                    <bean parent="SAML2.SSO.custom"
                          p:signAssertions="true"
                          p:signResponses="false" />
                </list>
            </property>
        </bean>
    </util:list>

Reference

Beans

Beans defined in relying-party.xml and related system configuration follow:

Bean ID
Type
Function
shibboleth.UnverifiedRelyingPartyRelyingPartyConfigurationConfigures IdP behavior for unauthenticated/unverifiable requests
shibboleth.DefaultRelyingPartyRelyingPartyConfigurationConfigures default IdP behavior for authenticated/verified requests
shibboleth.RelyingPartyOverridesList<RelyingPartyConfiguration>Configures non-default IdP behavior for requests that meet activation conditions attached to overrides
RelyingPartyRelyingPartyConfigurationA template bean for use in defining RelyingParty overrides by hand
RelyingPartyByNameRelyingPartyConfigurationA template bean for defining RelyingParty overrides based on matching by name
RelyingPartyByGroupRelyingPartyConfigurationA template bean for defining RelyingParty overrides based on matching by <EntitiesDescriptor> groups
RelyingPartyByTagRelyingPartyConfigurationA template bean for defining RelyingParty overrides based on matching <EntityAttributes> extension content
TagCandidateEntityAttributesPredicate.CandidateA template bean for defining EntityAttribute matching rules for injection into beans based on RelyingPartyByTag

Shibboleth.SSO

BrowserSSOProfileConfigurationDefault configuration for SAML 1.1 SSO profile
SAML1.AttributeQueryAttributeQueryProfileConfigurationDefault configuration for SAML 1.1 Attribute Query profile
SAML1.ArtifactResolutionArtifactResolutionProfileConfigurationDefault configuration for SAML 1.1 Artifact Resolution profile
SAML2.SSOBrowserSSOProfileConfigurationDefault configuration for SAML 2.0 SSO profile
SAML2.ECPECPProfileConfigurationDefault configuration for SAML 2.0 Enhanced Client/Proxy profile
SAML2.LogoutDefault configuration for SAML 2.0 Single Logout profile
SAML2.AttributeQueryAttributeQueryProfileConfigurationDefault configuration for SAML 2.0 Attribute Query profile
SAML2.ArtifactResolutionArtifactResolutionProfileConfigurationDefault configuration for SAML 2.0 Artifact Resolution profile
Liberty.SSOSSSOSProfileConfigurationDefault configuration for Liberty ID-WSF Delegated SSO profile
CAS.LoginConfigurationLoginConfigurationDefault configuration for CAS login prototol
CAS.ProxyConfigurationProxyConfigurationDefault configuration for CAS proxy login protocol
CAS.ValidateConfigurationValidateConfigurationDefault configuration for CAS ticket validation protocol
shibboleth.DefaultArtifactConfigurationBasicSAMLArtifactConfigurationDefault configuration for SAML Artifact usage, injected into artifact-supporting SAML profile beans

Properties

Properties defined in idp.properties directly related to this configuration area follow:

Property
Type
Default
Function

idp.entityID

URINoneThe unique name of the IdP, used as the "issuer" in all SAML profiles
idp.artifact.enabledBooleantrueWhether to allow use of the SAML artifact bindings when sending messages
idp.artifact.secureChannelBooleantrueWhether preparation of messages to be communicated via SAML artifact should assume use of a secure channel (allowing signing and encryption to be skipped)
idp.artifact.endpointIndexInteger2Identifies the <ArtifactResolutionService> endpoint in SAML metadata associated with artifacts issued by a server node

System Profile Defaults

Without stepping fully into the SecurityConfiguration topic, the following defaults are used when enabling individual profiles. In addition, an appropriate "security policy" flow is enabled during request processing to enforce appropriate security guarantees.

  • All SAML Profiles
    • includeConditionsNotBefore = true
    • assertionLifetime = PT5M
    • signedRequestsPredicate = alwaysFalse
    • signAssertionsPredicate = alwaysFalse

  • Shibboleth.SSO
    • includeAttributeStatement = false
    • signResponsesPredicate = alwaysTrue
    • use of type 1 SAML artifacts where required

  • SAML1.AttributeQuery
  • SAML1.ArtifactResolution
    • signResponsesPredicate = if TLS isn't used or port 443 is used

  • SAML2.SSO
  • SAML2.ECP
    • includeAttributeStatement = true
    • skipEndpointValidationWhenSigned = false
    • maximumSPSessionLifetime = 0
    • signResponsesPredicate = alwaysTrue
    • encryptAssertionsPredicate = alwaysTrue
    • encryptNameIDsPredicate = alwaysFalse
    • encryptAttributesPredicate = alwaysFalse
    • use of type 4 SAML artifacts where required with an endpoint index of %{idp.artifact.endpointIndex:2}

  • SAML2.Logout
    • signRequestsPredicate = alwaysTrue on front channel, if TLS isn't used or port 443 is used on back channel
    • signResponsesPredicate = alwaysTrue on front channel, if TLS isn't used or port 443 is used on back channel
    • encryptNameIDsPredicate = alwaysTrue on front channel, if TLS isn't used or port 443 is used on back channel
    • use of type 4 SAML artifacts where required with an endpoint index of %{idp.artifact.endpointIndex:2}

  • SAML2.AttributeQuery
  • SAML2.ArtifactResolution
    • signResponsesPredicate = if TLS isn't used or port 443 is used
    • encryptAssertionsPredicate = if TLS isn't used or port 443 is used

V2 Compatibility

The V3 IdP supports the deprecated V2 relying-party.xml schema for compatibility and ease of upgrades, but deployers are encouraged to plan for migration off of this format, as it will cease to be supported in a future version. The IdP should load any existing V2 relying-party.xml file and configure itself in an expected manner. This can also potentially overlap with MetadataConfiguration and SecurityConfiguration, but not all of the file is actually processed; all of the content related to security policy rules at the end of the file is ignored and no changes made to that section will affect the IdP.  A warning is issued when this legacy content is ignored.

In order to load a legacy relying-party.xml file, a special set of resources must be loaded in services.xml to ensure proper compatibility support. This is intended to be triggered by setting "idp.service.relyingparty.resources = shibboleth.LegacyRelyingPartyResolverResources" in services.properties, which is done for you when an upgraded installation is performed.

The support for the legacy format is limited in that not all new IdP features can be supported. In particular, specifying custom or advanced rules for when to activate particular <RelyingParty> elements is not possible, nor is it possible to specify per-RelyingParty use of signing or encryption algorithms. Essentially you get the V2 feature set when using the old syntax.

  • No labels