diff --git a/build.xml b/build.xml index 9a939132b..1b2f378e4 100644 --- a/build.xml +++ b/build.xml @@ -573,7 +573,7 @@ - + diff --git a/conf/web/WEB-INF/applicationContext.xml b/conf/web/WEB-INF/applicationContext.xml index b3f75c15c..276f0a30b 100644 --- a/conf/web/WEB-INF/applicationContext.xml +++ b/conf/web/WEB-INF/applicationContext.xml @@ -212,6 +212,7 @@ + @@ -340,7 +341,6 @@ - - - - - - - - diff --git a/conf/web/WEB-INF/struts.xml b/conf/web/WEB-INF/struts.xml index 30a4b7839..cd9ded66c 100644 --- a/conf/web/WEB-INF/struts.xml +++ b/conf/web/WEB-INF/struts.xml @@ -1548,7 +1548,6 @@ /WEB-INF/my/myCreatedChallenges.jsp - ${ssoLoginUrl} @@ -1556,7 +1555,6 @@ /WEB-INF/my/myChallenges.jsp - ${ssoLoginUrl} diff --git a/src/java/main/com/topcoder/direct/services/configs/ServerConfiguration.java b/src/java/main/com/topcoder/direct/services/configs/ServerConfiguration.java index 7029d99bd..b57312455 100644 --- a/src/java/main/com/topcoder/direct/services/configs/ServerConfiguration.java +++ b/src/java/main/com/topcoder/direct/services/configs/ServerConfiguration.java @@ -55,7 +55,5 @@ public class ServerConfiguration extends ApplicationServer { * * @since 1.1 */ - public static String JWT_COOOKIE_KEY = bundle.getProperty("JWT_COOKIE_KEY", "tcjwt"); - - public static String JWT_V3_COOKIE_KEY = bundle.getProperty("JWT_V3_COOKIE_KEY", "v3jwt"); + public static String JWT_COOKIE_KEY = bundle.getProperty("JWT_COOKIE_KEY", "tcjwt"); } diff --git a/src/java/main/com/topcoder/direct/services/view/action/ServiceBackendDataTablesAction.java b/src/java/main/com/topcoder/direct/services/view/action/ServiceBackendDataTablesAction.java index 4806d94c5..362e21de9 100644 --- a/src/java/main/com/topcoder/direct/services/view/action/ServiceBackendDataTablesAction.java +++ b/src/java/main/com/topcoder/direct/services/view/action/ServiceBackendDataTablesAction.java @@ -3,11 +3,11 @@ */ package com.topcoder.direct.services.view.action; +import com.topcoder.direct.services.configs.ServerConfiguration; import com.topcoder.direct.services.view.dto.contest.ContestStatus; import com.topcoder.direct.services.view.dto.project.ProjectBriefDTO; import com.topcoder.direct.services.view.util.DataProvider; import com.topcoder.direct.services.view.util.DirectUtils; -import com.topcoder.direct.services.view.util.JwtTokenUpdater; import com.topcoder.security.TCSubject; import org.apache.http.HttpEntity; import org.apache.http.HttpHeaders; @@ -17,6 +17,7 @@ import org.apache.http.client.utils.URIBuilder; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.log4j.Logger; +import org.apache.struts2.ServletActionContext; import org.codehaus.jackson.JsonNode; import org.codehaus.jackson.map.DeserializationConfig; import org.codehaus.jackson.map.ObjectMapper; @@ -215,11 +216,6 @@ public abstract class ServiceBackendDataTablesAction extends AbstractAction { */ protected static final ObjectMapper objectMapper; - /** - * JwtTokenUpdater - */ - private JwtTokenUpdater jwtTokenUpdater; - /** *

A static Map mapping the existing contest statuses to their textual presentations.

* @@ -323,7 +319,8 @@ protected JsonNode getJsonResultFromAPI(URI apiEndPoint) throws Exception { // specify the get request HttpGet getRequest = new HttpGet(apiEndPoint); - String token = jwtTokenUpdater.getV3Token(); + String token = DirectUtils.getCookieFromRequest(ServletActionContext.getRequest(), + ServerConfiguration.JWT_COOKIE_KEY).getValue(); getRequest.setHeader(HttpHeaders.AUTHORIZATION, "Bearer " + token); @@ -702,12 +699,4 @@ public String getEndDateTo() { public void setEndDateTo(String endDateTo) { this.endDateTo = endDateTo; } - - public JwtTokenUpdater getJwtTokenUpdater() { - return jwtTokenUpdater; - } - - public void setJwtTokenUpdater(JwtTokenUpdater jwtTokenUpdater) { - this.jwtTokenUpdater = jwtTokenUpdater; - } } diff --git a/src/java/main/com/topcoder/direct/services/view/action/contest/launch/CommonAction.java b/src/java/main/com/topcoder/direct/services/view/action/contest/launch/CommonAction.java index bd03ada39..c5c2360fc 100644 --- a/src/java/main/com/topcoder/direct/services/view/action/contest/launch/CommonAction.java +++ b/src/java/main/com/topcoder/direct/services/view/action/contest/launch/CommonAction.java @@ -20,7 +20,6 @@ import com.topcoder.direct.services.view.util.AuthorizationProvider; import com.topcoder.direct.services.view.util.DataProvider; import com.topcoder.direct.services.view.util.DirectUtils; -import com.topcoder.direct.services.view.util.JwtTokenUpdater; import com.topcoder.direct.services.view.util.challenge.CostCalculationService; import com.topcoder.management.project.ProjectGroup; import com.topcoder.security.TCSubject; @@ -129,7 +128,6 @@ public class CommonAction extends BaseContestFeeAction { */ private String userGroupsApiEndpoint; - private JwtTokenUpdater jwtTokenUpdater; /** *

@@ -561,7 +559,7 @@ public void setCategoryId(long categoryId) { public String getGroups() { try { TCSubject tcSubject = DirectUtils.getTCSubjectFromSession(); - Set projectGroups = DirectUtils.getGroups(tcSubject, jwtTokenUpdater, userGroupsApiEndpoint); + Set projectGroups = DirectUtils.getGroups(tcSubject, userGroupsApiEndpoint); setResult(projectGroups); } catch (Throwable e) { if (getModel() != null) { @@ -578,12 +576,4 @@ public String getUserGroupsApiEndpoint() { public void setUserGroupsApiEndpoint(String userGroupsApiEndpoint) { this.userGroupsApiEndpoint = userGroupsApiEndpoint; } - - public JwtTokenUpdater getJwtTokenUpdater() { - return jwtTokenUpdater; - } - - public void setJwtTokenUpdater(JwtTokenUpdater jwtTokenUpdater) { - this.jwtTokenUpdater = jwtTokenUpdater; - } } diff --git a/src/java/main/com/topcoder/direct/services/view/action/contest/launch/GetGroupMemberAction.java b/src/java/main/com/topcoder/direct/services/view/action/contest/launch/GetGroupMemberAction.java index a62b0470c..57c02ecc3 100644 --- a/src/java/main/com/topcoder/direct/services/view/action/contest/launch/GetGroupMemberAction.java +++ b/src/java/main/com/topcoder/direct/services/view/action/contest/launch/GetGroupMemberAction.java @@ -6,9 +6,7 @@ import com.topcoder.direct.services.configs.ServerConfiguration; import com.topcoder.direct.services.view.dto.contest.GroupMember; import com.topcoder.direct.services.view.dto.my.RestResult; -import com.topcoder.direct.services.view.exception.JwtAuthenticationException; import com.topcoder.direct.services.view.util.DirectUtils; -import com.topcoder.direct.services.view.util.JwtTokenUpdater; import com.topcoder.direct.services.view.util.SortedCacheAddress; import com.topcoder.web.common.cache.CacheClient; import com.topcoder.web.common.cache.CacheClientFactory; @@ -27,9 +25,13 @@ import org.codehaus.jackson.map.DeserializationConfig; import org.codehaus.jackson.map.ObjectMapper; -import javax.servlet.http.Cookie; import java.net.URI; -import java.util.*; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Set; /** * This action handle group member search for given list of groups and substringof user's handle @@ -62,11 +64,6 @@ class GetGroupMemberAction extends ContestAction { */ private String handle; - /** - * JwtTokenUpdater - */ - private JwtTokenUpdater jwtTokenUpdater; - /** * Json object mapper */ @@ -188,13 +185,8 @@ private RestResult getGroupMemberByGid(Long gid) throws Exception { try{ URI groupApiEndpointUri = new URI(String.format(groupApiEndpoint, gid)); HttpGet request = new HttpGet(groupApiEndpointUri); - String jwtToken; - try{ - jwtToken = jwtTokenUpdater.getV3Token(); - } catch (Exception e) { - logger.error("Can't get jwt token"); - throw e; - } + String jwtToken = DirectUtils.getCookieFromRequest(ServletActionContext.getRequest(), + ServerConfiguration.JWT_COOKIE_KEY).getValue(); request.setHeader(HttpHeaders.AUTHORIZATION, "Bearer " + jwtToken); request.addHeader(HttpHeaders.ACCEPT, "application/json"); @@ -239,12 +231,4 @@ public String getHandle() { public void setHandle(String handle) { this.handle = handle; } - - public JwtTokenUpdater getJwtTokenUpdater() { - return jwtTokenUpdater; - } - - public void setJwtTokenUpdater(JwtTokenUpdater jwtTokenUpdater) { - this.jwtTokenUpdater = jwtTokenUpdater; - } } diff --git a/src/java/main/com/topcoder/direct/services/view/action/my/MyChallengesAction.java b/src/java/main/com/topcoder/direct/services/view/action/my/MyChallengesAction.java index a8f35544d..4585311c5 100644 --- a/src/java/main/com/topcoder/direct/services/view/action/my/MyChallengesAction.java +++ b/src/java/main/com/topcoder/direct/services/view/action/my/MyChallengesAction.java @@ -3,19 +3,20 @@ */ package com.topcoder.direct.services.view.action.my; -import com.topcoder.direct.services.configs.ServerConfiguration; import com.topcoder.direct.services.view.action.ServiceBackendDataTablesAction; import com.topcoder.direct.services.view.dto.my.Challenge; import com.topcoder.direct.services.view.dto.my.RestResult; -import com.topcoder.direct.services.view.util.DirectUtils; import org.codehaus.jackson.JsonNode; -import org.apache.struts2.ServletActionContext; - import java.text.DateFormat; import java.text.NumberFormat; import java.text.SimpleDateFormat; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.TimeZone; /** *

@@ -49,10 +50,6 @@ public class MyChallengesAction extends ServiceBackendDataTablesAction { */ @Override public String execute() throws Exception { - if (DirectUtils.getCookieFromRequest(ServletActionContext.getRequest(), - ServerConfiguration.JWT_COOOKIE_KEY) == null) - return "forward"; - // populate filter data this.setupFilterPanel(); diff --git a/src/java/main/com/topcoder/direct/services/view/action/my/MyCreatedChallengesAction.java b/src/java/main/com/topcoder/direct/services/view/action/my/MyCreatedChallengesAction.java index ecc0fea93..b0155acb9 100644 --- a/src/java/main/com/topcoder/direct/services/view/action/my/MyCreatedChallengesAction.java +++ b/src/java/main/com/topcoder/direct/services/view/action/my/MyCreatedChallengesAction.java @@ -2,14 +2,11 @@ * Copyright (C) 2014 TopCoder Inc., All Rights Reserved. */ package com.topcoder.direct.services.view.action.my; - -import com.topcoder.direct.services.configs.ServerConfiguration; import com.topcoder.direct.services.view.action.ServiceBackendDataTablesAction; import com.topcoder.direct.services.view.dto.my.Challenge; import com.topcoder.direct.services.view.dto.my.RestResult; import com.topcoder.direct.services.view.util.DirectUtils; import com.topcoder.service.user.UserService; -import org.apache.struts2.ServletActionContext; import org.codehaus.jackson.JsonNode; import java.text.DateFormat; @@ -60,10 +57,6 @@ public class MyCreatedChallengesAction extends ServiceBackendDataTablesAction { */ @Override public String execute() throws Exception { - if (DirectUtils.getCookieFromRequest(ServletActionContext.getRequest(), - ServerConfiguration.JWT_COOOKIE_KEY) == null) - return "forward"; - // populate filter data this.setupFilterPanel(); diff --git a/src/java/main/com/topcoder/direct/services/view/exception/JwtAuthenticationException.java b/src/java/main/com/topcoder/direct/services/view/exception/JwtAuthenticationException.java deleted file mode 100644 index 4d06154af..000000000 --- a/src/java/main/com/topcoder/direct/services/view/exception/JwtAuthenticationException.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.topcoder.direct.services.view.exception; - -import com.topcoder.util.errorhandling.BaseException; - -public class JwtAuthenticationException extends BaseException { - public JwtAuthenticationException(String message) { - super(message); - } - - public JwtAuthenticationException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/src/java/main/com/topcoder/direct/services/view/interceptors/AuthenticationInterceptor.java b/src/java/main/com/topcoder/direct/services/view/interceptors/AuthenticationInterceptor.java index 8851aafca..76ec4ef56 100644 --- a/src/java/main/com/topcoder/direct/services/view/interceptors/AuthenticationInterceptor.java +++ b/src/java/main/com/topcoder/direct/services/view/interceptors/AuthenticationInterceptor.java @@ -5,7 +5,6 @@ package com.topcoder.direct.services.view.interceptors; -import java.util.Arrays; import java.util.Set; import javax.servlet.http.Cookie; @@ -24,10 +23,8 @@ import com.topcoder.direct.services.view.util.SessionData; import com.topcoder.direct.services.view.util.DirectUtils; import com.topcoder.direct.services.view.util.DirectProperties; -import com.topcoder.security.RolePrincipal; import com.topcoder.security.TCPrincipal; import com.topcoder.security.TCSubject; -import com.topcoder.shared.security.SimpleResource; import com.topcoder.shared.security.User; import com.topcoder.shared.util.DBMS; import com.topcoder.shared.util.logging.Logger; @@ -224,6 +221,11 @@ public class AuthenticationInterceptor extends AbstractInterceptor { */ private String redirectBackUrlIdentityKey; + /** + * Endpoint from token updater + */ + private String authorizationURL; + /** * Default constructor, constructs an instance of this class. */ @@ -284,7 +286,7 @@ public String intercept(ActionInvocation invocation) throws Exception { User user = auth.getActiveUser(); Cookie jwtCookie = DirectUtils.getCookieFromRequest(ServletActionContext.getRequest(), - ServerConfiguration.JWT_COOOKIE_KEY); + ServerConfiguration.JWT_COOKIE_KEY); if (jwtCookie == null) { return loginPageName; @@ -293,12 +295,17 @@ public String intercept(ActionInvocation invocation) throws Exception { JWTToken jwtToken = null; try { jwtToken = new JWTToken(jwtCookie.getValue(),DirectProperties.CLIENT_SECRET_AUTH0, - DirectProperties.JWT_VALID_ISSUERS, new JWTToken.Base64SecretEncoder()); + DirectProperties.JWT_VALID_ISSUERS, authorizationURL, new JWTToken.Base64SecretEncoder()); + jwtToken.verify(); } catch (TokenExpiredException e) { - //refresh token here - //redirect to loginpage for now - logger.error("Token is expired. Should do refresh token here"); - return loginPageName; + logger.error("Token is expired. Try to refresh"); + try { + jwtToken = jwtToken.refresh(); + } catch (Exception ex) { + logger.error("Failed to refresh token: " + ex.getMessage()); + logger.info("Redirect to login page"); + return loginPageName; + } } catch (Exception e) { return loginPageName; } @@ -437,4 +444,12 @@ public void setRedirectBackUrlIdentityKey(String redirectBackUrlIdentityKey) { Helper.checkNotNullOrEmpty(redirectBackUrlIdentityKey, "redirectBackUrlIdentityKey"); this.redirectBackUrlIdentityKey = redirectBackUrlIdentityKey; } + + public String getAuthorizationURL() { + return authorizationURL; + } + + public void setAuthorizationURL(String authorizationURL) { + this.authorizationURL = authorizationURL; + } } diff --git a/src/java/main/com/topcoder/direct/services/view/processor/security/LoginProcessor.java b/src/java/main/com/topcoder/direct/services/view/processor/security/LoginProcessor.java index a0953df7a..1d65ee27c 100644 --- a/src/java/main/com/topcoder/direct/services/view/processor/security/LoginProcessor.java +++ b/src/java/main/com/topcoder/direct/services/view/processor/security/LoginProcessor.java @@ -3,7 +3,6 @@ */ package com.topcoder.direct.services.view.processor.security; -import com.auth0.jwt.algorithms.Algorithm; import com.topcoder.direct.services.configs.ServerConfiguration; import com.topcoder.direct.services.view.action.LoginAction; import com.topcoder.direct.services.view.form.LoginForm; @@ -11,7 +10,6 @@ import com.topcoder.direct.services.view.util.DirectProperties; import com.topcoder.direct.services.view.util.DirectUtils; import com.topcoder.direct.services.view.util.jwt.DirectJWTSigner; -import com.topcoder.direct.services.view.util.jwt.JWTToken; import com.topcoder.security.TCSubject; import com.topcoder.security.login.AuthenticationException; import com.topcoder.security.login.LoginRemote; @@ -133,7 +131,7 @@ public void processRequest(LoginAction action) { // add session cookie, use -1 for expiration time log.info("Signed JWT: " + sign); DirectUtils.addDirectCookie(ServletActionContext.getResponse(), - ServerConfiguration.JWT_COOOKIE_KEY, sign, -1); + ServerConfiguration.JWT_COOKIE_KEY, sign, -1); } catch (AuthenticationException e) { log.error("User " + username + " failed to authenticate successfully due to invalid credentials", e); diff --git a/src/java/main/com/topcoder/direct/services/view/processor/security/MockLoginProcessor.java b/src/java/main/com/topcoder/direct/services/view/processor/security/MockLoginProcessor.java index f06b45803..2cafbe783 100644 --- a/src/java/main/com/topcoder/direct/services/view/processor/security/MockLoginProcessor.java +++ b/src/java/main/com/topcoder/direct/services/view/processor/security/MockLoginProcessor.java @@ -3,7 +3,6 @@ */ package com.topcoder.direct.services.view.processor.security; -import com.auth0.jwt.algorithms.Algorithm; import com.topcoder.direct.services.configs.ServerConfiguration; import com.topcoder.direct.services.view.action.LoginAction; import com.topcoder.direct.services.view.form.LoginForm; @@ -11,7 +10,6 @@ import com.topcoder.direct.services.view.util.DirectProperties; import com.topcoder.direct.services.view.util.DirectUtils; import com.topcoder.direct.services.view.util.jwt.DirectJWTSigner; -import com.topcoder.direct.services.view.util.jwt.JWTToken; import com.topcoder.security.RolePrincipal; import com.topcoder.security.TCPrincipal; import com.topcoder.security.TCSubject; @@ -213,7 +211,7 @@ public void processRequest(LoginAction action) { log.info("SIgned JWT: " + sign); // add session cookie, use -1 for expiration time DirectUtils.addDirectCookie(ServletActionContext.getResponse(), - ServerConfiguration.JWT_COOOKIE_KEY, sign, -1); + ServerConfiguration.JWT_COOKIE_KEY, sign, -1); } catch (Exception e) { log.error("User " + username + " could not set cookie", e); log.error(e.getMessage() + e.getCause()); diff --git a/src/java/main/com/topcoder/direct/services/view/util/DirectUtils.java b/src/java/main/com/topcoder/direct/services/view/util/DirectUtils.java index 1b96f0952..5d578a3fd 100644 --- a/src/java/main/com/topcoder/direct/services/view/util/DirectUtils.java +++ b/src/java/main/com/topcoder/direct/services/view/util/DirectUtils.java @@ -18,7 +18,16 @@ import com.topcoder.direct.services.view.action.BaseDirectStrutsAction; import com.topcoder.direct.services.view.action.specreview.ViewSpecificationReviewActionResultData; import com.topcoder.direct.services.view.dto.IdNamePair; -import com.topcoder.direct.services.view.dto.contest.*; +import com.topcoder.direct.services.view.dto.contest.BaseContestCommonDTO; +import com.topcoder.direct.services.view.dto.contest.ContestBriefDTO; +import com.topcoder.direct.services.view.dto.contest.ContestDashboardDTO; +import com.topcoder.direct.services.view.dto.contest.ContestRoundType; +import com.topcoder.direct.services.view.dto.contest.ContestStatsDTO; +import com.topcoder.direct.services.view.dto.contest.ContestStatus; +import com.topcoder.direct.services.view.dto.contest.PhasedContestDTO; +import com.topcoder.direct.services.view.dto.contest.ProjectPhaseDTO; +import com.topcoder.direct.services.view.dto.contest.ProjectPhaseType; +import com.topcoder.direct.services.view.dto.contest.TermOfUse; import com.topcoder.direct.services.view.dto.cost.CostDTO; import com.topcoder.direct.services.view.dto.project.ProjectBriefDTO; import com.topcoder.direct.services.view.interceptor.SecurityGroupsAccessInterceptor; @@ -26,7 +35,13 @@ import com.topcoder.management.deliverable.Submission; import com.topcoder.management.deliverable.Upload; import com.topcoder.management.deliverable.persistence.UploadPersistenceException; -import com.topcoder.management.project.*; +import com.topcoder.management.project.CopilotContestExtraInfo; +import com.topcoder.management.project.CopilotContestExtraInfoType; +import com.topcoder.management.project.Prize; +import com.topcoder.management.project.ProjectCopilotType; +import com.topcoder.management.project.ProjectGroup; +import com.topcoder.management.project.ProjectPropertyType; +import com.topcoder.management.project.ProjectType; import com.topcoder.management.resource.Resource; import com.topcoder.management.resource.ResourceRole; import com.topcoder.management.review.data.Comment; @@ -95,7 +110,13 @@ import javax.xml.datatype.DatatypeConfigurationException; import javax.xml.datatype.DatatypeFactory; import javax.xml.datatype.XMLGregorianCalendar; -import java.io.*; +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; import java.nio.channels.FileLock; import java.sql.Connection; import java.sql.PreparedStatement; @@ -103,7 +124,23 @@ import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TimeZone; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; @@ -3770,12 +3807,11 @@ public static List getGroupIdAndName(List projectGro * Get group from group API. * * @param tcSubject tcSubject of user - * @param jwtTokenUpdater the jwt token updater * @param endpoint endpoint url * @return set of group * @throws Exception */ - public static Set getGroupsFromApi(TCSubject tcSubject, JwtTokenUpdater jwtTokenUpdater, String endpoint) throws Exception { + public static Set getGroupsFromApi(TCSubject tcSubject, String endpoint) throws Exception { URIBuilder uri = new URIBuilder(endpoint); if (!DirectUtils.isCockpitAdmin(tcSubject) && !DirectUtils.isTcStaff(tcSubject)) { @@ -3787,7 +3823,7 @@ public static Set getGroupsFromApi(TCSubject tcSubject, JwtTokenUp HttpGet getRequest = new HttpGet(uri.build()); logger.info("Getting Group with thi uri: " + uri.build().toString()); - String v3Token = jwtTokenUpdater.getV3Token(); + String v3Token = getCookieFromRequest(getServletRequest(), ServerConfiguration.JWT_COOKIE_KEY).getValue(); getRequest.setHeader(HttpHeaders.AUTHORIZATION, "Bearer " + v3Token); @@ -3814,12 +3850,11 @@ public static Set getGroupsFromApi(TCSubject tcSubject, JwtTokenUp * Get groups. Get from cache first if none then get from api * * @param tcSubject tcSubject of user - * @param jwtTokenUpdater the jwt token updater * @param endpoint endpoint url * @return set of groupfor user * @throws Exception */ - public static Set getGroups(TCSubject tcSubject, JwtTokenUpdater jwtTokenUpdater, String endpoint) throws Exception { + public static Set getGroups(TCSubject tcSubject, String endpoint) throws Exception { CacheClient cc = null; Set projectGroups = null; SortedCacheAddress cacheAddress = new SortedCacheAddress(tcSubject.getUserId()); @@ -3830,7 +3865,7 @@ public static Set getGroups(TCSubject tcSubject, JwtTokenUpdater j logger.info("Can't get group for user " + tcSubject.getUserId() + " from cache"); } if (projectGroups == null) { - projectGroups = DirectUtils.getGroupsFromApi(tcSubject, jwtTokenUpdater, endpoint); + projectGroups = DirectUtils.getGroupsFromApi(tcSubject, endpoint); try { cc.set(cacheAddress, projectGroups, MaxAge.HOUR); } catch (Exception e) { diff --git a/src/java/main/com/topcoder/direct/services/view/util/JwtTokenUpdater.java b/src/java/main/com/topcoder/direct/services/view/util/JwtTokenUpdater.java deleted file mode 100644 index 7e6edd4cb..000000000 --- a/src/java/main/com/topcoder/direct/services/view/util/JwtTokenUpdater.java +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright (C) 2017 TopCoder Inc., All Rights Reserved. - */ -package com.topcoder.direct.services.view.util; - -import com.topcoder.direct.services.configs.ServerConfiguration; -import com.topcoder.direct.services.view.exception.JwtAuthenticationException; -import org.apache.commons.codec.binary.Base64; -import org.apache.http.HttpEntity; -import org.apache.http.HttpHeaders; -import org.apache.http.HttpResponse; -import org.apache.http.HttpStatus; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.DefaultHttpClient; -import org.apache.log4j.Logger; -import org.apache.struts2.ServletActionContext; -import org.codehaus.jackson.JsonNode; -import org.codehaus.jackson.map.DeserializationConfig; -import org.codehaus.jackson.map.ObjectMapper; - -import javax.servlet.http.Cookie; -import java.net.URI; -import java.nio.charset.StandardCharsets; -import java.text.SimpleDateFormat; -import java.util.Date; - -import static sun.security.krb5.internal.Krb5.getErrorMessage; - -/** - * Utility class to check and update token from cookie - * - */ -public class JwtTokenUpdater { - - private static final Logger logger = Logger.getLogger(JwtTokenUpdater.class); - - /** - * authorizationUrl - */ - private String authorizationURL; - - /** - * ssoLogin Url - */ - private String ssoLoginUrl; - - protected static final String ERROR_MESSAGE_FORMAT = "Service URL:%s, HTTP Status Code:%d, Error Message:%s"; - - private static final String AUTHORIZATION_PARAMS = "{\"param\": {\"externalToken\": \"%s\"}}"; - - protected static final ObjectMapper objectMapper; - - static { - objectMapper = new ObjectMapper(); - objectMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false); - objectMapper.setDateFormat(new SimpleDateFormat("MM/dd/yyyy HH:mm")); - } - - public JwtTokenUpdater() { - - } - - /** - * Validate and get v3 token from cookies - * - * @return v3 token - * @throws Exception - */ - public String getV3Token() throws Exception { - Cookie jwtCookieV3 = DirectUtils.getCookieFromRequest(ServletActionContext.getRequest(), - ServerConfiguration.JWT_V3_COOKIE_KEY); - Cookie jwtCookieV2 = DirectUtils.getCookieFromRequest(ServletActionContext.getRequest(), - ServerConfiguration.JWT_COOOKIE_KEY); - - if (jwtCookieV2 == null) { - throw new JwtAuthenticationException("Please re-login"); - } - - return validateCookieV2V3(jwtCookieV2,jwtCookieV3); - } - - - private String getRefreshTokenFromApi(String oldToken) throws Exception { - DefaultHttpClient httpClient = new DefaultHttpClient(); - try { - URI authorizationUri = new URI(this.authorizationURL); - HttpPost httpPost = new HttpPost(authorizationUri); - httpPost.addHeader(HttpHeaders.CONTENT_TYPE, "application/json"); - - StringEntity body = new StringEntity(String.format(AUTHORIZATION_PARAMS, oldToken)); - httpPost.setEntity(body); - HttpResponse response = httpClient.execute(httpPost); - HttpEntity entity = response.getEntity(); - if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) { - throw new JwtAuthenticationException(String.format(ERROR_MESSAGE_FORMAT, authorizationUri, - response.getStatusLine().getStatusCode(), - getErrorMessage(response.getStatusLine().getStatusCode()))); - } - - JsonNode result = objectMapper.readTree(entity.getContent()); - - return result.path("result").path("content").path("token").asText(); - } finally { - httpClient.getConnectionManager().shutdown(); - } - } - - /** - * Verify token. If token expired: refresh it - * - * @param v3token the v3 jwt token - * @param v2token the v2 jwt token - * @return - * @throws JwtAuthenticationException - */ - private String getValidJwtToken(String v3token, String v2token) throws JwtAuthenticationException { - String[] tokenSplit = v3token.split("\\."); - boolean valid = tokenSplit.length >= 2; - - try { - if (valid) { - StringBuilder payloadStr = new StringBuilder(tokenSplit[1]); - while (payloadStr.length() % 4 != 0) payloadStr.append('='); - String payload = new String(Base64.decodeBase64(payloadStr.toString().getBytes(StandardCharsets.UTF_8))); - - JsonNode jsonNode = objectMapper.readValue(payload, JsonNode.class); - - long exp = jsonNode.get("exp").getLongValue(); - Date expDate = new Date(exp * 1000); - logger.info("token expire at: " + expDate); - if (expDate.after(new Date())) { - return v3token; - } - } - - logger.info("refresh v3 token for : " + v2token); - String newToken = getRefreshTokenFromApi(v2token); - if (newToken == null || newToken.isEmpty()) { - throw new JwtAuthenticationException("Invalid refreshed token - " + newToken); - } - - return newToken; - } catch (JwtAuthenticationException e) { - throw e; - } catch (Exception e) { - throw new JwtAuthenticationException("Failed to refresh toke through api, Please go to sso login page : " + - this.ssoLoginUrl, e); - } - } - - /** - * Validate cookie v2 and v3 - * - * @param v2 cookie v2 - * @param v3 cookie v3 - * @return valid v3 token - * @throws Exception - */ - private String validateCookieV2V3(Cookie v2, Cookie v3) throws Exception { - String validToken; - String v3Token = null; - if (v3 == null) { - validToken = getRefreshTokenFromApi(v2.getValue()); - } else { - validToken = getValidJwtToken(v3.getValue(), v2.getValue()); - v3Token = v3.getValue(); - } - - if (!validToken.equals(v3Token)) { - DirectUtils.addDirectCookie(ServletActionContext.getResponse(), ServerConfiguration.JWT_V3_COOKIE_KEY, validToken, -1); - } - - return validToken; - } - - - public String getAuthorizationURL() { - return authorizationURL; - } - - public void setAuthorizationURL(String authorizationURL) { - this.authorizationURL = authorizationURL; - } - - public String getSsoLoginUrl() { - return ssoLoginUrl; - } - - public void setSsoLoginUrl(String ssoLoginUrl) { - this.ssoLoginUrl = ssoLoginUrl; - } -} diff --git a/src/java/main/com/topcoder/direct/services/view/util/jwt/JWTEndpointException.java b/src/java/main/com/topcoder/direct/services/view/util/jwt/JWTEndpointException.java new file mode 100644 index 000000000..c50b346b1 --- /dev/null +++ b/src/java/main/com/topcoder/direct/services/view/util/jwt/JWTEndpointException.java @@ -0,0 +1,11 @@ +package com.topcoder.direct.services.view.util.jwt; + +public class JWTEndpointException extends JWTException { + public JWTEndpointException(String message) { + super(message); + } + + public JWTEndpointException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/java/main/com/topcoder/direct/services/view/util/jwt/JWTToken.java b/src/java/main/com/topcoder/direct/services/view/util/jwt/JWTToken.java index d97a647b6..99a964236 100644 --- a/src/java/main/com/topcoder/direct/services/view/util/jwt/JWTToken.java +++ b/src/java/main/com/topcoder/direct/services/view/util/jwt/JWTToken.java @@ -3,13 +3,25 @@ */ package com.topcoder.direct.services.view.util.jwt; +import java.net.URI; import java.security.interfaces.RSAPublicKey; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.List; import java.util.Map.Entry; import org.apache.commons.codec.binary.Base64; +import org.apache.http.HttpEntity; +import org.apache.http.HttpHeaders; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.DefaultHttpClient; import org.apache.log4j.Logger; +import org.codehaus.jackson.JsonNode; +import org.codehaus.jackson.map.DeserializationConfig; +import org.codehaus.jackson.map.ObjectMapper; import com.auth0.jwk.GuavaCachedJwkProvider; import com.auth0.jwk.Jwk; @@ -24,6 +36,8 @@ import com.auth0.jwt.interfaces.DecodedJWT; import com.auth0.jwt.interfaces.Verification; +import static sun.security.krb5.internal.Krb5.getErrorMessage; + /** * Jwt token. Main purpose is to verify token. * Verification based on token signature, issuers, and time validation @@ -69,6 +83,25 @@ public class JWTToken { protected SecretEncoder encoder = new Base64SecretEncoder(); + private boolean valid = false; + + /** + * authorizationUrl + */ + private String authorizationURL; + + protected static final String ERROR_MESSAGE_FORMAT = "Service URL:%s, HTTP Status Code:%d, Error Message:%s"; + + private static final String AUTHORIZATION_PARAMS = "{\"param\": {\"externalToken\": \"%s\"}}"; + + protected static final ObjectMapper objectMapper; + + static { + objectMapper = new ObjectMapper(); + objectMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false); + objectMapper.setDateFormat(new SimpleDateFormat("MM/dd/yyyy HH:mm")); + } + /** * Constructor * @@ -78,7 +111,7 @@ public class JWTToken { * @param secretEncoder encoder of secret * @throws JWTException */ - public JWTToken(String token, String secret, String knownIssuers, SecretEncoder secretEncoder) throws JWTException{ + public JWTToken(String token, String secret, String knownIssuers, String authorizationURL, SecretEncoder secretEncoder) throws JWTException{ if (token == null) { logger.error("token can not be null"); throw new IllegalArgumentException("token can not be null"); @@ -95,8 +128,9 @@ public JWTToken(String token, String secret, String knownIssuers, SecretEncoder this.knownIssuers.add(issuer.trim()); } - - setTokenAndSecret(token, secret); + this.authorizationURL = authorizationURL; + this.token = token; + this.secret = secret; } /** @@ -105,7 +139,7 @@ public JWTToken(String token, String secret, String knownIssuers, SecretEncoder * @param algorithm algorithm to be used * @throws JWTException */ - protected void verify(Algorithm algorithm) throws JWTException { + protected void verifyToken(Algorithm algorithm) throws JWTException { try { Verification verification = JWT.require(algorithm); @@ -135,20 +169,22 @@ protected void verify(Algorithm algorithm) throws JWTException { } /** - * Set this JWT class besed on token string + * Verify this JWT class besed on token string * * @throws JWTException */ - protected void apply() throws JWTException { - apply(this.encoder); + public JWTToken verify() throws JWTException { + return verify(this.encoder); } /** - * Set this JWT class based on token string + * Verify this JWT class based on token string + * * @param enc secret encoder * @throws JWTException */ - protected void apply(SecretEncoder enc) throws JWTException { + public JWTToken verify(SecretEncoder enc) throws JWTException { + valid = false; if (token == null) throw new IllegalArgumentException("token must be specified."); @@ -179,7 +215,6 @@ protected void apply(SecretEncoder enc) throws JWTException { logger.info("Getting pub key from " + decodedJWT.getIssuer()); Jwk jwk = jwkProvider.get(decodedJWT.getKeyId()); RSAPublicKey publicKey = (RSAPublicKey) jwk.getPublicKey(); - logger.info("Pubkey: " + new String(publicKey.getEncoded())); algorithm = Algorithm.RSA256(publicKey, null); } catch (Exception e) { @@ -203,8 +238,10 @@ protected void apply(SecretEncoder enc) throws JWTException { } else { throw new JWTException("Algorithm not supported: " + algorithmName); } - verify(algorithm); + verifyToken(algorithm); decodedJWTApply(decodedJWT); + valid = true; + return this; } /** @@ -254,6 +291,43 @@ protected Integer calcExpirySeconds(Integer exp, Integer iat) { return exp - issuedAt; } + /** + * Refresh jwt from authorizationUrl + * + * @return this instance + * @throws Exception if any error occurs + */ + public JWTToken refresh() throws Exception { + if (authorizationURL == null || "".equals(authorizationURL)) + throw new JWTException("Please set authorizationUrl"); + + logger.info("Refresh token from this url: " + authorizationURL); + DefaultHttpClient httpClient = new DefaultHttpClient(); + String newToken; + try { + URI authorizationUri = new URI(this.authorizationURL); + HttpPost httpPost = new HttpPost(authorizationUri); + httpPost.addHeader(HttpHeaders.CONTENT_TYPE, "application/json"); + + StringEntity body = new StringEntity(String.format(AUTHORIZATION_PARAMS, token)); + httpPost.setEntity(body); + HttpResponse response = httpClient.execute(httpPost); + HttpEntity entity = response.getEntity(); + if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) { + throw new JWTEndpointException(String.format(ERROR_MESSAGE_FORMAT, authorizationUri, + response.getStatusLine().getStatusCode(), + getErrorMessage(response.getStatusLine().getStatusCode()))); + } + + JsonNode result = objectMapper.readTree(entity.getContent()); + newToken = result.path("result").path("content").path("token").asText(); + } finally { + httpClient.getConnectionManager().shutdown(); + } + setTokenAndSecret(newToken, secret); + return this; + } + public String getUserId() { return userId; } @@ -345,6 +419,18 @@ public String getToken() { return token; } + public String getAuthorizationURL() { + return authorizationURL; + } + + public void setAuthorizationURL(String authorizationURL) { + this.authorizationURL = authorizationURL; + } + + public boolean isValid() { + return valid; + } + /** * Set new token * @@ -358,7 +444,7 @@ public void setTokenAndSecret(String token, String secret) throws JWTException { } this.token = token; this.secret = secret; - apply(); + verify(); } public static class SecretEncoder { diff --git a/token.properties.docker b/token.properties.docker index dff2d3332..c98b64de6 100644 --- a/token.properties.docker +++ b/token.properties.docker @@ -326,7 +326,7 @@ @CLIENT_SECRET_AUTH0@ = ZEEIRf_aLhvbYymAMTFefoEJ_8y7ELrUaboMTmE5fQoJXEo7sxxyg8IW6gtbyKuT @REG_SERVER_NAME@= tc.cloud.topcoder.com @LDAP_AUTH0_CONNECTION_NAME@=vm-ldap-connection -@JWT_VALID_ISSUERS@ = https://sma.auth0.com, https://newtc.auth0.com +@JWT_VALID_ISSUERS@ = https://sma.auth0.com, https://newtc.auth0.com, https://topcoder-newauth.auth0.com/ @ApplicationServer.SSO_COOKIE_KEY@=tcsso_vm @ApplicationServer.SSO_HASH_SECRET@=GKDKJF80dbdc541fe829898aa01d9e30118bab5d6b9fe94fd052a40069385f5628 @@ -336,12 +336,10 @@ # Direct API # ##################################### @ApplicationServer.JWT_COOKIE_KEY@=tcjwt_vm -@ApplicationServer.JWT_V3_COOKIE_KEY@=v3jwt @memberSearchApiUrl@=https://tc-api.cloud.topcoder.com:8443/v3/members/_suggest/ @groupMemberSearchApiUrl@=https://cockpit.cloud.topcoder.com/direct/group/member?handle= @groupMemberApiUrl@=http://tc-api.cloud.topcoder.com:8080/v3/groups/%d/members @directChallengeServicesApiUrl@=http://api.topcoder-dev.com/v3/direct/challenges -@authorizationUrl@=http://192.168.31.105:8084/v3/authorizations -@ssoLoginUrl@=https://topcoder-dev.com/login/ +@authorizationUrl@=https://api.topcoder-dev.com/v3/authorizations @userGroupsApiEndpoint@=http://tc-api.cloud.topcoder.com:8080/v3/groups diff --git a/token.properties.example b/token.properties.example index 9f5824dea..c306d821d 100644 --- a/token.properties.example +++ b/token.properties.example @@ -302,8 +302,6 @@ @ApplicationServer.SSO_DOMAIN@=topcoder.com @ApplicationServer.JWT_COOKIE_KEY@=tcjwt -@ApplicationServer.JWT_V3_COOKIE_KEY@=v3jwt - ####################################### # securityGroupApplicationContext.xml # @@ -401,5 +399,4 @@ @groupMemberApiUrl@=http://172.18.0.1:8080/v3/groups/%d/members @directChallengeServicesApiUrl@=http://api.topcoder-dev.com/v3/direct/challenges @authorizationUrl@=http://api.topcoder-dev.com/v3/authorizations -@ssoLoginUrl@=https://topcoder-dev.com/login/ @userGroupsApiEndpoint@=http://172.18.0.1:8080/v3/groups \ No newline at end of file