use of io.hops.hopsworks.jwt.exception.InvalidationException in project hopsworks by logicalclocks.
the class AirflowManager method prepareSecurityMaterial.
@Lock(LockType.READ)
@AccessTimeout(value = 1, unit = TimeUnit.SECONDS)
public void prepareSecurityMaterial(Users user, Project project, String[] audience) throws AirflowException {
isInitialized();
MaterializedJWTID materialID = new MaterializedJWTID(project.getId(), user.getUid(), MaterializedJWTID.USAGE.AIRFLOW);
if (!materializedJWTFacade.exists(materialID)) {
LocalDateTime expirationDate = DateUtils.getNow().plus(settings.getJWTLifetimeMs(), ChronoUnit.MILLIS);
AirflowJWT airflowJWT = new AirflowJWT(user.getUsername(), project.getId(), project.getName(), expirationDate, user.getUid());
try {
String[] roles = getUserRoles(user);
MaterializedJWT airflowMaterial = new MaterializedJWT(new MaterializedJWTID(project.getId(), user.getUid(), MaterializedJWTID.USAGE.AIRFLOW));
materializedJWTFacade.persist(airflowMaterial);
Map<String, Object> claims = new HashMap<>(3);
claims.put(Constants.RENEWABLE, false);
claims.put(Constants.EXPIRY_LEEWAY, settings.getJWTExpLeewaySec());
claims.put(Constants.ROLES, roles);
String token = jwtController.createToken(settings.getJWTSigningKeyName(), false, settings.getJWTIssuer(), audience, DateUtils.localDateTime2Date(expirationDate), DateUtils.localDateTime2Date(DateUtils.getNow()), user.getUsername(), claims, SignatureAlgorithm.valueOf(settings.getJWTSignatureAlg()));
String projectAirflowDir = getProjectSecretsDirectory(user.getUsername()).toString();
airflowJWT.tokenFile = Paths.get(projectAirflowDir, getTokenFileName(project.getName(), user.getUsername()));
airflowJWT.token = token;
writeTokenToFile(airflowJWT);
certificateMaterializer.materializeCertificatesLocalCustomDir(user.getUsername(), project.getName(), projectAirflowDir);
airflowJWTs.add(airflowJWT);
} catch (GeneralSecurityException | JWTException ex) {
deleteAirflowMaterial(materialID);
throw new AirflowException(RESTCodes.AirflowErrorCode.JWT_NOT_CREATED, Level.SEVERE, "Could not generate Airflow JWT for user " + user.getUsername(), ex.getMessage(), ex);
} catch (IOException ex) {
LOG.log(Level.WARNING, "Could not write Airflow JWT for user " + hdfsUsersController.getHdfsUserName(project, user), ex);
deleteAirflowMaterial(materialID);
try {
jwtController.invalidate(airflowJWT.token);
} catch (InvalidationException invEx) {
LOG.log(Level.FINE, "Could not invalidate Airflow JWT. Skipping...", ex);
}
throw new AirflowException(RESTCodes.AirflowErrorCode.JWT_NOT_STORED, Level.SEVERE, "Could not store Airflow JWT for user " + hdfsUsersController.getHdfsUserName(project, user), ex.getMessage(), ex);
}
}
}
use of io.hops.hopsworks.jwt.exception.InvalidationException in project hopsworks by logicalclocks.
the class AirflowManager method monitorSecurityMaterial.
/**
* Timer bean to periodically (JWT expiration lee way / 2) (a) clean stale JWT and X.509 material for Airflow
* and (b) renew used JWTs.
*
* a. Iterate all the material in memory and clean those that don't have any entry in the database, project has been
* deleted or those that project exist but user does not own any non-paused DAG in Airflow.
*
* b. For a valid JWT, renew it if the time has come (after expiration time and before expiration + expLeeWay)
*
* @param timer
*/
@Lock(LockType.WRITE)
@AccessTimeout(value = 500)
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
@Timeout
public void monitorSecurityMaterial(Timer timer) {
try {
LocalDateTime now = DateUtils.getNow();
// Clean unused token files and X.509 certificates
cleanStaleSecurityMaterial();
// Renew them
Set<AirflowJWT> newTokens2Add = new HashSet<>();
Iterator<AirflowJWT> airflowJWTIt = airflowJWTs.iterator();
while (airflowJWTIt.hasNext()) {
AirflowJWT airflowJWT = airflowJWTIt.next();
// If first does not need to be renewed, neither do the rest
if (airflowJWT.maybeRenew(now)) {
try {
LocalDateTime expirationDateTime = now.plus(settings.getJWTLifetimeMs(), ChronoUnit.MILLIS);
Date expirationDate = DateUtils.localDateTime2Date(expirationDateTime);
String token = jwtController.renewToken(airflowJWT.token, expirationDate, DateUtils.localDateTime2Date(DateUtils.getNow()), true, new HashMap<>(3));
AirflowJWT renewedJWT = new AirflowJWT(airflowJWT.username, airflowJWT.projectId, airflowJWT.projectName, expirationDateTime, airflowJWT.uid);
renewedJWT.tokenFile = airflowJWT.tokenFile;
renewedJWT.token = token;
airflowJWTIt.remove();
writeTokenToFile(renewedJWT);
newTokens2Add.add(renewedJWT);
} catch (JWTException ex) {
LOG.log(Level.WARNING, "Could not renew Airflow JWT for " + airflowJWT, ex);
} catch (IOException ex) {
LOG.log(Level.WARNING, "Could not write renewed Airflow JWT for " + airflowJWT, ex);
try {
jwtController.invalidate(airflowJWT.token);
} catch (InvalidationException iex) {
LOG.log(Level.FINE, "Could not invalidate Airflow JWT. SKipping...");
}
} catch (Exception ex) {
LOG.log(Level.SEVERE, "Generic error while renewing Airflow JWTs", ex);
}
} else {
break;
}
}
airflowJWTs.addAll(newTokens2Add);
} catch (Exception e) {
LOG.log(Level.SEVERE, "Got an exception while renewing/invalidating airflow jwt token", e);
}
}
use of io.hops.hopsworks.jwt.exception.InvalidationException in project hopsworks by logicalclocks.
the class JupyterJWTManager method recover.
protected void recover() {
LOG.log(INFO, "Starting Jupyter JWT manager recovery");
List<MaterializedJWT> failed2recover = new ArrayList<>();
// Get state from the database
for (MaterializedJWT materializedJWT : materializedJWTFacade.findAll4Jupyter()) {
LOG.log(Level.FINEST, "Recovering Jupyter JWT " + materializedJWT.getIdentifier());
// First lookup project and user in db
Project project = projectFacade.find(materializedJWT.getIdentifier().getProjectId());
Users user = userFacade.find(materializedJWT.getIdentifier().getUserId());
if (project == null || user == null) {
LOG.log(Level.WARNING, "Tried to recover " + materializedJWT.getIdentifier() + " but could not find " + "either Project or User");
failed2recover.add(materializedJWT);
continue;
}
// Get Jupyter configuration from db
String hdfsUsername = hdfsUsersController.getHdfsUserName(project, user);
JupyterProject jupyterProject = jupyterFacade.findByUser(hdfsUsername);
if (jupyterProject == null) {
LOG.log(Level.FINEST, "There is no Jupyter configuration persisted for " + materializedJWT.getIdentifier());
failed2recover.add(materializedJWT);
continue;
}
// Check if Jupyter is still running
if (!jupyterManager.ping(jupyterProject)) {
LOG.log(Level.FINEST, "Jupyter server is not running for " + materializedJWT.getIdentifier() + " Skip recovering...");
failed2recover.add(materializedJWT);
continue;
}
JupyterSettings jupyterSettings = jupyterSettingsFacade.findByProjectUser(project, user.getEmail());
Path tokenFile = constructTokenFilePath(jupyterSettings);
String token = null;
JupyterJWT jupyterJWT = null;
CidAndPort pidAndPort = new CidAndPort(jupyterProject.getCid(), jupyterProject.getPort());
try {
token = FileUtils.readFileToString(tokenFile.toFile());
DecodedJWT decodedJWT = jwtController.verifyToken(token, settings.getJWTIssuer());
jupyterJWT = new JupyterJWT(project, user, DateUtils.date2LocalDateTime(decodedJWT.getExpiresAt()), pidAndPort);
jupyterJWT.token = token;
jupyterJWT.tokenFile = tokenFile;
LOG.log(Level.FINE, "Successfully read existing JWT from local filesystem");
} catch (IOException | JWTException | JWTDecodeException ex) {
LOG.log(Level.FINE, "Could not recover Jupyter JWT from local filesystem, generating new!", ex);
// JWT does not exist or it is not valid any longer
// We should create a new one
String[] audience = new String[] { "api" };
LocalDateTime expirationDate = LocalDateTime.now().plus(settings.getJWTLifetimeMs(), ChronoUnit.MILLIS);
String[] userRoles = usersController.getUserRoles(user).toArray(new String[1]);
try {
Map<String, Object> claims = new HashMap<>(3);
claims.put(Constants.RENEWABLE, false);
claims.put(Constants.EXPIRY_LEEWAY, settings.getJWTExpLeewaySec());
claims.put(Constants.ROLES, userRoles);
token = jwtController.createToken(settings.getJWTSigningKeyName(), false, settings.getJWTIssuer(), audience, DateUtils.localDateTime2Date(expirationDate), DateUtils.localDateTime2Date(DateUtils.getNow()), user.getUsername(), claims, SignatureAlgorithm.valueOf(settings.getJWTSignatureAlg()));
jupyterJWT = new JupyterJWT(project, user, expirationDate, pidAndPort);
jupyterJWT.token = token;
jupyterJWT.tokenFile = tokenFile;
jwtTokenWriter.writeToken(settings, jupyterJWT);
LOG.log(Level.FINE, "Generated new Jupyter JWT cause could not recover existing");
} catch (IOException recIOEx) {
LOG.log(Level.WARNING, "Failed to recover Jupyter JWT for " + materializedJWT.getIdentifier() + ", generated new valid JWT but failed to write to local filesystem. Invalidating new token!" + " Continue recovering...");
if (token != null) {
try {
jwtController.invalidate(token);
} catch (InvalidationException jwtInvEx) {
// NO-OP
}
}
failed2recover.add(materializedJWT);
continue;
} catch (GeneralSecurityException | JWTException jwtEx) {
LOG.log(Level.WARNING, "Failed to recover Jupyter JWT for " + materializedJWT.getIdentifier() + ", tried to generate new token and it failed as well. Could not recover! Continue recovering...");
// Did our best, it's good to know when you should give up
failed2recover.add(materializedJWT);
continue;
}
}
addToken(jupyterJWT);
}
// Remove from the database entries that we failed to recover
for (MaterializedJWT failedRecovery : failed2recover) {
materializedJWTFacade.delete(failedRecovery.getIdentifier());
}
LOG.log(INFO, "Finished Jupyter JWT recovery");
}
use of io.hops.hopsworks.jwt.exception.InvalidationException in project hopsworks by logicalclocks.
the class JupyterJWTManager method monitorJWT.
@Lock(LockType.WRITE)
@AccessTimeout(value = 500)
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
@Timeout
public void monitorJWT() {
// Renew the rest of them
Set<JupyterJWT> renewedJWTs = new HashSet<>(this.jupyterJWTs.size());
Iterator<JupyterJWT> jupyterJWTs = this.jupyterJWTs.iterator();
LocalDateTime now = DateUtils.getNow();
try {
while (jupyterJWTs.hasNext()) {
JupyterJWT element = jupyterJWTs.next();
// If element N does not need to be renewed neither does N+1
if (element.maybeRenew(now)) {
LocalDateTime newExpirationDate = now.plus(settings.getJWTLifetimeMs(), ChronoUnit.MILLIS);
String newToken = null;
try {
newToken = jwtController.renewToken(element.token, DateUtils.localDateTime2Date(newExpirationDate), DateUtils.localDateTime2Date(now), true, new HashMap<>(3));
JupyterJWT renewedJWT = new JupyterJWT(element.project, element.user, newExpirationDate, element.pidAndPort);
renewedJWT.tokenFile = element.tokenFile;
renewedJWT.token = newToken;
jwtTokenWriter.writeToken(settings, renewedJWT);
renewedJWTs.add(renewedJWT);
} catch (JWTException ex) {
LOG.log(Level.WARNING, "Could not renew Jupyter JWT for " + element, ex);
} catch (IOException ex) {
LOG.log(Level.WARNING, "Could not write renewed Jupyter JWT to file for " + element, ex);
if (newToken != null) {
try {
jwtController.invalidate(newToken);
} catch (InvalidationException invEx) {
LOG.log(Level.FINE, "Could not invalidate failed token", invEx);
}
}
} catch (Exception ex) {
LOG.log(Level.SEVERE, "Generic error renewing Jupyter JWT for " + element, ex);
}
} else {
break;
}
}
renewedJWTs.forEach(t -> {
removeToken(t.pidAndPort);
addToken(t);
});
} catch (Exception e) {
LOG.log(Level.SEVERE, "Got an exception while renewing jupyter jwt token", e);
}
}
use of io.hops.hopsworks.jwt.exception.InvalidationException in project hopsworks by logicalclocks.
the class JWTController method invalidateServiceToken.
public void invalidateServiceToken(String serviceToken2invalidate, String defaultJWTSigningKeyName) {
DecodedJWT serviceJWT2invalidate = decodeToken(serviceToken2invalidate);
try {
invalidate(serviceToken2invalidate);
} catch (InvalidationException ex) {
LOGGER.log(Level.WARNING, "Could not invalidate service JWT with ID " + serviceJWT2invalidate.getId() + ". Continuing with deleting signing key");
}
Claim signingKeyID = serviceJWT2invalidate.getClaim(Constants.SERVICE_JWT_RENEWAL_KEY_ID);
if (signingKeyID != null && !signingKeyID.isNull()) {
// Do not use Claim.asInt, it returns null
JwtSigningKey signingKey = findSigningKeyById(Integer.parseInt(signingKeyID.asString()));
if (signingKey != null && defaultJWTSigningKeyName != null) {
if (!defaultJWTSigningKeyName.equals(signingKey.getName()) && !ONE_TIME_JWT_SIGNING_KEY_NAME.equals(signingKey.getName())) {
deleteSigningKey(signingKey.getName());
}
}
}
}
Aggregations