Skip to content

Commit

Permalink
Feature ETP-801: Allows legacy format priv key
Browse files Browse the repository at this point in the history
  • Loading branch information
sebastianbarrozo committed Dec 18, 2024
1 parent eb83697 commit dc66a05
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -337,25 +337,25 @@ private static String getAlgorithmUsed(String[] tokenParts) throws JsonProcessin
*/
private static Algorithm getDecoderAlgorithm(SWSConfig config,
String algorithmUsed) throws JSONException, NoSuchAlgorithmException, InvalidKeySpecException, UnsupportedEncodingException {
String publicKeyContent = config.getPrivateKey();
boolean isNewVersion = StringUtils.startsWith(publicKeyContent, "{") && StringUtils.endsWith(publicKeyContent, "}");
if (isNewVersion) {
JSONObject keys = new JSONObject(publicKeyContent);
publicKeyContent = StringUtils.equals(ES256_ALGORITHM, algorithmUsed) ? keys.getString(PUBLIC_KEY) : keys.getString(PRIVATE_KEY);
}

String privateKey = config.getPrivateKey();
Algorithm algorithm;
if (isNewVersion && StringUtils.equals(ES256_ALGORITHM, algorithmUsed)) {
final ECPublicKey publicKey = getECPublicKey(publicKeyContent);
algorithm = Algorithm.ECDSA256(publicKey);
} else if (!isNewVersion || StringUtils.equals(HS256_ALGORITHM, algorithmUsed)) {
publicKeyContent = publicKeyContent.replace(BEGIN_SECRET_KEY, "")
.replace(END_SECRET_KEY, "")
.replace("\\s", "");
algorithm = Algorithm.HMAC256(publicKeyContent);
if (isNewVersionPrivKey(privateKey)) {
JSONObject keys = new JSONObject(privateKey);
if (StringUtils.equals(ES256_ALGORITHM, algorithmUsed)) {
String publicKeyContent = keys.getString(PUBLIC_KEY);
ECPublicKey publicKey = getECPublicKey(publicKeyContent);
algorithm = Algorithm.ECDSA256(publicKey);
} else if (StringUtils.equals(HS256_ALGORITHM, algorithmUsed)) {
String privKey = cleanPrivateKey(config);
algorithm = Algorithm.HMAC256(privKey);
} else {
String errorMessage = String.format(
OBMessageUtils.messageBD("SMFSWS_UnsupportedSigningAlgorithm"), algorithmUsed);
throw new IllegalArgumentException(errorMessage);
}
} else {
String errorMessage = String.format(OBMessageUtils.messageBD("SMFSWS_UnsupportedSigningAlgorithm"), algorithmUsed);
throw new IllegalArgumentException(errorMessage);
// Legacy private key format. Use HS256 algorithm.
algorithm = Algorithm.HMAC256(privateKey);
}
return algorithm;
}
Expand Down Expand Up @@ -434,6 +434,16 @@ public static String generateToken(User user, Role role, Organization org) throw
return generateToken(user, role, org, null);
}

/**
* Detects if the private key content is in the new version format (JSON object).
*
* @param privateKey The private key content to be checked.
* @return true if the private key content is in the new version format (JSON object), false otherwise.
*/
public static boolean isNewVersionPrivKey(String privateKey) {
return StringUtils.startsWith(privateKey, "{") && StringUtils.endsWith(privateKey, "}");
}

/**
* Generates a JSON Web Token (JWT) for a given user with specified role, organization, and warehouse details.
* This method dynamically selects the token signing algorithm based on the configuration setting.
Expand All @@ -453,24 +463,29 @@ public static String generateToken(User user, Role role, Organization org, Wareh
OBContext.setAdminMode(true);
SWSConfig config = SWSConfig.getInstance();

Role selectedRole = null;
Warehouse selectedWarehouse = null;
List<UserRoles> userRoleList = user.getADUserRolesList();
Role defaultWsRole = user.getSmfswsDefaultWsRole();
Role defaultRole = user.getDefaultRole();
Organization defaultOrg = user.getDefaultOrganization();
Warehouse defaultWarehouse = user.getDefaultWarehouse();
selectedRole = getRole(role, userRoleList, defaultWsRole, defaultRole);
Role selectedRole = getRole(role, userRoleList, defaultWsRole, defaultRole);
Organization selectedOrg = getOrganization(org, selectedRole, defaultRole, defaultOrg);
selectedWarehouse = getWarehouse(warehouse, selectedOrg, selectedWarehouse, defaultWarehouse);

String algorithmUsed = Preferences.getPreferenceValue("SMFSWS_EncryptionAlgorithm", true,
OBContext.getOBContext().getCurrentClient(),
OBContext.getOBContext().getCurrentOrganization(), OBContext.getOBContext().getUser(), OBContext.getOBContext().getRole(),
null);

String privateKey = cleanPrivateKey(config, algorithmUsed);
Algorithm algorithm = getEncoderAlgorithm(privateKey, algorithmUsed);
selectedWarehouse = getWarehouse(warehouse, selectedOrg, defaultWarehouse);

String privateKey = config.getPrivateKey();
Algorithm algorithm;
if(isNewVersionPrivKey(privateKey)) {
String algorithmUsed = Preferences.getPreferenceValue("SMFSWS_EncryptionAlgorithm", true,
OBContext.getOBContext().getCurrentClient(),
OBContext.getOBContext().getCurrentOrganization(), OBContext.getOBContext().getUser(), OBContext.getOBContext().getRole(),
null);
privateKey = cleanPrivateKey(config);
algorithm = getEncoderAlgorithm(privateKey, algorithmUsed);
} else {
// Legacy private key format. Use HS256 algorithm.
algorithm = getEncoderAlgorithm(privateKey, HS256_ALGORITHM);
}
Builder jwtBuilder = getJwtBuilder(user, selectedRole, selectedOrg, selectedWarehouse);

if (config.getExpirationTime() > 0) {
Expand All @@ -495,20 +510,20 @@ public static String generateToken(User user, Role role, Organization org, Wareh
* @return The cleaned private key content as a String.
* @throws JSONException If there is an issue parsing the private key JSON object.
*/
private static String cleanPrivateKey(SWSConfig config, String algorithmUsed) throws JSONException {
private static String cleanPrivateKey(SWSConfig config)
throws JSONException {
String privateKeyContent = config.getPrivateKey();
boolean isNewVersion = StringUtils.startsWith(privateKeyContent, "{") && StringUtils.endsWith(privateKeyContent, "}");
if (isNewVersion) {
JSONObject keys = new JSONObject(privateKeyContent);
privateKeyContent = keys.getString(PRIVATE_KEY);
JSONObject keys = new JSONObject(privateKeyContent);
privateKeyContent = keys.getString(PRIVATE_KEY);
if (StringUtils.startsWith(BEGIN_SECRET_KEY, privateKeyContent)) {
privateKeyContent = privateKeyContent.replace(BEGIN_SECRET_KEY, "")
.replace(END_SECRET_KEY, "")
.replace("\\s", "");
} else {
privateKeyContent = privateKeyContent.replace(BEGIN_PRIVATE_KEY, "")
.replace(END_PRIVATE_KEY, "")
.replace("\\s", "");
}
privateKeyContent = StringUtils.equals(HS256_ALGORITHM, algorithmUsed) ?
privateKeyContent.replace(BEGIN_SECRET_KEY, "")
.replace(END_SECRET_KEY, "")
.replace("\\s", "") :
privateKeyContent.replace(BEGIN_PRIVATE_KEY, "")
.replace(END_PRIVATE_KEY, "")
.replace("\\s", "");
return privateKeyContent;
}

Expand All @@ -525,7 +540,8 @@ private static String cleanPrivateKey(SWSConfig config, String algorithmUsed) th
* @throws UnsupportedEncodingException If there is an issue decoding the private key.
*/
private static Algorithm getEncoderAlgorithm(String privateKeyContent, String algorithmUsed)
throws NoSuchAlgorithmException, InvalidKeySpecException, UnsupportedEncodingException {
throws NoSuchAlgorithmException, InvalidKeySpecException, UnsupportedEncodingException,
JSONException {
Algorithm algorithm;

if (StringUtils.equals(ES256_ALGORITHM, algorithmUsed)) {
Expand All @@ -545,13 +561,13 @@ private static Algorithm getEncoderAlgorithm(String privateKeyContent, String al
*
* @param warehouse The provided {@link Warehouse} to be validated.
* @param selectedOrg The {@link Organization} associated with the warehouse.
* @param selectedWarehouse The warehouse selected during processing, initially null.
* @param defaultWarehouse The default {@link Warehouse} to be used if the provided warehouse is not valid.
* @return The selected {@link Warehouse}.
* @throws OBException If the organization has no available warehouses.
*/
private static Warehouse getWarehouse(Warehouse warehouse, Organization selectedOrg, Warehouse selectedWarehouse,
private static Warehouse getWarehouse(Warehouse warehouse, Organization selectedOrg,
Warehouse defaultWarehouse) {
Warehouse selectedWarehouse = null;
List<Warehouse> warehouseList = SecureWebServicesUtils.getOrganizationWarehouses(selectedOrg);
// if warehouse is valid, select
if (warehouse != null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,14 @@ public class SecureWebServicesUtilsTest extends WeldBaseTest {

private static final String HS256_PRIVATE_KEY_MOCK = "{\"private-key\":\"-----BEGIN SECRET KEY-----uKOQOkfQPEmFs7CKQhT9UJNQ5DHEZmnBxU/2f5x06YE=-----END SECRET KEY-----\",\"public-key\":\"\"}";
private static final String ES256_PRIVATE_KEY_MOCK = "{\"private-key\":\"-----BEGIN PRIVATE KEY-----MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgs6Wr9OstUyGI3WIdXUGrx4/DA87e3dst93f7p5NVGSmhRANCAASgaQjofAzCf93v4qs+Z9ou5g74gP/B9Uxn8inJ8/0rShFdV7/60B8EeZxPiiTTe1zvkl9V/5IRkQkXIJrmY4UI-----END PRIVATE KEY-----\",\"public-key\":\"-----BEGIN PUBLIC KEY-----MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEoGkI6HwMwn/d7+KrPmfaLuYO+ID/wfVMZ/IpyfP9K0oRXVe/+tAfBHmcT4ok03tc75JfVf+SEZEJFyCa5mOFCA==-----END PUBLIC KEY-----\"}";
private static final String HS256_PRIVATE_KEY_MOCK_LEGACY = "*z8iR8Aujg{G-$lPz]+H7U.pv21|P1H=vGRnL[K+7_07@Bq\"~A},AlS^;}60dOq-";

private static final String ENCRYPTION_ALGORITHM_PREFERENCE = "SMFSWS_EncryptionAlgorithm";
private static final String ENCRYPTION_ALGORITHM_HS256 = "HS256";
private static final String ENCRYPTION_ALGORITHM_ES256 = "ES256";
public static final String ORGANIZATION = "organization";
public static final String USER = "user";
public static final String ROLE = "role";

/**
* Sets up the test environment.
Expand Down Expand Up @@ -101,9 +105,9 @@ public void testGenerateAndDecodeTokenWithHS256Algorithm() throws Exception {
String token = SecureWebServicesUtils.generateToken(user, role, org, warehouse);
DecodedJWT decodedToken = SecureWebServicesUtils.decodeToken(token);

assertEquals(user.getId(), decodedToken.getClaim("user").asString());
assertEquals(role.getId(), decodedToken.getClaim("role").asString());
assertEquals(org.getId(), decodedToken.getClaim("organization").asString());
assertEquals(user.getId(), decodedToken.getClaim(USER).asString());
assertEquals(role.getId(), decodedToken.getClaim(ROLE).asString());
assertEquals(org.getId(), decodedToken.getClaim(ORGANIZATION).asString());
}

/**
Expand All @@ -121,9 +125,9 @@ public void testGenerateAndDecodeTokenWithES256Algorithm() throws Exception {
String token = SecureWebServicesUtils.generateToken(user, role, org, warehouse);
DecodedJWT decodedToken = SecureWebServicesUtils.decodeToken(token);

assertEquals(user.getId(), decodedToken.getClaim("user").asString());
assertEquals(role.getId(), decodedToken.getClaim("role").asString());
assertEquals(org.getId(), decodedToken.getClaim("organization").asString());
assertEquals(user.getId(), decodedToken.getClaim(USER).asString());
assertEquals(role.getId(), decodedToken.getClaim(ROLE).asString());
assertEquals(org.getId(), decodedToken.getClaim(ORGANIZATION).asString());
}

/**
Expand All @@ -138,6 +142,50 @@ public void testDecodeTokenThrowsExceptionWithUnsupportedAlgorithm() {
assertThrows(IllegalArgumentException.class, () -> SecureWebServicesUtils.decodeToken(tokenRS));
}

/**
* Test the generation and decoding of a token with the ES256 algorithm throws an exception when
* the token is invalid.
*
* @throws Exception
*/
@Test
public void testLegacyPrivKeyES256() throws Exception {
configSWSConfig(HS256_PRIVATE_KEY_MOCK_LEGACY);
configAlgorithmPreference(ENCRYPTION_ALGORITHM_ES256);
User user = OBContext.getOBContext().getUser();
Role role = OBContext.getOBContext().getRole();
Organization org = OBContext.getOBContext().getCurrentOrganization();
Warehouse warehouse = user.getDefaultWarehouse();

String token = SecureWebServicesUtils.generateToken(user, role, org, warehouse);
DecodedJWT decodedToken = SecureWebServicesUtils.decodeToken(token);

assertEquals(user.getId(), decodedToken.getClaim(USER).asString());
assertEquals(role.getId(), decodedToken.getClaim(ROLE).asString());
assertEquals(org.getId(), decodedToken.getClaim(ORGANIZATION).asString());
}

/**
* Test the generation and decoding of a token with the HS256 algorithm.
*
* @throws Exception
*/
@Test
public void testLegacyPrivKeyHS256() throws Exception {
configSWSConfig(HS256_PRIVATE_KEY_MOCK_LEGACY);
configAlgorithmPreference(ENCRYPTION_ALGORITHM_HS256);
User user = OBContext.getOBContext().getUser();
Role role = OBContext.getOBContext().getRole();
Organization org = OBContext.getOBContext().getCurrentOrganization();
Warehouse warehouse = user.getDefaultWarehouse();

String token = SecureWebServicesUtils.generateToken(user, role, org, warehouse);
DecodedJWT decodedToken = SecureWebServicesUtils.decodeToken(token);

assertEquals(user.getId(), decodedToken.getClaim(USER).asString());
assertEquals(role.getId(), decodedToken.getClaim(ROLE).asString());
assertEquals(org.getId(), decodedToken.getClaim(ORGANIZATION).asString());
}
/**
* Cleans up the test environment.
*/
Expand All @@ -153,7 +201,9 @@ public void cleanUp() {
Preference pref = (Preference) OBDal.getInstance().createCriteria(Preference.class)
.add(Restrictions.eq(Preference.PROPERTY_PROPERTY, ENCRYPTION_ALGORITHM_PREFERENCE)).add(Restrictions.eq(Preference.PROPERTY_SELECTED, true))
.uniqueResult();
OBDal.getInstance().remove(pref);
if(pref != null) {
OBDal.getInstance().remove(pref);
}

OBDal.getInstance().flush();
OBDal.getInstance().commitAndClose();
Expand Down

0 comments on commit dc66a05

Please sign in to comment.