KEYCLOAK-4062 - GUI changes for KeyName format + few tests
This commit is contained in:
parent
5448403345
commit
5006fe2292
7 changed files with 102 additions and 9 deletions
|
@ -571,12 +571,8 @@ public abstract class AbstractSamlAuthenticationHandler implements SamlAuthentic
|
||||||
key = locator.getKey(keyId);
|
key = locator.getKey(keyId);
|
||||||
boolean keyLocated = key != null;
|
boolean keyLocated = key != null;
|
||||||
|
|
||||||
if (validateRedirectBindingSignatureForKey(sigAlg, rawQueryBytes, decodedSignature, key)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (keyLocated) {
|
if (keyLocated) {
|
||||||
return false;
|
return validateRedirectBindingSignatureForKey(sigAlg, rawQueryBytes, decodedSignature, key);
|
||||||
}
|
}
|
||||||
} catch (KeyManagementException ex) {
|
} catch (KeyManagementException ex) {
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,8 @@ import org.keycloak.common.util.PemUtils;
|
||||||
import org.keycloak.keys.Attributes;
|
import org.keycloak.keys.Attributes;
|
||||||
import org.keycloak.keys.KeyProvider;
|
import org.keycloak.keys.KeyProvider;
|
||||||
import org.keycloak.keys.RsaKeyProviderFactory;
|
import org.keycloak.keys.RsaKeyProviderFactory;
|
||||||
|
import org.keycloak.protocol.saml.SamlClient;
|
||||||
|
import org.keycloak.protocol.saml.SamlConfigAttributes;
|
||||||
import org.keycloak.representations.idm.ComponentRepresentation;
|
import org.keycloak.representations.idm.ComponentRepresentation;
|
||||||
import org.keycloak.protocol.saml.mappers.AttributeStatementHelper;
|
import org.keycloak.protocol.saml.mappers.AttributeStatementHelper;
|
||||||
import org.keycloak.protocol.saml.mappers.RoleListMapper;
|
import org.keycloak.protocol.saml.mappers.RoleListMapper;
|
||||||
|
@ -42,6 +44,7 @@ import org.keycloak.representations.idm.UserRepresentation;
|
||||||
import org.keycloak.saml.BaseSAML2BindingBuilder;
|
import org.keycloak.saml.BaseSAML2BindingBuilder;
|
||||||
import org.keycloak.saml.SAML2ErrorResponseBuilder;
|
import org.keycloak.saml.SAML2ErrorResponseBuilder;
|
||||||
import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
|
import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
|
||||||
|
import org.keycloak.saml.common.util.XmlKeyInfoKeyNameTransformer;
|
||||||
import org.keycloak.testsuite.adapter.AbstractServletsAdapterTest;
|
import org.keycloak.testsuite.adapter.AbstractServletsAdapterTest;
|
||||||
import org.keycloak.testsuite.adapter.page.BadAssertionSalesPostSig;
|
import org.keycloak.testsuite.adapter.page.BadAssertionSalesPostSig;
|
||||||
import org.keycloak.testsuite.adapter.page.BadClientSalesPostSigServlet;
|
import org.keycloak.testsuite.adapter.page.BadClientSalesPostSigServlet;
|
||||||
|
@ -71,6 +74,7 @@ import org.keycloak.testsuite.auth.page.login.Login;
|
||||||
import org.keycloak.testsuite.auth.page.login.SAMLIDPInitiatedLogin;
|
import org.keycloak.testsuite.auth.page.login.SAMLIDPInitiatedLogin;
|
||||||
import org.keycloak.testsuite.page.AbstractPage;
|
import org.keycloak.testsuite.page.AbstractPage;
|
||||||
import org.keycloak.testsuite.util.IOUtil;
|
import org.keycloak.testsuite.util.IOUtil;
|
||||||
|
import org.keycloak.testsuite.util.RealmBuilder;
|
||||||
import org.keycloak.testsuite.util.UserBuilder;
|
import org.keycloak.testsuite.util.UserBuilder;
|
||||||
|
|
||||||
import org.openqa.selenium.By;
|
import org.openqa.selenium.By;
|
||||||
|
@ -439,10 +443,11 @@ public abstract class AbstractSAMLServletsAdapterTest extends AbstractServletsAd
|
||||||
testSuccessfulAndUnauthorizedLogin(employeeSigServletPage, testRealmSAMLRedirectLoginPage);
|
testSuccessfulAndUnauthorizedLogin(employeeSigServletPage, testRealmSAMLRedirectLoginPage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final KeyPair NEW_KEY_PAIR = KeyUtils.generateRsaKeyPair(1024);
|
||||||
|
private static final String NEW_KEY_PRIVATE_KEY_PEM = PemUtils.encodeKey(NEW_KEY_PAIR.getPrivate());
|
||||||
|
|
||||||
private PublicKey createKeys(String priority) throws Exception {
|
private PublicKey createKeys(String priority) throws Exception {
|
||||||
KeyPair keyPair = KeyUtils.generateRsaKeyPair(1024);
|
PublicKey publicKey = NEW_KEY_PAIR.getPublic();
|
||||||
String privateKeyPem = PemUtils.encodeKey(keyPair.getPrivate());
|
|
||||||
PublicKey publicKey = keyPair.getPublic();
|
|
||||||
|
|
||||||
ComponentRepresentation rep = new ComponentRepresentation();
|
ComponentRepresentation rep = new ComponentRepresentation();
|
||||||
rep.setName("mycomponent");
|
rep.setName("mycomponent");
|
||||||
|
@ -452,7 +457,7 @@ public abstract class AbstractSAMLServletsAdapterTest extends AbstractServletsAd
|
||||||
|
|
||||||
org.keycloak.common.util.MultivaluedHashMap config = new org.keycloak.common.util.MultivaluedHashMap();
|
org.keycloak.common.util.MultivaluedHashMap config = new org.keycloak.common.util.MultivaluedHashMap();
|
||||||
config.addFirst("priority", priority);
|
config.addFirst("priority", priority);
|
||||||
config.addFirst(Attributes.PRIVATE_KEY_KEY, privateKeyPem);
|
config.addFirst(Attributes.PRIVATE_KEY_KEY, NEW_KEY_PRIVATE_KEY_PEM);
|
||||||
rep.setConfig(config);
|
rep.setConfig(config);
|
||||||
|
|
||||||
testRealmResource().components().add(rep);
|
testRealmResource().components().add(rep);
|
||||||
|
@ -492,11 +497,53 @@ public abstract class AbstractSAMLServletsAdapterTest extends AbstractServletsAd
|
||||||
testRotatedKeysPropagated(employeeSigPostNoIdpKeyServletPage, testRealmSAMLPostLoginPage);
|
testRotatedKeysPropagated(employeeSigPostNoIdpKeyServletPage, testRealmSAMLPostLoginPage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void employeeSigPostNoIdpKeyTestNoKeyNameInKeyInfo() throws Exception {
|
||||||
|
RealmRepresentation r = testRealmResource().toRepresentation();
|
||||||
|
r.getAttributes().put(SamlConfigAttributes.SAML_SERVER_SIGNATURE_KEYINFO_KEY_NAME_TRANSFORMER, XmlKeyInfoKeyNameTransformer.NONE.name());
|
||||||
|
testRotatedKeysPropagated(employeeSigPostNoIdpKeyServletPage, testRealmSAMLPostLoginPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void employeeSigPostNoIdpKeyTestCertSubjectAsKeyNameInKeyInfo() throws Exception {
|
||||||
|
RealmRepresentation r = testRealmResource().toRepresentation();
|
||||||
|
r.getAttributes().put(SamlConfigAttributes.SAML_SERVER_SIGNATURE_KEYINFO_KEY_NAME_TRANSFORMER, XmlKeyInfoKeyNameTransformer.CERT_SUBJECT.name());
|
||||||
|
testRotatedKeysPropagated(employeeSigPostNoIdpKeyServletPage, testRealmSAMLPostLoginPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void employeeSigPostNoIdpKeyTestKeyIdAsKeyNameInKeyInfo() throws Exception {
|
||||||
|
RealmRepresentation r = testRealmResource().toRepresentation();
|
||||||
|
r.getAttributes().put(SamlConfigAttributes.SAML_SERVER_SIGNATURE_KEYINFO_KEY_NAME_TRANSFORMER, XmlKeyInfoKeyNameTransformer.KEY_ID.name());
|
||||||
|
testRotatedKeysPropagated(employeeSigPostNoIdpKeyServletPage, testRealmSAMLPostLoginPage);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void employeeSigRedirNoIdpKeyTest() throws Exception {
|
public void employeeSigRedirNoIdpKeyTest() throws Exception {
|
||||||
testRotatedKeysPropagated(employeeSigRedirNoIdpKeyServletPage, testRealmSAMLRedirectLoginPage);
|
testRotatedKeysPropagated(employeeSigRedirNoIdpKeyServletPage, testRealmSAMLRedirectLoginPage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void employeeSigRedirNoIdpKeyTestNoKeyNameInKeyInfo() throws Exception {
|
||||||
|
RealmRepresentation r = testRealmResource().toRepresentation();
|
||||||
|
r.getAttributes().put(SamlConfigAttributes.SAML_SERVER_SIGNATURE_KEYINFO_KEY_NAME_TRANSFORMER, XmlKeyInfoKeyNameTransformer.NONE.name());
|
||||||
|
testRotatedKeysPropagated(employeeSigRedirNoIdpKeyServletPage, testRealmSAMLRedirectLoginPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void employeeSigRedirNoIdpKeyTestCertSubjectAsKeyNameInKeyInfo() throws Exception {
|
||||||
|
RealmRepresentation r = testRealmResource().toRepresentation();
|
||||||
|
r.getAttributes().put(SamlConfigAttributes.SAML_SERVER_SIGNATURE_KEYINFO_KEY_NAME_TRANSFORMER, XmlKeyInfoKeyNameTransformer.CERT_SUBJECT.name());
|
||||||
|
testRotatedKeysPropagated(employeeSigRedirNoIdpKeyServletPage, testRealmSAMLRedirectLoginPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void employeeSigRedirNoIdpKeyTestKeyIdAsKeyNameInKeyInfo() throws Exception {
|
||||||
|
RealmRepresentation r = testRealmResource().toRepresentation();
|
||||||
|
r.getAttributes().put(SamlConfigAttributes.SAML_SERVER_SIGNATURE_KEYINFO_KEY_NAME_TRANSFORMER, XmlKeyInfoKeyNameTransformer.KEY_ID.name());
|
||||||
|
testRotatedKeysPropagated(employeeSigRedirNoIdpKeyServletPage, testRealmSAMLRedirectLoginPage);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void employeeSigRedirOptNoIdpKeyTest() throws Exception {
|
public void employeeSigRedirOptNoIdpKeyTest() throws Exception {
|
||||||
testRotatedKeysPropagated(employeeSigRedirOptNoIdpKeyServletPage, testRealmSAMLRedirectLoginPage);
|
testRotatedKeysPropagated(employeeSigRedirOptNoIdpKeyServletPage, testRealmSAMLRedirectLoginPage);
|
||||||
|
|
|
@ -272,6 +272,8 @@ logout-service-post-binding-url=Logout Service POST Binding URL
|
||||||
logout-service-post-binding-url.tooltip=SAML POST Binding URL for the client's single logout service. You can leave this blank if you are using a different binding
|
logout-service-post-binding-url.tooltip=SAML POST Binding URL for the client's single logout service. You can leave this blank if you are using a different binding
|
||||||
logout-service-redir-binding-url=Logout Service Redirect Binding URL
|
logout-service-redir-binding-url=Logout Service Redirect Binding URL
|
||||||
logout-service-redir-binding-url.tooltip=SAML Redirect Binding URL for the client's single logout service. You can leave this blank if you are using a different binding.
|
logout-service-redir-binding-url.tooltip=SAML Redirect Binding URL for the client's single logout service. You can leave this blank if you are using a different binding.
|
||||||
|
saml-signature-keyName-transformer=SAML Signature Key Name
|
||||||
|
saml-signature-keyName-transformer.tooltip=Signed SAML documents contain identification of signing key in KeyName element. For Keycloak / RH-SSO counterparty, use KEY_ID, for MS AD FS use CERT_SUBJECT, for others check and use NONE if no other option works.
|
||||||
|
|
||||||
# client import
|
# client import
|
||||||
import-client=Import Client
|
import-client=Import Client
|
||||||
|
|
|
@ -837,6 +837,11 @@ module.controller('ClientDetailCtrl', function($scope, realm, client, templates,
|
||||||
"transient",
|
"transient",
|
||||||
"persistent"
|
"persistent"
|
||||||
];
|
];
|
||||||
|
$scope.xmlKeyNameTranformers = [
|
||||||
|
"NONE",
|
||||||
|
"KEY_ID",
|
||||||
|
"CERT_SUBJECT"
|
||||||
|
];
|
||||||
|
|
||||||
$scope.canonicalization = [
|
$scope.canonicalization = [
|
||||||
{name: "EXCLUSIVE", value: "http://www.w3.org/2001/10/xml-exc-c14n#" },
|
{name: "EXCLUSIVE", value: "http://www.w3.org/2001/10/xml-exc-c14n#" },
|
||||||
|
@ -866,6 +871,7 @@ module.controller('ClientDetailCtrl', function($scope, realm, client, templates,
|
||||||
$scope.samlEncrypt = false;
|
$scope.samlEncrypt = false;
|
||||||
$scope.samlForcePostBinding = false;
|
$scope.samlForcePostBinding = false;
|
||||||
$scope.samlForceNameIdFormat = false;
|
$scope.samlForceNameIdFormat = false;
|
||||||
|
$scope.samlXmlKeyNameTranformer = $scope.xmlKeyNameTranformers[1];
|
||||||
$scope.disableAuthorizationTab = !client.authorizationServicesEnabled;
|
$scope.disableAuthorizationTab = !client.authorizationServicesEnabled;
|
||||||
$scope.disableServiceAccountRolesTab = !client.serviceAccountsEnabled;
|
$scope.disableServiceAccountRolesTab = !client.serviceAccountsEnabled;
|
||||||
$scope.disableCredentialsTab = client.publicClient;
|
$scope.disableCredentialsTab = client.publicClient;
|
||||||
|
@ -918,6 +924,13 @@ module.controller('ClientDetailCtrl', function($scope, realm, client, templates,
|
||||||
$scope.samlServerSignatureEnableKeyInfoExtension = false;
|
$scope.samlServerSignatureEnableKeyInfoExtension = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ($scope.client.attributes['saml.server.signature.keyinfo.xmlSigKeyInfoKeyNameTransformer'] === 'NONE') {
|
||||||
|
$scope.samlXmlKeyNameTranformer = $scope.xmlKeyNameTranformers[0];
|
||||||
|
} else if ($scope.client.attributes['saml.server.signature.keyinfo.xmlSigKeyInfoKeyNameTransformer'] === 'KEY_ID') {
|
||||||
|
$scope.samlXmlKeyNameTranformer = $scope.xmlKeyNameTranformers[1];
|
||||||
|
} else if ($scope.client.attributes['saml.server.signature.keyinfo.xmlSigKeyInfoKeyNameTransformer'] === 'CERT_SUBJECT') {
|
||||||
|
$scope.samlXmlKeyNameTranformer = $scope.xmlKeyNameTranformers[2];
|
||||||
|
}
|
||||||
if ($scope.client.attributes["saml.assertion.signature"]) {
|
if ($scope.client.attributes["saml.assertion.signature"]) {
|
||||||
if ($scope.client.attributes["saml.assertion.signature"] == "true") {
|
if ($scope.client.attributes["saml.assertion.signature"] == "true") {
|
||||||
$scope.samlAssertionSignature = true;
|
$scope.samlAssertionSignature = true;
|
||||||
|
@ -1037,6 +1050,10 @@ module.controller('ClientDetailCtrl', function($scope, realm, client, templates,
|
||||||
$scope.client.attributes['saml_name_id_format'] = $scope.nameIdFormat;
|
$scope.client.attributes['saml_name_id_format'] = $scope.nameIdFormat;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$scope.changeSamlSigKeyNameTranformer = function() {
|
||||||
|
$scope.client.attributes['saml.server.signature.keyinfo.xmlSigKeyInfoKeyNameTransformer'] = $scope.samlXmlKeyNameTranformer;
|
||||||
|
};
|
||||||
|
|
||||||
$scope.changeUserInfoSignedResponseAlg = function() {
|
$scope.changeUserInfoSignedResponseAlg = function() {
|
||||||
if ($scope.userInfoSignedResponseAlg === 'unsigned') {
|
if ($scope.userInfoSignedResponseAlg === 'unsigned') {
|
||||||
$scope.client.attributes['user.info.response.signature.alg'] = null;
|
$scope.client.attributes['user.info.response.signature.alg'] = null;
|
||||||
|
|
|
@ -764,11 +764,17 @@ module.controller('RealmIdentityProviderCtrl', function($scope, $filter, $upload
|
||||||
"RSA_SHA512",
|
"RSA_SHA512",
|
||||||
"DSA_SHA1"
|
"DSA_SHA1"
|
||||||
];
|
];
|
||||||
|
$scope.xmlKeyNameTranformers = [
|
||||||
|
"NONE",
|
||||||
|
"KEY_ID",
|
||||||
|
"CERT_SUBJECT"
|
||||||
|
];
|
||||||
if (instance && instance.alias) {
|
if (instance && instance.alias) {
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
$scope.identityProvider.config.nameIDPolicyFormat = $scope.nameIdFormats[0].format;
|
$scope.identityProvider.config.nameIDPolicyFormat = $scope.nameIdFormats[0].format;
|
||||||
$scope.identityProvider.config.signatureAlgorithm = $scope.signatureAlgorithms[1];
|
$scope.identityProvider.config.signatureAlgorithm = $scope.signatureAlgorithms[1];
|
||||||
|
$scope.identityProvider.config.samlXmlKeyNameTranformer = $scope.xmlKeyNameTranformers[1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -158,6 +158,19 @@
|
||||||
</div>
|
</div>
|
||||||
<kc-tooltip>{{:: 'signature-algorithm.tooltip' | translate}}</kc-tooltip>
|
<kc-tooltip>{{:: 'signature-algorithm.tooltip' | translate}}</kc-tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group clearfix block" data-ng-show="(samlAssertionSignature || samlServerSignature) && protocol == 'saml'">
|
||||||
|
<label class="col-md-2 control-label" for="samlSigKeyNameTranformer">{{:: 'saml-signature-keyName-transformer' | translate}}</label>
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<div>
|
||||||
|
<select class="form-control" id="xmlKeyNameTranformer"
|
||||||
|
ng-change="changeSamlSigKeyNameTranformer()"
|
||||||
|
ng-model="samlXmlKeyNameTranformer"
|
||||||
|
ng-options="alg for alg in xmlKeyNameTranformers">
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<kc-tooltip>{{:: 'saml-signature-keyName-transformer.tooltip' | translate}}</kc-tooltip>
|
||||||
|
</div>
|
||||||
<div class="form-group" data-ng-show="(samlAssertionSignature || samlServerSignature) && protocol == 'saml'">
|
<div class="form-group" data-ng-show="(samlAssertionSignature || samlServerSignature) && protocol == 'saml'">
|
||||||
<label class="col-md-2 control-label" for="canonicalization">{{:: 'canonicalization-method' | translate}}</label>
|
<label class="col-md-2 control-label" for="canonicalization">{{:: 'canonicalization-method' | translate}}</label>
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
|
|
|
@ -161,6 +161,18 @@
|
||||||
</div>
|
</div>
|
||||||
<kc-tooltip>{{:: 'signature-algorithm.tooltip' | translate}}</kc-tooltip>
|
<kc-tooltip>{{:: 'signature-algorithm.tooltip' | translate}}</kc-tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group clearfix block" data-ng-show="identityProvider.config.wantAuthnRequestsSigned == 'true'">
|
||||||
|
<label class="col-md-2 control-label" for="samlSigKeyNameTranformer">{{:: 'saml-signature-keyName-transformer' | translate}}</label>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div>
|
||||||
|
<select class="form-control" id="samlSigKeyNameTranformer"
|
||||||
|
ng-model="identityProvider.config.xmlSigKeyInfoKeyNameTransformer"
|
||||||
|
ng-options="xmlKeyNameTranformer for xmlKeyNameTranformer in xmlKeyNameTranformers">
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<kc-tooltip>{{:: 'saml-signature-keyName-transformer.tooltip' | translate}}</kc-tooltip>
|
||||||
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-md-2 control-label" for="forceAuthn">{{:: 'force-authentication' | translate}}</label>
|
<label class="col-md-2 control-label" for="forceAuthn">{{:: 'force-authentication' | translate}}</label>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
|
|
Loading…
Reference in a new issue