001/*
002 * oauth2-oidc-sdk
003 *
004 * Copyright 2012-2016, Connect2id Ltd and contributors.
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use
007 * this file except in compliance with the License. You may obtain a copy of the
008 * License at
009 *
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software distributed
013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
015 * specific language governing permissions and limitations under the License.
016 */
017
018package com.nimbusds.openid.connect.sdk.claims;
019
020
021import java.net.URI;
022import java.util.Objects;
023import java.util.Set;
024import java.util.UUID;
025
026import com.nimbusds.oauth2.sdk.token.AccessToken;
027import net.minidev.json.JSONObject;
028
029
030/**
031 * Distributed OpenID claims set.
032 *
033 * <p>Example distributed claims with an access token (included in a UserInfo
034 * response):
035 *
036 * <pre>
037 * {
038 *   "_claim_names"   : { "credit_score" : "src1" },
039 *   "_claim_sources" : { "src1" : { "endpoint"     : "https://creditagency.example.com/claims_here",
040 *                                   "access_token" : "ksj3n283dke" } }
041 * }
042 * </pre>
043 *
044 * <p>Example distributed claims without a specified access token (included in
045 * a UserInfo response):
046 *
047 * <pre>
048 * {
049 *   "_claim_names" : { "payment_info"     : "src2",
050 *                      "shipping_address" : "src2" },
051 *   "_claim_sources" : { "src2" : { "endpoint" : "https://bank.example.com/claim_source" } }
052 * }
053 * </pre>
054 *
055 * <p>Related specifications:
056 *
057 * <ul>
058 *     <li>OpenID Connect Core 1.0
059 * </ul>
060 */
061public class DistributedClaims extends ExternalClaims {
062        
063        
064        /**
065         * The claims source endpoint.
066         */
067        private final URI sourceEndpoint;
068        
069        
070        /**
071         * Access token for retrieving the claims at the source URI,
072         * {@code null} if not specified.
073         */
074        private final AccessToken accessToken;
075        
076        
077        /**
078         * Creates a new aggregated OpenID claims instance, the claims source
079         * identifier is set to a GUUID string.
080         *
081         * @param names          The claim names. Must not be {@code null} or
082         *                       empty.
083         * @param sourceEndpoint The claims source endpoint. Must not be
084         *                       {@code null}.
085         * @param accessToken    Access token for retrieving the claims at the
086         *                       source endpoint, {@code null} if not
087         *                       specified.
088         */
089        public DistributedClaims(final Set<String> names, final URI sourceEndpoint, final AccessToken accessToken) {
090                
091                this(UUID.randomUUID().toString(), names, sourceEndpoint, accessToken);
092        }
093        
094        
095        /**
096         * Creates a new aggregated OpenID claims instance.
097         *
098         * @param sourceID       Identifier for the claims source. Must not be
099         *                       {@code null} or empty string.
100         * @param names          The claim names. Must not be {@code null} or
101         *                       empty.
102         * @param sourceEndpoint The claims source endpoint. Must not be
103         *                       {@code null}.
104         * @param accessToken    Access token for retrieving the claims at the
105         *                       source endpoint, {@code null} if not
106         *                       specified.
107         */
108        public DistributedClaims(final String sourceID, final Set<String> names, final URI sourceEndpoint, final AccessToken accessToken) {
109                
110                super(sourceID, names);
111                this.sourceEndpoint = Objects.requireNonNull(sourceEndpoint);
112                this.accessToken = accessToken;
113        }
114        
115        
116        /**
117         * Returns the claims source endpoint.
118         *
119         * @return The claims source endpoint.
120         */
121        public URI getSourceEndpoint() {
122                
123                return sourceEndpoint;
124        }
125        
126        
127        /**
128         * Returns the access token for retrieving the claims at the source
129         * endpoint.
130         *
131         * @return The access token for retrieving the claims at the source
132         *         endpoint, {@code null} if not specified.
133         */
134        public AccessToken getAccessToken() {
135                
136                return accessToken;
137        }
138        
139        
140        @Override
141        void mergeInto(final JSONObject jsonObject) {
142                
143                JSONObject claimNamesObject = new JSONObject();
144                
145                for (String name: getNames()) {
146                        claimNamesObject.put(name, getSourceID());
147                }
148                
149                if (jsonObject.containsKey("_claim_names")) {
150                        ((JSONObject) jsonObject.get("_claim_names")).putAll(claimNamesObject);
151                } else {
152                        jsonObject.put("_claim_names", claimNamesObject);
153                }
154                
155                JSONObject sourceSpec = new JSONObject();
156                sourceSpec.put("endpoint", getSourceEndpoint().toString());
157                if (getAccessToken() != null) {
158                        sourceSpec.put("access_token", getAccessToken().getValue());
159                }
160                JSONObject claimSourcesObject = new JSONObject();
161                claimSourcesObject.put(getSourceID(), sourceSpec);
162                
163                if (jsonObject.containsKey("_claim_sources")) {
164                        ((JSONObject) jsonObject.get("_claim_sources")).putAll(claimSourcesObject);
165                } else {
166                        jsonObject.put("_claim_sources", claimSourcesObject);
167                }
168        }
169}