edu.internet2.middleware.grouper.app.boxProvisioner.BoxGrouperExternalSystem Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of grouper Show documentation
Show all versions of grouper Show documentation
Internet2 Groups Management Toolkit
package edu.internet2.middleware.grouper.app.boxProvisioner;
import java.io.File;
import java.io.StringReader;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Security;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder;
import org.bouncycastle.operator.InputDecryptorProvider;
import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator.Builder;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.RSAKeyProvider;
import com.fasterxml.jackson.databind.JsonNode;
import edu.internet2.middleware.grouper.app.config.GrouperConfigurationModuleAttribute;
import edu.internet2.middleware.grouper.app.externalSystem.GrouperExternalSystem;
import edu.internet2.middleware.grouper.cfg.dbConfig.ConfigFileName;
import edu.internet2.middleware.grouper.cfg.text.GrouperTextContainer;
import edu.internet2.middleware.grouper.misc.GrouperStartup;
import edu.internet2.middleware.grouper.util.GrouperHttpClient;
import edu.internet2.middleware.grouper.util.GrouperHttpMethod;
import edu.internet2.middleware.grouper.util.GrouperUtil;
import edu.internet2.middleware.grouperClient.collections.MultiKey;
import edu.internet2.middleware.grouperClient.util.ExpirableCache;
import edu.internet2.middleware.grouperClient.util.GrouperClientConfig;
import edu.internet2.middleware.morphString.Morph;
public class BoxGrouperExternalSystem extends GrouperExternalSystem {
public static void main(String[] args) {
GrouperStartup.startup();
BoxGrouperExternalSystem boxGrouperExternalSystem = new BoxGrouperExternalSystem();
boxGrouperExternalSystem.setConfigId("boxNonProd");
List test = boxGrouperExternalSystem.test();
System.out.println(GrouperUtil.toStringForLog(test));
}
/**
* cache of config key to expires on and encrypted bearer token
*/
private static ExpirableCache configKeyToExpiresOnAndBearerToken = new ExpirableCache(60);
public static void clearCache() {
configKeyToExpiresOnAndBearerToken.clear();
}
@Override
public ConfigFileName getConfigFileName() {
return ConfigFileName.GROUPER_CLIENT_PROPERTIES;
}
@Override
public String getConfigItemPrefix() {
if (StringUtils.isBlank(this.getConfigId())) {
throw new RuntimeException("Must have configId!");
}
return "grouperClient.boxConnector." + this.getConfigId() + ".";
}
@Override
public String getConfigIdRegex() {
return "^(grouperClient\\.boxConnector)\\.([^.]+)\\.(.*)$";
}
@Override
public String getConfigIdThatIdentifiesThisConfig() {
return "myConnector";
}
static class BoxRsaKeyProvider implements RSAKeyProvider {
private RSAPrivateKey privateKey;
private String publicKeyId;
BoxRsaKeyProvider(PrivateKey privateKey, String publicKeyId) {
this.privateKey = (RSAPrivateKey)privateKey;
this.publicKeyId = publicKeyId;
}
@Override
public RSAPublicKey getPublicKeyById(String keyId) {
throw new RuntimeException("not implemented");
}
@Override
public String getPrivateKeyId() {
return this.publicKeyId;
}
@Override
public RSAPrivateKey getPrivateKey() {
return privateKey;
}
}
public static String retrieveAccessTokenForBoxConfigId(Map debugMap, String configId) {
long now = System.currentTimeMillis();
MultiKey expiresOnAndEncryptedBearerToken = configKeyToExpiresOnAndBearerToken.get(configId);
String encryptedBearerToken = null;
if (expiresOnAndEncryptedBearerToken != null) {
long expiresOnSeconds = (Long)expiresOnAndEncryptedBearerToken.getKey(0);
encryptedBearerToken = (String)expiresOnAndEncryptedBearerToken.getKey(1);
if (expiresOnSeconds * 1000 > System.currentTimeMillis()) {
// use it
if (debugMap != null) {
debugMap.put("boxCachedAccessToken", true);
}
return Morph.decrypt(encryptedBearerToken);
}
}
String authenticationUrl = GrouperClientConfig.retrieveConfig().propertyValueString("grouperClient.boxConnector." + configId + ".authenticationUrl");
String publicKeyId = GrouperClientConfig.retrieveConfig().propertyValueString("grouperClient.boxConnector." + configId + ".publicKeyId");
String privateKeyFilePath = GrouperClientConfig.retrieveConfig().propertyValueString("grouperClient.boxConnector." + configId + ".privateKeyFileName");
String privateKeyString = GrouperClientConfig.retrieveConfig().propertyValueString("grouperClient.boxConnector." + configId + ".privateKeyContents_0");
String privateKeyPass = GrouperClientConfig.retrieveConfig().propertyValueString("grouperClient.boxConnector." + configId + ".privateKeyPass");
String clientId = GrouperClientConfig.retrieveConfig().propertyValueString("grouperClient.boxConnector." + configId + ".clientId");
String clientSecret = GrouperClientConfig.retrieveConfig().propertyValueString("grouperClient.boxConnector." + configId + ".clientSecret");
String enterpriseId = GrouperClientConfig.retrieveConfig().propertyValueString("grouperClient.boxConnector." + configId + ".enterpriseId");
boolean usingPrivateKey = StringUtils.isNotBlank(privateKeyFilePath) || StringUtils.isNotBlank(privateKeyString);
PrivateKey privateKey = null;
String privateKeyContents = null;
String signedJwt = null;
if (usingPrivateKey) {
if (StringUtils.isNotBlank(privateKeyFilePath)) {
privateKeyContents = GrouperUtil.readFileIntoString(new File(privateKeyFilePath));
} else {
privateKeyContents = privateKeyString;
}
if (StringUtils.isNotBlank(privateKeyPass)) {
try {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
PEMParser pemParser = new PEMParser(new StringReader(privateKeyContents));
Object object = pemParser.readObject();
JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
PKCS8EncryptedPrivateKeyInfo pkcs8EncryptedPrivateKeyInfo = (PKCS8EncryptedPrivateKeyInfo)object;
InputDecryptorProvider decryptionProv = new JceOpenSSLPKCS8DecryptorProviderBuilder().build(privateKeyPass.toCharArray());
PrivateKeyInfo keyInfo = pkcs8EncryptedPrivateKeyInfo.decryptPrivateKeyInfo(decryptionProv);
privateKey = converter.getPrivateKey(keyInfo);
} catch(Exception e) {
throw new RuntimeException("Could not construct private key", e);
}
} else {
try {
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKeyContents));
KeyFactory kf = KeyFactory.getInstance("RSA");
privateKey = kf.generatePrivate(keySpec);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("Could not reconstruct the private key, the given algorithm could not be found.", e);
} catch (InvalidKeySpecException e) {
throw new RuntimeException("Could not reconstruct the private key", e);
} catch (Exception e) {
throw new RuntimeException("Could not construct private key from key contents", e);
}
}
Algorithm algorithm = Algorithm.RSA512(new BoxRsaKeyProvider(privateKey, publicKeyId));
Builder jwtBuilder = JWT.create()
.withIssuer(clientId)
.withSubject(enterpriseId)
.withAudience(authenticationUrl)
.withClaim("box_sub_type", "enterprise")
.withIssuedAt(new Date(now))
.withJWTId(UUID.randomUUID().toString())
.withExpiresAt(new Date(now + 45 * 1000L));
signedJwt = jwtBuilder.sign(algorithm);
}
GrouperHttpClient grouperHttpClient = new GrouperHttpClient();
final String url = authenticationUrl;
grouperHttpClient.assignGrouperHttpMethod(GrouperHttpMethod.post);
grouperHttpClient.assignUrl(url);
String proxyHost = GrouperClientConfig.retrieveConfig().propertyValueString("grouperClient.boxConnector." + configId + ".proxyHost");
String proxyType = GrouperClientConfig.retrieveConfig().propertyValueString("grouperClient.boxConnector." + configId + ".proxyType");
String proxyUrl = null;
if(StringUtils.isNotBlank(proxyHost)) {
proxyUrl = proxyHost;
}
if (StringUtils.isNotBlank(proxyUrl)) {
grouperHttpClient.assignProxyUrl(proxyUrl);
}
if (StringUtils.isNotBlank(proxyType)) {
grouperHttpClient.assignProxyType(proxyType);
}
if (usingPrivateKey) {
grouperHttpClient.addBodyParameter("assertion", signedJwt);
grouperHttpClient.addBodyParameter("grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer");
} else {
grouperHttpClient.addBodyParameter("grant_type", "client_credentials");
grouperHttpClient.addBodyParameter("box_subject_type", "enterprise");
grouperHttpClient.addBodyParameter("box_subject_id", enterpriseId);
}
grouperHttpClient.addBodyParameter("client_id", clientId);
grouperHttpClient.addBodyParameter("client_secret", clientSecret);
int code = -1;
String json = null;
try {
grouperHttpClient.executeRequest();
code = grouperHttpClient.getResponseCode();
json = grouperHttpClient.getResponseBody();
} catch (Exception e) {
throw new RuntimeException("Error connecting to '" + url + "'", e);
}
if (code != 200) {
throw new RuntimeException("Cant get access token from '" + url + "' " + code + ", " + json);
}
JsonNode jsonObject = GrouperUtil.jsonJacksonNode(json);
int expiresInSeconds = GrouperUtil.jsonJacksonGetInteger(jsonObject, "expires_in");
String accessToken = GrouperUtil.jsonJacksonGetString(jsonObject, "access_token");
long expiresOn = now/1000 + expiresInSeconds - 5; // subtract 5 seconds just to be safe
expiresOnAndEncryptedBearerToken = new MultiKey(expiresOn, Morph.encrypt(accessToken));
configKeyToExpiresOnAndBearerToken.put(configId, expiresOnAndEncryptedBearerToken);
return accessToken;
}
@Override
public void validatePreSave(boolean isInsert, boolean fromUi,
List errorsToDisplay, Map validationErrorsToDisplay) {
super.validatePreSave(isInsert, fromUi, errorsToDisplay, validationErrorsToDisplay);
GrouperConfigurationModuleAttribute authenticationType = this.retrieveAttributes().get("authenticationType");
if (authenticationType != null && StringUtils.equals(authenticationType.getValueOrExpressionEvaluation(), "JWT")) {
GrouperConfigurationModuleAttribute privateKeyContents = this.retrieveAttributes().get("privateKeyContents_0");
GrouperConfigurationModuleAttribute privateKeyFile = this.retrieveAttributes().get("privateKeyFileName");
if (StringUtils.isBlank(privateKeyContents.getValueOrExpressionEvaluation()) && StringUtils.isBlank(privateKeyFile.getValueOrExpressionEvaluation())) {
validationErrorsToDisplay.put(privateKeyContents.getHtmlForElementIdHandle(), GrouperTextContainer.textOrNull("grouperConfigurationValidationBoxFilePathOrPrivateKeyRequired"));
}
}
}
@Override
public List test() throws UnsupportedOperationException {
List ret = new ArrayList<>();
try {
retrieveAccessTokenForBoxConfigId(new HashMap(), this.getConfigId());
} catch (Exception e) {
ret.add("Unable to make box connection: " + GrouperUtil.escapeHtml(e.getMessage(), true));
}
return ret;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy