11/*
2- * Copyright (C) 2014 TopCoder Inc., All Rights Reserved.
2+ * Copyright (C) 2014 - 2017 TopCoder Inc., All Rights Reserved.
33 */
44package com .topcoder .direct .services .view .action ;
55
66import com .topcoder .direct .services .configs .ServerConfiguration ;
77import com .topcoder .direct .services .view .dto .contest .ContestStatus ;
8+ import com .topcoder .direct .services .view .dto .my .SingleRestResult ;
9+ import com .topcoder .direct .services .view .dto .my .Token ;
810import com .topcoder .direct .services .view .dto .project .ProjectBriefDTO ;
11+ import com .topcoder .direct .services .view .exception .JwtAuthenticationException ;
912import com .topcoder .direct .services .view .util .DataProvider ;
1013import com .topcoder .direct .services .view .util .DirectUtils ;
1114import com .topcoder .security .TCSubject ;
15+ import org .apache .commons .codec .binary .Base64 ;
1216import org .apache .http .HttpEntity ;
1317import org .apache .http .HttpHeaders ;
1418import org .apache .http .HttpResponse ;
1519import org .apache .http .HttpStatus ;
1620import org .apache .http .client .methods .HttpGet ;
21+ import org .apache .http .client .methods .HttpPost ;
1722import org .apache .http .client .utils .URIBuilder ;
23+ import org .apache .http .entity .StringEntity ;
1824import org .apache .http .impl .client .DefaultHttpClient ;
1925import org .apache .log4j .Logger ;
2026import org .apache .struts2 .ServletActionContext ;
2733import java .net .URI ;
2834import java .net .URISyntaxException ;
2935import java .net .URLEncoder ;
36+ import java .nio .charset .StandardCharsets ;
3037import java .text .SimpleDateFormat ;
31- import java .util .ArrayList ;
32- import java .util .LinkedHashMap ;
33- import java .util .List ;
34- import java .util .Map ;
38+ import java .util .*;
3539
3640/**
3741 * <p>
@@ -104,6 +108,11 @@ public abstract class ServiceBackendDataTablesAction extends AbstractAction {
104108 */
105109 private String serviceURL ;
106110
111+ /**
112+ * authorization Url
113+ */
114+ private String authorizationURL ;
115+
107116 /**
108117 * The challenge types options in filter panel
109118 *
@@ -120,7 +129,6 @@ public abstract class ServiceBackendDataTablesAction extends AbstractAction {
120129
121130 /**
122131 * The customer options in filter panel.
123- *
124132 * @since 1.1
125133 */
126134 private Map <Long , String > customers ;
@@ -188,6 +196,11 @@ public abstract class ServiceBackendDataTablesAction extends AbstractAction {
188196 */
189197 private String endDateTo ;
190198
199+ /**
200+ * ssoLogin Url
201+ */
202+ private String ssoLoginUrl ;
203+
191204 /**
192205 * The max pagination size.
193206 */
@@ -213,6 +226,11 @@ public abstract class ServiceBackendDataTablesAction extends AbstractAction {
213226 */
214227 protected static final String ERROR_MESSAGE_FORMAT = "Service URL:%s, HTTP Status Code:%d, Error Message:%s" ;
215228
229+ /**
230+ * URI params for refresh token
231+ */
232+ private final String AUTHORIZATION_PARAMS = "{\" param\" : {\" externalToken\" : \" %s\" }}" ;
233+
216234 /**
217235 * The jackson object mapping which is used to deserialize json return from API to domain model.
218236 */
@@ -321,23 +339,26 @@ protected JsonNode getJsonResultFromAPI(URI apiEndPoint) throws Exception {
321339 // specify the get request
322340 HttpGet getRequest = new HttpGet (apiEndPoint );
323341
324- Cookie jwtCookie = DirectUtils .getCookieFromRequest (ServletActionContext .getRequest (),
342+ Cookie jwtCookieV3 = DirectUtils .getCookieFromRequest (ServletActionContext .getRequest (),
325343 ServerConfiguration .JWT_V3_COOKIE_KEY );
344+ Cookie jwtCookieV2 = DirectUtils .getCookieFromRequest (ServletActionContext .getRequest (),
345+ ServerConfiguration .JWT_COOOKIE_KEY );
326346
327- if (jwtCookie == null ) {
328- throw new Exception ( "The jwt cookie for the authorized user could not be loaded " );
347+ if (jwtCookieV2 == null ) {
348+ throw new JwtAuthenticationException ( "Please re-login " );
329349 }
330350
351+ validateCookieV2V3 (jwtCookieV2 ,jwtCookieV3 );
352+
331353 getRequest .setHeader (HttpHeaders .AUTHORIZATION ,
332- "Bearer " + jwtCookie .getValue ());
354+ "Bearer " + jwtCookieV3 .getValue ());
333355
334356 getRequest .addHeader (HttpHeaders .ACCEPT , "application/json" );
335357
336358 HttpResponse httpResponse = httpClient .execute (getRequest );
337359 HttpEntity entity = httpResponse .getEntity ();
338360
339361 if (httpResponse .getStatusLine ().getStatusCode () != HttpStatus .SC_OK ) {
340-
341362 throw new Exception (String .format (ERROR_MESSAGE_FORMAT ,
342363 getRequest .getURI (),
343364 httpResponse .getStatusLine ().getStatusCode (),
@@ -706,4 +727,144 @@ public String getEndDateTo() {
706727 public void setEndDateTo (String endDateTo ) {
707728 this .endDateTo = endDateTo ;
708729 }
730+
731+ /**
732+ * Getter for {@link #authorizationURL}
733+ * @return authorizationURL
734+ */
735+ public String getAuthorizationURL () {
736+ return authorizationURL ;
737+ }
738+
739+ /**
740+ * Setter for {@link #authorizationURL}
741+ * @param authorizationURL
742+ */
743+ public void setAuthorizationURL (String authorizationURL ) {
744+ this .authorizationURL = authorizationURL ;
745+ }
746+
747+ /**
748+ * Get Full SSO login url
749+ * @return
750+ */
751+ public String getSsoLoginUrl () {
752+ try {
753+ URIBuilder builder = new URIBuilder (ssoLoginUrl );
754+ builder .addParameter ("next" , ServletActionContext .getRequest ().getRequestURL ().toString ());
755+ return builder .build ().toString ();
756+ } catch (Exception e ) {
757+ return ssoLoginUrl ;
758+ }
759+ }
760+
761+ /**
762+ * Setter {@link #ssoLoginUrl}
763+ *
764+ * @param ssoLoginUrl
765+ */
766+ public void setSsoLoginUrl (String ssoLoginUrl ) {
767+ this .ssoLoginUrl = ssoLoginUrl ;
768+ }
769+
770+ /**
771+ * Refresh token from API endpoint
772+ *
773+ * @param oldToken
774+ * @return
775+ * @throws Exception
776+ */
777+ private Token getRefreshTokenFromApi (String oldToken ) throws Exception {
778+ DefaultHttpClient httpClient = new DefaultHttpClient ();
779+ SingleRestResult <Token > resultToken = null ;
780+ try {
781+ URI authorizationUri = new URI (getAuthorizationURL ());
782+ HttpPost httpPost = new HttpPost (authorizationUri );
783+ httpPost .addHeader (HttpHeaders .CONTENT_TYPE , "application/json" );
784+
785+ StringEntity body = new StringEntity (String .format (AUTHORIZATION_PARAMS , oldToken ));
786+ httpPost .setEntity (body );
787+ HttpResponse response = httpClient .execute (httpPost );
788+ HttpEntity entity = response .getEntity ();
789+ if (response .getStatusLine ().getStatusCode () != HttpStatus .SC_OK ) {
790+ throw new JwtAuthenticationException (String .format (ERROR_MESSAGE_FORMAT , authorizationUri ,
791+ response .getStatusLine ().getStatusCode (),
792+ getErrorMessage (response .getStatusLine ().getStatusCode ())));
793+ }
794+
795+ JsonNode result = objectMapper .readTree (entity .getContent ());
796+ resultToken = objectMapper .readValue (result .get ("result" ),
797+ objectMapper .getTypeFactory ().constructParametricType (SingleRestResult .class , Token .class ));
798+ } finally {
799+ httpClient .getConnectionManager ().shutdown ();
800+ }
801+ return resultToken .getContent ();
802+ }
803+
804+ /**
805+ * Verify token.If token expired: refresh it
806+ *
807+ * @param tokenV3
808+ * @param tokenV2
809+ * @return
810+ * @throws JwtAuthenticationException
811+ */
812+ private String getValidJwtToken (String tokenV3 , String tokenV2 ) throws JwtAuthenticationException {
813+ String [] tokenSplit = tokenV3 .split ("\\ ." );
814+ boolean valid = true ;
815+ if (tokenSplit .length < 2 ) valid = false ;
816+
817+ JsonNode jsonNode = null ;
818+
819+ try {
820+ if (valid ) {
821+ StringBuffer payloadStr = new StringBuffer (tokenSplit [1 ]);
822+ while (payloadStr .length () % 4 != 0 ) payloadStr .append ('=' );
823+ String payload = new String (Base64 .decodeBase64 (payloadStr .toString ().getBytes (StandardCharsets .UTF_8 )));
824+
825+ jsonNode = objectMapper .readValue (payload .toString (), JsonNode .class );
826+
827+ long exp = jsonNode .get ("exp" ).getLongValue ();
828+ Date expDate = new Date (exp * 1000 );
829+ logger .info ("token expire: " + expDate );
830+ if (expDate .before (new Date ())) valid = false ;
831+ }
832+
833+ if (!valid ) {
834+ logger .info ("refresh new token for : " + tokenV2 );
835+ Token newToken = getRefreshTokenFromApi (tokenV2 );
836+ if (newToken == null || newToken .getToken ().isEmpty ()) {
837+ throw new JwtAuthenticationException ("Invalid refresh token" );
838+ }
839+
840+ return newToken .getToken ();
841+ }
842+ } catch (Exception e ) {
843+ throw new JwtAuthenticationException ("Failed to refresh toke through api, Please go to sso login page : " +
844+ getSsoLoginUrl ());
845+ }
846+ return tokenV3 ;
847+ }
848+
849+ /**
850+ * Validate cookie v2 and v3
851+ *
852+ * @param v2 cookie v2
853+ * @param v3 cookie v3
854+ * @throws Exception
855+ */
856+ protected void validateCookieV2V3 (Cookie v2 , Cookie v3 ) throws Exception {
857+ String validToken = null ;
858+ String v3Token = null ;
859+ if (v3 == null ) {
860+ validToken = getRefreshTokenFromApi (v2 .getValue ()).getToken ();
861+ } else {
862+ validToken = getValidJwtToken (v3 .getValue (), v2 .getValue ());
863+ v3Token = v3 .getValue ();
864+ }
865+
866+ if (!validToken .equals (v3Token )) {
867+ DirectUtils .addDirectCookie (ServletActionContext .getResponse (), ServerConfiguration .JWT_V3_COOKIE_KEY , validToken , -1 );
868+ }
869+ }
709870}
0 commit comments