added PKCE editing capabilities to UI

pull/1192/merge
Justin Richer 2017-02-20 15:40:16 -05:00
parent 650429a2de
commit 141f4da7f1
9 changed files with 109 additions and 44 deletions

View File

@ -56,5 +56,6 @@ public interface RegisteredClientFields {
public String REDIRECT_URIS = "redirect_uris";
public String CLIENT_SECRET = "client_secret";
public String CLIENT_ID = "client_id";
public String CODE_CHALLENGE_METHOD = "code_challenge_method";
}

View File

@ -20,6 +20,36 @@
package org.mitre.openid.connect;
import static org.mitre.util.JsonUtils.getAsArray;
import static org.mitre.util.JsonUtils.getAsDate;
import static org.mitre.util.JsonUtils.getAsJweAlgorithm;
import static org.mitre.util.JsonUtils.getAsJweEncryptionMethod;
import static org.mitre.util.JsonUtils.getAsJwsAlgorithm;
import static org.mitre.util.JsonUtils.getAsPkceAlgorithm;
import static org.mitre.util.JsonUtils.getAsString;
import static org.mitre.util.JsonUtils.getAsStringSet;
import java.text.ParseException;
import org.mitre.oauth2.model.ClientDetailsEntity;
import org.mitre.oauth2.model.ClientDetailsEntity.AppType;
import org.mitre.oauth2.model.ClientDetailsEntity.AuthMethod;
import org.mitre.oauth2.model.ClientDetailsEntity.SubjectType;
import org.mitre.oauth2.model.RegisteredClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.Sets;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jwt.JWT;
import com.nimbusds.jwt.JWTParser;
import static org.mitre.oauth2.model.RegisteredClientFields.APPLICATION_TYPE;
import static org.mitre.oauth2.model.RegisteredClientFields.CLAIMS_REDIRECT_URIS;
import static org.mitre.oauth2.model.RegisteredClientFields.CLIENT_ID;
@ -28,6 +58,7 @@ import static org.mitre.oauth2.model.RegisteredClientFields.CLIENT_NAME;
import static org.mitre.oauth2.model.RegisteredClientFields.CLIENT_SECRET;
import static org.mitre.oauth2.model.RegisteredClientFields.CLIENT_SECRET_EXPIRES_AT;
import static org.mitre.oauth2.model.RegisteredClientFields.CLIENT_URI;
import static org.mitre.oauth2.model.RegisteredClientFields.CODE_CHALLENGE_METHOD;
import static org.mitre.oauth2.model.RegisteredClientFields.CONTACTS;
import static org.mitre.oauth2.model.RegisteredClientFields.DEFAULT_ACR_VALUES;
import static org.mitre.oauth2.model.RegisteredClientFields.DEFAULT_MAX_AGE;
@ -59,37 +90,6 @@ import static org.mitre.oauth2.model.RegisteredClientFields.TOS_URI;
import static org.mitre.oauth2.model.RegisteredClientFields.USERINFO_ENCRYPTED_RESPONSE_ALG;
import static org.mitre.oauth2.model.RegisteredClientFields.USERINFO_ENCRYPTED_RESPONSE_ENC;
import static org.mitre.oauth2.model.RegisteredClientFields.USERINFO_SIGNED_RESPONSE_ALG;
import static org.mitre.util.JsonUtils.getAsArray;
import static org.mitre.util.JsonUtils.getAsDate;
import static org.mitre.util.JsonUtils.getAsJweAlgorithm;
import static org.mitre.util.JsonUtils.getAsJweEncryptionMethod;
import static org.mitre.util.JsonUtils.getAsJwsAlgorithm;
import static org.mitre.util.JsonUtils.getAsString;
import static org.mitre.util.JsonUtils.getAsStringSet;
import java.text.ParseException;
import org.mitre.jwt.assertion.AssertionValidator;
import org.mitre.oauth2.model.ClientDetailsEntity;
import org.mitre.oauth2.model.ClientDetailsEntity.AppType;
import org.mitre.oauth2.model.ClientDetailsEntity.AuthMethod;
import org.mitre.oauth2.model.ClientDetailsEntity.SubjectType;
import org.mitre.oauth2.model.RegisteredClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.Sets;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jwt.JWT;
import com.nimbusds.jwt.JWTParser;
/**
* Utility class to handle the parsing and serialization of ClientDetails objects.
@ -204,6 +204,9 @@ public class ClientDetailsEntityJsonProcessor {
c.setClaimsRedirectUris(getAsStringSet(o, CLAIMS_REDIRECT_URIS));
c.setCodeChallengeMethod(getAsPkceAlgorithm(o, CODE_CHALLENGE_METHOD));
// note that this does not process or validate the software statement, that's handled in other components
String softwareStatement = getAsString(o, SOFTWARE_STATEMENT);
if (!Strings.isNullOrEmpty(softwareStatement)) {
try {
@ -216,6 +219,7 @@ public class ClientDetailsEntityJsonProcessor {
}
return c;
} else {
return null;
@ -339,6 +343,8 @@ public class ClientDetailsEntityJsonProcessor {
o.add(CLAIMS_REDIRECT_URIS, getAsArray(c.getClaimsRedirectUris()));
o.addProperty(CODE_CHALLENGE_METHOD, c.getCodeChallengeMethod() != null ? c.getCodeChallengeMethod().getName() : null);
if (c.getSoftwareStatement() != null) {
o.addProperty(SOFTWARE_STATEMENT, c.getSoftwareStatement().serialize());
}

View File

@ -28,6 +28,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import org.mitre.oauth2.model.PKCEAlgorithm;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -138,6 +139,21 @@ public class JsonUtils {
}
}
/**
* Gets the value of the given member as a PKCE Algorithm, null if it doesn't exist
* @param o
* @param member
* @return
*/
public static PKCEAlgorithm getAsPkceAlgorithm(JsonObject o, String member) {
String s = getAsString(o, member);
if (s != null) {
return PKCEAlgorithm.parse(s);
} else {
return null;
}
}
/**
* Gets the value of the given member as a string, null if it doesn't exist
*/

View File

@ -77,6 +77,8 @@ var ClientModel = Backbone.Model.extend({
requestUris:[],
codeChallengeMethod:null,
authorities:[],
accessTokenValiditySeconds: null,
refreshTokenValiditySeconds: null,
@ -957,7 +959,8 @@ var ClientFormView = Backbone.View.extend({
idTokenSignedResponseAlg: this.defaultToNull($('#idTokenSignedResponseAlg select').val()),
idTokenEncryptedResponseAlg: this.defaultToNull($('#idTokenEncryptedResponseAlg select').val()),
idTokenEncryptedResponseEnc: this.defaultToNull($('#idTokenEncryptedResponseEnc select').val()),
tokenEndpointAuthSigningAlg: this.defaultToNull($('#tokenEndpointAuthSigningAlg select').val())
tokenEndpointAuthSigningAlg: this.defaultToNull($('#tokenEndpointAuthSigningAlg select').val()),
codeChallengeMethod: this.defaultToNull($('#codeChallengeMethod select').val())
};
// post-validate
@ -1165,13 +1168,6 @@ var ClientFormView = Backbone.View.extend({
});
// add routes to the app
/*
"admin/clients":"listClients",
"admin/client/new":"newClient",
"admin/client/:id":"editClient",
*/
ui.routes.push({path: "admin/clients", name: "listClients", callback:
function () {

View File

@ -61,6 +61,8 @@ var DynRegClient = Backbone.Model.extend({
request_uris:[],
code_challenge_method:null,
registration_access_token:null,
registration_client_uri:null
},
@ -435,7 +437,8 @@ var DynRegEditView = Backbone.View.extend({
id_token_signed_response_alg: this.defaultToNull($('#idTokenSignedResponseAlg select').val()),
id_token_encrypted_response_alg: this.defaultToNull($('#idTokenEncryptedResponseAlg select').val()),
id_token_encrypted_response_enc: this.defaultToNull($('#idTokenEncryptedResponseEnc select').val()),
token_endpoint_auth_signing_alg: this.defaultToNull($('#tokenEndpointAuthSigningAlg select').val())
token_endpoint_auth_signing_alg: this.defaultToNull($('#tokenEndpointAuthSigningAlg select').val()),
code_challenge_method: this.defaultToNull($('#codeChallengeMethod select').val())
};
// set all empty strings to nulls

View File

@ -744,6 +744,17 @@
</div>
</div>
<div class="control-group" id="codeChallengeMethod">
<label class="control-label" data-i18n="client.client-form.code-challenge-method">Proof Key for Code Exchange (PKCE) Code Challenge Method</label>
<div class="controls">
<select>
<option value="default" <%-client.codeChallengeMethod == null ? 'selected ' : ''%> data-i18n="client.client-form.code-challenge-none">No code challenge</option>
<option value="plain" <%-client.codeChallengeMethod == "plain" ? 'selected' : ''%> data-i18n="client.client-form.code-challenge-plain">Plain code challenge</option>
<option value="S256" <%-client.codeChallengeMethod == "S256" ? 'selected' : ''%> data-i18n="client.client-form.code-challenge-s256">SHA-256 hash algorithm</option>
</select>
</div>
</div>
</div>
<div class="tab-pane" id="client-other-tab">

View File

@ -512,6 +512,17 @@
</select>
</div>
</div>
<div class="control-group" id="codeChallengeMethod">
<label class="control-label" data-i18n="client.client-form.code-challenge-method">Proof Key for Code Exchange (PKCE) Code Challenge Method</label>
<div class="controls">
<select>
<option value="default" <%-client.code_challenge_method == null ? 'selected ' : ''%> data-i18n="client.client-form.code-challenge-none">No code challenge</option>
<option value="plain" <%-client.code_challenge_method == "plain" ? 'selected' : ''%> data-i18n="client.client-form.code-challenge-plain">Plain code challenge</option>
<option value="S256" <%-client.code_challenge_method == "S256" ? 'selected' : ''%> data-i18n="client.client-form.code-challenge-s256">SHA-256 hash algorithm</option>
</select>
</div>
</div>
</div>
<div class="tab-pane" id="client-other-tab">

View File

@ -27,6 +27,7 @@ import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.mitre.oauth2.model.PKCEAlgorithm;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
@ -36,11 +37,15 @@ import org.springframework.web.servlet.view.AbstractView;
import com.google.gson.ExclusionStrategy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.nimbusds.jose.Algorithm;
import com.nimbusds.jose.EncryptionMethod;
import com.nimbusds.jose.JWEAlgorithm;
import com.nimbusds.jose.JWSAlgorithm;
@ -118,6 +123,15 @@ public abstract class AbstractClientEntityView extends AbstractView {
}
})
.registerTypeAdapter(PKCEAlgorithm.class, new JsonSerializer<PKCEAlgorithm>() {
public JsonPrimitive serialize(PKCEAlgorithm src, Type typeOfSrc, JsonSerializationContext context) {
if (src != null) {
return new JsonPrimitive(src.getName());
} else {
return null;
}
}
})
.serializeNulls()
.setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
.create();

View File

@ -29,6 +29,7 @@ import org.mitre.oauth2.model.ClientDetailsEntity;
import org.mitre.oauth2.model.ClientDetailsEntity.AppType;
import org.mitre.oauth2.model.ClientDetailsEntity.AuthMethod;
import org.mitre.oauth2.model.ClientDetailsEntity.SubjectType;
import org.mitre.oauth2.model.PKCEAlgorithm;
import org.mitre.oauth2.service.ClientDetailsEntityService;
import org.mitre.oauth2.web.AuthenticationUtilities;
import org.mitre.openid.connect.exception.ValidationException;
@ -68,9 +69,6 @@ import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.google.gson.JsonSyntaxException;
import com.nimbusds.jose.Algorithm;
import com.nimbusds.jose.EncryptionMethod;
@ -203,6 +201,15 @@ public class ClientAPI {
}
}
})
.registerTypeAdapter(PKCEAlgorithm.class, new JsonDeserializer<Algorithm>() {
public PKCEAlgorithm deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
if (json.isJsonPrimitive()) {
return PKCEAlgorithm.parse(json.getAsString());
} else {
return null;
}
}
})
.setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
.create();