c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden/*
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden * The contents of this file are subject to the terms of the Common Development and
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden * Distribution License (the License). You may not use this file except in compliance with the
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden * License.
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden *
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden * specific language governing permission and limitations under the License.
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden *
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden * When distributing Covered Software, include this CDDL Header Notice in each file and include
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden * Header, with the fields enclosed by brackets [] replaced by your own identifying
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden * information: "Portions copyright [year] [name of copyright owner]".
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden *
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden * Copyright 2014 ForgeRock AS.
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden */
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Maddenpackage com.sun.identity.entitlement;
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Maddenimport com.sun.identity.shared.debug.Debug;
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Maddenimport org.apache.commons.lang.StringUtils;
965c9c7794176a6815b197f53aa146a29e181b99Neil Maddenimport org.json.JSONException;
965c9c7794176a6815b197f53aa146a29e181b99Neil Maddenimport org.json.JSONObject;
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Maddenimport javax.security.auth.Subject;
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Maddenimport java.util.Collections;
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Maddenimport java.util.Map;
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Maddenimport java.util.Set;
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden/**
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden * A policy subject condition that examines claims in a Json Web Token (JWT) subject, such as an OpenID Connect
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden * ID token. Currently only supports testing claims for string equality.
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden */
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Maddenpublic class JwtClaimSubject implements EntitlementSubject {
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden private static final Debug DEBUG = Debug.getInstance("amEntitlements");
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden private static final String CLAIM_FIELD = "claimName";
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden private static final String VALUE_FIELD = "claimValue";
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden private static final Map<String, Set<String>> NO_ADVICE = Collections.emptyMap();
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden private String claimName;
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden private String claimValue;
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden @Override
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden public void setState(final String state) {
965c9c7794176a6815b197f53aa146a29e181b99Neil Madden try {
965c9c7794176a6815b197f53aa146a29e181b99Neil Madden final JSONObject json = new JSONObject(state);
965c9c7794176a6815b197f53aa146a29e181b99Neil Madden this.claimName = json.getString(CLAIM_FIELD);
965c9c7794176a6815b197f53aa146a29e181b99Neil Madden this.claimValue = json.getString(VALUE_FIELD);
965c9c7794176a6815b197f53aa146a29e181b99Neil Madden } catch (JSONException e) {
965c9c7794176a6815b197f53aa146a29e181b99Neil Madden throw new RuntimeException(e);
965c9c7794176a6815b197f53aa146a29e181b99Neil Madden }
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden }
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden @Override
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden public String getState() {
965c9c7794176a6815b197f53aa146a29e181b99Neil Madden try {
965c9c7794176a6815b197f53aa146a29e181b99Neil Madden final JSONObject json = new JSONObject();
965c9c7794176a6815b197f53aa146a29e181b99Neil Madden json.put(CLAIM_FIELD, claimName);
965c9c7794176a6815b197f53aa146a29e181b99Neil Madden json.put(VALUE_FIELD, claimValue);
965c9c7794176a6815b197f53aa146a29e181b99Neil Madden return json.toString(2);
965c9c7794176a6815b197f53aa146a29e181b99Neil Madden } catch (JSONException e) {
965c9c7794176a6815b197f53aa146a29e181b99Neil Madden throw new RuntimeException(e);
965c9c7794176a6815b197f53aa146a29e181b99Neil Madden }
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden }
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden @Override
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden public Map<String, Set<String>> getSearchIndexAttributes() {
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden return Collections.singletonMap(SubjectAttributesCollector.NAMESPACE_IDENTITY,
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden Collections.singleton(SubjectAttributesCollector.ATTR_NAME_ALL_ENTITIES));
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden }
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden @Override
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden public Set<String> getRequiredAttributeNames() {
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden return Collections.emptySet();
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden }
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden @Override
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden public SubjectDecision evaluate(final String realm, final SubjectAttributesManager mgr, final Subject subject,
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden final String resourceName, final Map<String, Set<String>> environment)
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden throws EntitlementException {
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden final Set<JwtPrincipal> jwts = subject.getPrincipals(JwtPrincipal.class);
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden if (jwts.isEmpty()) {
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden DEBUG.message("No JWT principal in subject");
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden return new SubjectDecision(false, NO_ADVICE);
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden }
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden final JwtPrincipal jwt = jwts.iterator().next();
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden final String value = jwt.getClaim(claimName);
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden final boolean match = StringUtils.equals(claimValue, value);
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden return new SubjectDecision(match, NO_ADVICE);
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden }
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden @Override
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden public boolean isIdentity() {
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden return true;
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden }
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden public void setClaimName(String claim) {
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden this.claimName = claim;
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden }
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden public String getClaimName() {
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden return claimName;
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden }
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden public void setClaimValue(String value) {
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden this.claimValue = value;
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden }
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden public String getClaimValue() {
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden return claimValue;
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden }
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden @Override
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden public boolean equals(final Object o) {
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden if (this == o) {
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden return true;
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden }
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden if (o == null || getClass() != o.getClass()) {
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden return false;
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden }
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden final JwtClaimSubject that = (JwtClaimSubject) o;
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden return StringUtils.equals(this.claimName, that.claimName)
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden && StringUtils.equals(this.claimValue, that.claimValue);
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden }
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden @Override
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden public int hashCode() {
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden int result = claimName != null ? claimName.hashCode() : 0;
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden result = 31 * result + (claimValue != null ? claimValue.hashCode() : 0);
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden return result;
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden }
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden @Override
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden public String toString() {
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden return "JwtClaimSubject{ claimName='" + claimName + "', claimValue='" + claimValue + "' }";
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden }
c8ab19d28fde5eda3b2daab4b1124887681fedf9Neil Madden}