Skip to content

Commit

Permalink
Merged in SHIBUI-855 (pull request #203)
Browse files Browse the repository at this point in the history
SHIBUI-855 Added forceAuthN attribute

Approved-by: Ryan Mathis <rmathis@unicon.net>
Approved-by: Jonathan Johnson <jj@scaldingspoon.com>
  • Loading branch information
rmathis committed Sep 26, 2018
2 parents 8dc34cb + a0f6e0a commit c0a8a91
Show file tree
Hide file tree
Showing 13 changed files with 151 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ public class RelyingPartyOverridesRepresentation implements Serializable {

private List<String> authenticationMethods = new ArrayList<>();

private boolean forceAuthn;

public boolean isSignAssertion() {
return signAssertion;
}
Expand Down Expand Up @@ -97,4 +99,12 @@ public List<String> getAuthenticationMethods() {
public void setAuthenticationMethods(List<String> authenticationMethods) {
this.authenticationMethods = authenticationMethods;
}

public boolean isForceAuthn() {
return forceAuthn;
}

public void setForceAuthn(boolean forceAuthn) {
this.forceAuthn = forceAuthn;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,9 @@ public EntityDescriptorRepresentation createRepresentationFromDescriptor(org.ope
case MDDCConstants.RELEASE_ATTRIBUTES:
representation.setAttributeRelease(getStringListOfAttributeValues(attribute.getAttributeValues()));
break;
case MDDCConstants.FORCE_AUTHN:
relyingPartyOverridesRepresentation.setForceAuthn(getBooleanValueOfAttribute(jpaAttribute));
break;
default:
break;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ public JPAEntityServiceImpl(OpenSamlObjects openSamlObjects) {
this.openSamlObjects = openSamlObjects;
}

public JPAEntityServiceImpl(OpenSamlObjects openSamlObjects, AttributeUtility attributeUtility) {
this.openSamlObjects = openSamlObjects;
this.attributeUtility = attributeUtility;
}

@Override
public List<Attribute> getAttributeListFromEntityRepresentation(EntityDescriptorRepresentation entityDescriptorRepresentation) {
List<edu.internet2.tier.shibboleth.admin.ui.domain.Attribute> list = new ArrayList<>();
Expand Down Expand Up @@ -109,6 +114,9 @@ public List<Attribute> getAttributeListFromRelyingPartyOverridesRepresentation(R
if (relyingPartyOverridesRepresentation.getAuthenticationMethods() != null && relyingPartyOverridesRepresentation.getAuthenticationMethods().size() > 0) {
list.add(attributeUtility.createAttributeWithArbitraryValues(MDDCConstants.DEFAULT_AUTHENTICATION_METHODS, MDDCConstants.DEFAULT_AUTHENTICATION_METHODS_FN, relyingPartyOverridesRepresentation.getAuthenticationMethods()));
}
if (relyingPartyOverridesRepresentation.isForceAuthn()) {
list.add(attributeUtility.createAttributeWithBooleanValue(MDDCConstants.FORCE_AUTHN, MDDCConstants.FORCE_AUTHN_FN, true));
}
}

return (List<Attribute>)(List<? extends Attribute>)list;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ public class MDDCConstants {
public static final String DEFAULT_AUTHENTICATION_METHODS = "http://shibboleth.net/ns/profiles/defaultAuthenticationMethods";
public static final String DEFAULT_AUTHENTICATION_METHODS_FN = "defaultAuthenticationMethods";

public static final String FORCE_AUTHN = "http://shibboleth.net/ns/profiles/forceAuthn";
public static final String FORCE_AUTHN_FN = "forceAuthn";

public static final Map<String, String> PROTOCOL_BINDINGS;

static {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ public static RelyingPartyOverridesRepresentation getRelyingPartyOverridesRepres
case MDDCConstants.DEFAULT_AUTHENTICATION_METHODS:
relyingPartyOverridesRepresentation.setAuthenticationMethods(getStringListValueOfAttribute(jpaAttribute));
break;
case MDDCConstants.FORCE_AUTHN:
relyingPartyOverridesRepresentation.setForceAuthn(getBooleanValueOfAttribute(jpaAttribute));
break;
default:
break;
}
Expand Down Expand Up @@ -158,6 +161,9 @@ public static List<org.opensaml.saml.saml2.core.Attribute> getAttributeListFromA
list.add(ATTRIBUTE_UTILITY.createAttributeWithArbitraryValues(MDDCConstants.DEFAULT_AUTHENTICATION_METHODS, MDDCConstants
.DEFAULT_AUTHENTICATION_METHODS_FN, relyingPartyOverridesRepresentation.getAuthenticationMethods()));
}
if (relyingPartyOverridesRepresentation.isForceAuthn()) {
list.add(ATTRIBUTE_UTILITY.createAttributeWithBooleanValue(MDDCConstants.FORCE_AUTHN, MDDCConstants.FORCE_AUTHN_FN, true));
}
}

return (List<org.opensaml.saml.saml2.core.Attribute>) (List<? extends org.opensaml.saml.saml2.core.Attribute>) list;
Expand Down
2 changes: 2 additions & 0 deletions backend/src/main/resources/i18n/messages_en.properties
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,7 @@ label.attribute-eduPersonEntitlement=eduPersonEntitlement
label.attribute-eduPersonAssurance=eduPersonAssurance
label.attribute-eduPersonUniqueId=eduPersonUniqueId
label.attribute-employeeNumber=employeeNumber
label.force-authn=Force AuthN

message.must-be-unique=Must be unique.
message.name-must-be-unique=Name must be unique.
Expand Down Expand Up @@ -356,6 +357,7 @@ message.entity-id-min-unique=You must add at least one entity id target and they
message.required-for-scripts=Required for Scripts
message.required-for-regex=Required for Regex

tooltip.force-authn=Disallows use (or reuse) of authentication results and login flows that don\u0027t provide a real-time proof of user presence in the login process
tooltip.service-provider-name-dashboard-display-only=Service Provider Name (Dashboard Display Only)
tooltip.service-provider-entity-id=Service Provider Entity ID
tooltip.organization-name=Organization Name
Expand Down
2 changes: 2 additions & 0 deletions backend/src/main/resources/i18n/messages_es.properties
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,7 @@ label.attribute-eduPersonEntitlement=(es) eduPersonEntitlement
label.attribute-eduPersonAssurance=(es) eduPersonAssurance
label.attribute-eduPersonUniqueId=(es) eduPersonUniqueId
label.attribute-employeeNumber=(es) employeeNumber
label.force-authn=(es) Force AuthN

message.must-be-unique=(es) Must be unique.
message.name-must-be-unique=(es) Name must be unique.
Expand Down Expand Up @@ -356,6 +357,7 @@ message.entity-id-min-unique=(es) You must add at least one entity id target and
message.required-for-scripts=(es) Required for Scripts
message.required-for-regex=(es) Required for Regex

tooltip.force-authn=(es) Disallows use (or reuse) of authentication results and login flows that don\u0027t provide a real-time proof of user presence in the login process
tooltip.service-provider-name-dashboard-display-only=(es) Service Provider Name (Dashboard Display Only)
tooltip.service-provider-entity-id=(es) Service Provider Entity ID
tooltip.organization-name=(es) Organization Name
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -457,7 +457,8 @@ class EntityDescriptorControllerTests extends Specification {
"omitNotBefore": false,
"responderId": null,
"nameIdFormats": [],
"authenticationMethods": []
"authenticationMethods": [],
"forceAuthn": false
},
"attributeRelease": [
"givenName",
Expand Down Expand Up @@ -585,7 +586,8 @@ class EntityDescriptorControllerTests extends Specification {
"omitNotBefore": false,
"responderId": null,
"nameIdFormats": [],
"authenticationMethods": []
"authenticationMethods": [],
"forceAuthn": false
},
"attributeRelease": [
"givenName",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.EntityDescriptorRe
import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.LogoutEndpointRepresentation
import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.MduiRepresentation
import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.OrganizationRepresentation
import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.RelyingPartyOverridesRepresentation
import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.SecurityInfoRepresentation
import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.ServiceProviderSsoDescriptorRepresentation
import edu.internet2.tier.shibboleth.admin.ui.opensaml.OpenSamlObjects
import edu.internet2.tier.shibboleth.admin.ui.util.RandomGenerator
import edu.internet2.tier.shibboleth.admin.ui.util.TestObjectGenerator
import edu.internet2.tier.shibboleth.admin.util.AttributeUtility
import org.springframework.boot.test.json.JacksonTester
import org.xmlunit.builder.DiffBuilder
import org.xmlunit.builder.Input
Expand All @@ -31,7 +33,8 @@ class JPAEntityDescriptorServiceImplTests extends Specification {
it
}

def service = new JPAEntityDescriptorServiceImpl(openSamlObjects, new JPAEntityServiceImpl(openSamlObjects))
def service = new JPAEntityDescriptorServiceImpl(openSamlObjects,
new JPAEntityServiceImpl(openSamlObjects, new AttributeUtility(openSamlObjects)))

JacksonTester<EntityDescriptorRepresentation> jacksonTester

Expand Down Expand Up @@ -447,6 +450,46 @@ class JPAEntityDescriptorServiceImplTests extends Specification {
!diff.hasDifferences()
}

def "SHIBUI-855, generate forceAuthn XML"() {
when:

def expected = '''
<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" entityID="http://test.example.org/test1">
<md:Extensions>
<mdattr:EntityAttributes xmlns:mdattr="urn:oasis:names:tc:SAML:metadata:attribute">
<saml2:Attribute xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" FriendlyName="forceAuthn" Name="http://shibboleth.net/ns/profiles/forceAuthn" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
<saml2:AttributeValue xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xsd:boolean">true</saml2:AttributeValue>
</saml2:Attribute>
</mdattr:EntityAttributes>
</md:Extensions>
</md:EntityDescriptor>
'''

def test = openSamlObjects.marshalToXmlString(service.createDescriptorFromRepresentation(new EntityDescriptorRepresentation().with {
it.entityId = 'http://test.example.org/test1'
it.relyingPartyOverrides = new RelyingPartyOverridesRepresentation().with {
it.forceAuthn = true;
it
}
it
}))

def diff = DiffBuilder.compare(Input.fromString(expected)).withTest(Input.fromString(test)).ignoreComments().ignoreWhitespace().build()

then:
!diff.hasDifferences()
}


def "SHIBUI-855, read forceAuthn from json"() {
when:
def representation = new ObjectMapper().readValue(this.class.getResource('/json/SHIBUI-855.json').bytes, EntityDescriptorRepresentation)
def output = service.createRepresentationFromDescriptor(service.createDescriptorFromRepresentation(representation))

then:
assert output.relyingPartyOverrides?.forceAuthn == true
}

def "test ACS configuration"() {
when:
def expected = '''
Expand Down
45 changes: 45 additions & 0 deletions backend/src/test/resources/json/SHIBUI-855.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"id": "",
"entityId": "test",
"serviceProviderName": "test",
"organization": {
"name": null,
"displayName": null,
"url": null
},
"contacts": [],
"mdui": {
"displayName": null,
"informationUrl": null,
"privacyStatementUrl": null,
"description": null,
"logoUrl": null,
"logoHeight": null,
"logoWidth": null
},
"securityInfo": {
"x509CertificateAvailable": false,
"authenticationRequestsSigned": false,
"wantAssertionsSigned": false,
"x509Certificates": []
},
"assertionConsumerServices": [
{
"binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST",
"locationUrl": "test",
"makeDefault": true
}
],
"serviceProviderSsoDescriptor": {
"protocolSupportEnum": null,
"nameIdFormats": []
},
"logoutEndpoints": [],
"serviceEnabled": false,
"relyingPartyOverrides": {
"nameIdFormats": [],
"authenticationMethods": [],
"forceAuthn": true
},
"attributeRelease": []
}
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,23 @@
[attr.aria-label]="'tooltip.instruction' | translate"></i>
</fieldset>
</div>
<div class="form-group">
<fieldset aria-label="Force Authn? Checkbox - select for yes">
<div class="custom-control custom-checkbox custom-control-inline custom-control-reverse">
<input disableValidation="true" type="checkbox" class="custom-control-input" formControlName="forceAuthn"
[value]="true" id="forceAuthn" role="checkbox" aria-checked="false">
<label class="custom-control-label" translate="label.force-authn" for="forceAuthn">
Force AuthN
</label>
</div>
<ng-template #tooltipForceAuthn>
<translate-i18n key="tooltip.force-authn">Disallows use (or reuse) of authentication results and login flows that don't provide a real-time proof of user
presence in the login process</translate-i18n>
</ng-template>
<i class="info-icon fa fa-fw fa-info-circle text-primary fa-lg" [ngbPopover]="tooltipForceAuthn"
[attr.aria-label]="'tooltip.instruction' | translate"></i>
</fieldset>
</div>
<div class="form-group">
<fieldset aria-label="Omit not before condition? Checkbox - select for yes">
<div class="custom-control custom-checkbox custom-control-inline custom-control-reverse">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export class RelyingPartyFormComponent extends ProviderFormFragmentComponent imp
turnOffEncryption: false,
useSha: false,
ignoreAuthenticationMethod: false,
forceAuthn: false,
omitNotBefore: false,
responderId: '',
nameIdFormats: this.nameIdFormatList,
Expand Down
6 changes: 6 additions & 0 deletions ui/src/assets/schema/filter/entity-attributes.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,12 @@
"type": "boolean",
"default": false
},
"forceAuthn": {
"title": "label.force-authn",
"description": "tooltip.force-authn",
"type": "boolean",
"default": false
},
"omitNotBefore": {
"title": "label.omit-not-before-condition",
"type": "boolean",
Expand Down

0 comments on commit c0a8a91

Please sign in to comment.