001package com.pingidentity.developer.pingid;
002
003
004import org.apache.commons.io.IOUtils;
005import org.jose4j.base64url.Base64;
006import org.jose4j.json.internal.json_simple.JSONObject;
007import org.jose4j.json.internal.json_simple.parser.JSONParser;
008import org.jose4j.jws.AlgorithmIdentifiers;
009import org.jose4j.jws.JsonWebSignature;
010import org.jose4j.keys.HmacKey;
011import org.jose4j.lang.JoseException;
012
013import java.io.InputStream;
014import java.io.OutputStreamWriter;
015import java.net.HttpURLConnection;
016import java.net.URL;
017import java.text.SimpleDateFormat;
018import java.util.Date;
019import java.util.HashMap;
020import java.util.Map;
021import java.util.TimeZone;
022
023
024public class Operation {
025
026    private String endpoint;
027    private String requestToken;
028    private String responseToken;
029    private int responseCode;
030    private Boolean wasSuccessful;
031
032    private long errorId;
033    private String errorMsg;
034    private String uniqueMsgId;
035
036    private String idpUrl;
037    private String orgAlias;
038    private String token;
039    private String useBase64Key;
040
041    private String lastSessionId;
042
043    private Map<String, Object> values;
044
045    private String clientData;
046    private User user;
047
048    private final String apiVersion = "4.6";
049
050
051    public Operation(String orgAlias, String token, String useBase64Key, String pingidUrl) {
052        this.orgAlias = orgAlias;
053        this.token = token;
054        this.useBase64Key = useBase64Key;
055        this.idpUrl = pingidUrl;
056
057        this.values = new HashMap<>();
058    }
059
060    public Operation(Operation operation) {
061        this(operation.orgAlias, operation.token, operation.useBase64Key, operation.idpUrl);
062    }
063
064    public String getEndpoint() {
065        return endpoint;
066    }
067
068    public String getRequestToken() {
069        return requestToken;
070    }
071
072    public Boolean getWasSuccessful() {
073        return wasSuccessful;
074    }
075
076    public String getErrorMsg() {
077        return responseCode + " - " + errorId + " - " + errorMsg;
078    }
079
080    public User getUser() {
081        return user;
082    }
083
084    public void setTargetUser(String username) {
085        this.user = new User(username);
086    }
087
088
089    public void startAuthentication(Application application) {
090        this.endpoint = idpUrl + "/rest/4/startauthentication/do";
091        JSONObject reqBody = new JSONObject();
092        reqBody.put("spAlias", application.getSpAlias());
093        reqBody.put("username", this.getUser().getUserName());
094        reqBody.put("deviceId", null);
095        reqBody.put("sessionId", null);
096        reqBody.put("clientData", null);
097
098        JSONObject formParameters = new JSONObject();
099        formParameters.put("sp_name", application.getName());
100        if (application.getLogoUrl() != null && !application.getLogoUrl().isEmpty()) {
101            formParameters.put("sp_logo", application.getLogoUrl());
102        }
103
104        reqBody.put("formParameters", formParameters);
105
106        this.requestToken = buildRequestToken(reqBody);
107
108        sendRequest();
109        JSONObject response = parseResponse();
110
111        if (this.wasSuccessful) {
112            values.clear();
113            this.lastSessionId = (String) response.get("sessionId");
114        }
115    }
116
117    public void AuthenticateOnline(Application application, String authType) {
118        this.endpoint = idpUrl + "/rest/4/authonline/do";
119        JSONObject reqBody = new JSONObject();
120        reqBody.put("authType", authType);
121        reqBody.put("spAlias", application.getSpAlias());
122        reqBody.put("userName", this.user.getUserName());
123        reqBody.put("clientData", this.clientData);
124
125        JSONObject formParameters = new JSONObject();
126        formParameters.put("sp_name", application.getName());
127        if (application.getLogoUrl() != null && !application.getLogoUrl().isEmpty()) {
128            formParameters.put("sp_logo", application.getLogoUrl());
129        }
130
131        reqBody.put("formParameters", formParameters);
132
133        this.requestToken = buildRequestToken(reqBody);
134
135        sendRequest();
136        JSONObject response = parseResponse();
137
138        if (this.wasSuccessful) {
139            values.clear();
140            this.lastSessionId = (String) response.get("sessionId");
141        }
142    }
143
144    public void AuthenticateOffline(String otp) {
145        this.endpoint = idpUrl + "/rest/4/authoffline/do";
146
147        JSONObject reqBody = new JSONObject();
148        reqBody.put("spAlias", "web");
149        reqBody.put("otp", otp);
150        reqBody.put("userName", this.user.getUserName());
151        reqBody.put("sessionId", this.lastSessionId);
152        reqBody.put("clientData", this.clientData);
153
154        this.requestToken = buildRequestToken(reqBody);
155
156        sendRequest();
157        parseResponse();
158        values.clear();
159    }
160
161    private String buildRequestToken(JSONObject requestBody) {
162        JSONObject requestHeader = buildRequestHeader();
163
164        JSONObject payload = new JSONObject();
165        payload.put("reqHeader", requestHeader);
166        payload.put("reqBody", requestBody);
167
168        JsonWebSignature jws = new JsonWebSignature();
169
170        jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.HMAC_SHA256);
171        jws.setHeader("org_alias", this.orgAlias);
172        jws.setHeader("token", this.token);
173
174        jws.setPayload(payload.toJSONString());
175
176        // Set the verification key
177        HmacKey key = new HmacKey(Base64.decode(this.useBase64Key));
178        jws.setKey(key);
179
180        String jwsCompactSerialization = null;
181        try {
182            jwsCompactSerialization = jws.getCompactSerialization();
183        } catch (JoseException e) {
184            e.printStackTrace();
185        }
186
187        this.requestToken = jwsCompactSerialization;
188
189        return jwsCompactSerialization;
190    }
191
192    private JSONObject buildRequestHeader() {
193        JSONObject reqHeader = new JSONObject();
194        reqHeader.put("locale", "en");
195        reqHeader.put("orgAlias", this.orgAlias);
196        reqHeader.put("secretKey", this.token);
197        reqHeader.put("timestamp", getCurrentTimeStamp());
198        reqHeader.put("version", this.apiVersion);
199
200        return reqHeader;
201    }
202
203    static String getCurrentTimeStamp() {
204        Date currentDate = new Date();
205        SimpleDateFormat PingIDDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
206        PingIDDateFormat.setTimeZone(TimeZone.getTimeZone("America/Denver"));
207
208        return PingIDDateFormat.format(currentDate);
209    }
210
211    private void sendRequest() {
212        try {
213            URL restUrl = new URL(this.getEndpoint());
214            HttpURLConnection urlConnection = (HttpURLConnection) restUrl.openConnection();
215            urlConnection.setRequestMethod("POST");
216            urlConnection.addRequestProperty("Content-Type", "application/json");
217            urlConnection.addRequestProperty("Accept", "*/*");
218
219            urlConnection.setDoOutput(true);
220            OutputStreamWriter outputStreamWriter = new OutputStreamWriter(urlConnection.getOutputStream(), "UTF-8");
221            outputStreamWriter.write(this.getRequestToken());
222            outputStreamWriter.flush();
223            outputStreamWriter.close();
224
225            int responseCode = urlConnection.getResponseCode();
226            this.responseCode = responseCode;
227
228            if (responseCode == 200) {
229
230                String encoding = urlConnection.getContentEncoding();
231                InputStream is = urlConnection.getInputStream();
232                String stringJWS = IOUtils.toString(is, encoding);
233                this.responseToken = stringJWS;
234                this.wasSuccessful = true;
235
236                urlConnection.disconnect();
237            } else {
238
239                String encoding = urlConnection.getContentEncoding();
240                InputStream is = urlConnection.getErrorStream();
241                String stringJWS = IOUtils.toString(is, encoding);
242                this.responseToken = stringJWS;
243                this.wasSuccessful = false;
244
245                urlConnection.disconnect();
246            }
247        } catch (Exception ex) {
248            this.responseCode = 500;
249            this.wasSuccessful = false;
250        }
251    }
252
253    private JSONObject parseResponse() {
254        JSONParser parser = new JSONParser();
255        JSONObject responsePayloadJSON = null;
256
257        try {
258            JsonWebSignature responseJWS = new JsonWebSignature();
259            responseJWS.setCompactSerialization(this.responseToken);
260            HmacKey key = new HmacKey(Base64.decode(this.useBase64Key));
261            responseJWS.setKey(key);
262            responsePayloadJSON = (JSONObject) parser.parse(responseJWS.getPayload());
263
264            // workaround for PingID API 4.5 beta
265            if (responsePayloadJSON.containsKey("responseBody")) {
266                responsePayloadJSON = (JSONObject) responsePayloadJSON.get("responseBody");
267            }
268
269        } catch (Exception e) {
270            e.printStackTrace();
271        }
272
273        if (responsePayloadJSON != null) {
274            this.errorId = (long) responsePayloadJSON.get("errorId");
275            this.errorMsg = (String) responsePayloadJSON.get("errorMsg");
276            this.uniqueMsgId = (String) responsePayloadJSON.get("uniqueMsgId");
277            this.clientData = (String) responsePayloadJSON.get("clientData");
278        } else {
279            this.errorId = 501;
280            this.errorMsg = "Could not parse JWS";
281            this.uniqueMsgId = "";
282            this.clientData = "";
283            this.wasSuccessful = false;
284        }
285
286        return responsePayloadJSON;
287    }
288}