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    public void setClientData(final String clientData) {
089        this.clientData = clientData;
090    }
091
092    public void startAuthentication(Application application) {
093        this.endpoint = idpUrl + "/rest/4/startauthentication/do";
094        JSONObject reqBody = new JSONObject();
095        reqBody.put("spAlias", application.getSpAlias());
096        reqBody.put("username", this.getUser().getUserName());
097        reqBody.put("deviceId", null);
098        reqBody.put("sessionId", null);
099        reqBody.put("clientData", null);
100
101        JSONObject formParameters = new JSONObject();
102        formParameters.put("sp_name", application.getName());
103        if (application.getLogoUrl() != null && !application.getLogoUrl().isEmpty()) {
104            formParameters.put("sp_logo", application.getLogoUrl());
105        }
106
107        reqBody.put("formParameters", formParameters);
108
109        this.requestToken = buildRequestToken(reqBody);
110
111        sendRequest();
112        JSONObject response = parseResponse();
113
114        if (this.wasSuccessful) {
115            values.clear();
116            this.lastSessionId = (String) response.get("sessionId");
117        }
118    }
119
120    public void AuthenticateOnline(Application application, String authType) {
121        this.endpoint = idpUrl + "/rest/4/authonline/do";
122        JSONObject reqBody = new JSONObject();
123        reqBody.put("authType", authType);
124        reqBody.put("spAlias", application.getSpAlias());
125        reqBody.put("userName", this.user.getUserName());
126        reqBody.put("clientData", this.clientData);
127
128        JSONObject formParameters = new JSONObject();
129        formParameters.put("sp_name", application.getName());
130        if (application.getLogoUrl() != null && !application.getLogoUrl().isEmpty()) {
131            formParameters.put("sp_logo", application.getLogoUrl());
132        }
133
134        reqBody.put("formParameters", formParameters);
135
136        this.requestToken = buildRequestToken(reqBody);
137
138        sendRequest();
139        JSONObject response = parseResponse();
140
141        if (this.wasSuccessful) {
142            values.clear();
143            this.lastSessionId = (String) response.get("sessionId");
144        }
145    }
146
147    public void AuthenticateOffline(String otp) {
148        this.endpoint = idpUrl + "/rest/4/authoffline/do";
149
150        JSONObject reqBody = new JSONObject();
151        reqBody.put("spAlias", "web");
152        reqBody.put("otp", otp);
153        reqBody.put("userName", this.user.getUserName());
154        reqBody.put("sessionId", this.lastSessionId);
155        reqBody.put("clientData", this.clientData);
156
157        this.requestToken = buildRequestToken(reqBody);
158
159        sendRequest();
160        parseResponse();
161        values.clear();
162    }
163
164    private String buildRequestToken(JSONObject requestBody) {
165        JSONObject requestHeader = buildRequestHeader();
166
167        JSONObject payload = new JSONObject();
168        payload.put("reqHeader", requestHeader);
169        payload.put("reqBody", requestBody);
170
171        JsonWebSignature jws = new JsonWebSignature();
172
173        jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.HMAC_SHA256);
174        jws.setHeader("org_alias", this.orgAlias);
175        jws.setHeader("token", this.token);
176
177        jws.setPayload(payload.toJSONString());
178
179        // Set the verification key
180        HmacKey key = new HmacKey(Base64.decode(this.useBase64Key));
181        jws.setKey(key);
182
183        String jwsCompactSerialization = null;
184        try {
185            jwsCompactSerialization = jws.getCompactSerialization();
186        } catch (JoseException e) {
187            e.printStackTrace();
188        }
189
190        this.requestToken = jwsCompactSerialization;
191
192        return jwsCompactSerialization;
193    }
194
195    private JSONObject buildRequestHeader() {
196        JSONObject reqHeader = new JSONObject();
197        reqHeader.put("locale", "en");
198        reqHeader.put("orgAlias", this.orgAlias);
199        reqHeader.put("secretKey", this.token);
200        reqHeader.put("timestamp", getCurrentTimeStamp());
201        reqHeader.put("version", this.apiVersion);
202
203        return reqHeader;
204    }
205
206    static String getCurrentTimeStamp() {
207        Date currentDate = new Date();
208        SimpleDateFormat PingIDDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
209        PingIDDateFormat.setTimeZone(TimeZone.getTimeZone("America/Denver"));
210
211        return PingIDDateFormat.format(currentDate);
212    }
213
214    private void sendRequest() {
215        try {
216            URL restUrl = new URL(this.getEndpoint());
217            HttpURLConnection urlConnection = (HttpURLConnection) restUrl.openConnection();
218            urlConnection.setRequestMethod("POST");
219            urlConnection.addRequestProperty("Content-Type", "application/json");
220            urlConnection.addRequestProperty("Accept", "*/*");
221
222            urlConnection.setDoOutput(true);
223            OutputStreamWriter outputStreamWriter = new OutputStreamWriter(urlConnection.getOutputStream(), "UTF-8");
224            outputStreamWriter.write(this.getRequestToken());
225            outputStreamWriter.flush();
226            outputStreamWriter.close();
227
228            int responseCode = urlConnection.getResponseCode();
229            this.responseCode = responseCode;
230
231            if (responseCode == 200) {
232
233                String encoding = urlConnection.getContentEncoding();
234                InputStream is = urlConnection.getInputStream();
235                String stringJWS = IOUtils.toString(is, encoding);
236                this.responseToken = stringJWS;
237                this.wasSuccessful = true;
238
239                urlConnection.disconnect();
240            } else {
241
242                String encoding = urlConnection.getContentEncoding();
243                InputStream is = urlConnection.getErrorStream();
244                String stringJWS = IOUtils.toString(is, encoding);
245                this.responseToken = stringJWS;
246                this.wasSuccessful = false;
247
248                urlConnection.disconnect();
249            }
250        } catch (Exception ex) {
251            this.responseCode = 500;
252            this.wasSuccessful = false;
253        }
254    }
255
256    private JSONObject parseResponse() {
257        JSONParser parser = new JSONParser();
258        JSONObject responsePayloadJSON = null;
259
260        try {
261            JsonWebSignature responseJWS = new JsonWebSignature();
262            responseJWS.setCompactSerialization(this.responseToken);
263            HmacKey key = new HmacKey(Base64.decode(this.useBase64Key));
264            responseJWS.setKey(key);
265            responsePayloadJSON = (JSONObject) parser.parse(responseJWS.getPayload());
266
267            // workaround for PingID API 4.5 beta
268            if (responsePayloadJSON.containsKey("responseBody")) {
269                responsePayloadJSON = (JSONObject) responsePayloadJSON.get("responseBody");
270            }
271
272        } catch (Exception e) {
273            e.printStackTrace();
274        }
275
276        if (responsePayloadJSON != null) {
277            this.errorId = (long) responsePayloadJSON.get("errorId");
278            this.errorMsg = (String) responsePayloadJSON.get("errorMsg");
279            this.uniqueMsgId = (String) responsePayloadJSON.get("uniqueMsgId");
280            this.clientData = (String) responsePayloadJSON.get("clientData");
281        } else {
282            this.errorId = 501;
283            this.errorMsg = "Could not parse JWS";
284            this.uniqueMsgId = "";
285            this.clientData = "";
286            this.wasSuccessful = false;
287        }
288
289        return responsePayloadJSON;
290    }
291}