Search in sources :

Example 1 with InvalidationException

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);
        }
    }
}
Also used : LocalDateTime(java.time.LocalDateTime) HashMap(java.util.HashMap) JWTException(io.hops.hopsworks.jwt.exception.JWTException) GeneralSecurityException(java.security.GeneralSecurityException) IOException(java.io.IOException) MaterializedJWT(io.hops.hopsworks.persistence.entity.airflow.MaterializedJWT) MaterializedJWTID(io.hops.hopsworks.persistence.entity.airflow.MaterializedJWTID) AirflowException(io.hops.hopsworks.exceptions.AirflowException) InvalidationException(io.hops.hopsworks.jwt.exception.InvalidationException) AccessTimeout(javax.ejb.AccessTimeout) Lock(javax.ejb.Lock)

Example 2 with InvalidationException

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);
    }
}
Also used : LocalDateTime(java.time.LocalDateTime) JWTException(io.hops.hopsworks.jwt.exception.JWTException) IOException(java.io.IOException) InvalidationException(io.hops.hopsworks.jwt.exception.InvalidationException) Date(java.util.Date) GeneralSecurityException(java.security.GeneralSecurityException) JWTDecodeException(com.auth0.jwt.exceptions.JWTDecodeException) SQLException(java.sql.SQLException) AirflowException(io.hops.hopsworks.exceptions.AirflowException) IOException(java.io.IOException) JWTException(io.hops.hopsworks.jwt.exception.JWTException) InvalidationException(io.hops.hopsworks.jwt.exception.InvalidationException) HashSet(java.util.HashSet) AccessTimeout(javax.ejb.AccessTimeout) TransactionAttribute(javax.ejb.TransactionAttribute) Timeout(javax.ejb.Timeout) AccessTimeout(javax.ejb.AccessTimeout) Lock(javax.ejb.Lock)

Example 3 with InvalidationException

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");
}
Also used : Path(java.nio.file.Path) LocalDateTime(java.time.LocalDateTime) JWTException(io.hops.hopsworks.jwt.exception.JWTException) ArrayList(java.util.ArrayList) JupyterProject(io.hops.hopsworks.persistence.entity.jupyter.JupyterProject) Users(io.hops.hopsworks.persistence.entity.user.Users) IOException(java.io.IOException) MaterializedJWT(io.hops.hopsworks.persistence.entity.airflow.MaterializedJWT) JupyterProject(io.hops.hopsworks.persistence.entity.jupyter.JupyterProject) Project(io.hops.hopsworks.persistence.entity.project.Project) JWTDecodeException(com.auth0.jwt.exceptions.JWTDecodeException) JupyterSettings(io.hops.hopsworks.persistence.entity.jupyter.JupyterSettings) InvalidationException(io.hops.hopsworks.jwt.exception.InvalidationException) DecodedJWT(com.auth0.jwt.interfaces.DecodedJWT) Map(java.util.Map) HashMap(java.util.HashMap)

Example 4 with InvalidationException

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);
    }
}
Also used : LocalDateTime(java.time.LocalDateTime) HashMap(java.util.HashMap) JWTException(io.hops.hopsworks.jwt.exception.JWTException) IOException(java.io.IOException) InvalidationException(io.hops.hopsworks.jwt.exception.InvalidationException) GeneralSecurityException(java.security.GeneralSecurityException) JWTDecodeException(com.auth0.jwt.exceptions.JWTDecodeException) IOException(java.io.IOException) ServiceException(io.hops.hopsworks.exceptions.ServiceException) JWTException(io.hops.hopsworks.jwt.exception.JWTException) InvalidationException(io.hops.hopsworks.jwt.exception.InvalidationException) HashSet(java.util.HashSet) AccessTimeout(javax.ejb.AccessTimeout) TransactionAttribute(javax.ejb.TransactionAttribute) Timeout(javax.ejb.Timeout) AccessTimeout(javax.ejb.AccessTimeout) Lock(javax.ejb.Lock)

Example 5 with InvalidationException

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());
            }
        }
    }
}
Also used : JwtSigningKey(io.hops.hopsworks.persistence.entity.jwt.JwtSigningKey) InvalidationException(io.hops.hopsworks.jwt.exception.InvalidationException) DecodedJWT(com.auth0.jwt.interfaces.DecodedJWT) Claim(com.auth0.jwt.interfaces.Claim)

Aggregations

InvalidationException (io.hops.hopsworks.jwt.exception.InvalidationException)10 JWTException (io.hops.hopsworks.jwt.exception.JWTException)8 IOException (java.io.IOException)6 LocalDateTime (java.time.LocalDateTime)6 HashMap (java.util.HashMap)5 JWTDecodeException (com.auth0.jwt.exceptions.JWTDecodeException)4 DecodedJWT (com.auth0.jwt.interfaces.DecodedJWT)4 MaterializedJWT (io.hops.hopsworks.persistence.entity.airflow.MaterializedJWT)4 GeneralSecurityException (java.security.GeneralSecurityException)4 AccessTimeout (javax.ejb.AccessTimeout)4 Lock (javax.ejb.Lock)4 Users (io.hops.hopsworks.persistence.entity.user.Users)3 AirflowException (io.hops.hopsworks.exceptions.AirflowException)2 ServiceException (io.hops.hopsworks.exceptions.ServiceException)2 DuplicateSigningKeyException (io.hops.hopsworks.jwt.exception.DuplicateSigningKeyException)2 NotRenewableException (io.hops.hopsworks.jwt.exception.NotRenewableException)2 SigningKeyNotFoundException (io.hops.hopsworks.jwt.exception.SigningKeyNotFoundException)2 VerificationException (io.hops.hopsworks.jwt.exception.VerificationException)2 MaterializedJWTID (io.hops.hopsworks.persistence.entity.airflow.MaterializedJWTID)2 Project (io.hops.hopsworks.persistence.entity.project.Project)2