Search in sources :

Example 6 with JWTException

use of io.hops.hopsworks.jwt.exception.JWTException in project hopsworks by logicalclocks.

the class JWTResource method renewServiceToken.

@PUT
@Path("/service")
@ApiOperation(value = "Renew a service JWT without invalidating the previous token", response = ServiceJWTDTO.class)
public Response renewServiceToken(JsonWebTokenDTO jwt, @Context HttpServletRequest request) throws HopsSecurityException {
    // This token should be the one-time renewal token
    String token = jWTHelper.getAuthToken(request);
    Users user = jWTHelper.getUserPrincipal(request);
    if (user == null) {
        DecodedJWT decodedJWT = JWT.decode(token);
        throw new HopsSecurityException(RESTCodes.SecurityErrorCode.NOT_RENEWABLE_TOKEN, Level.FINE, "User not found associated with that JWT", "Could not find user in the database associated with JWT " + decodedJWT.getId());
    }
    try {
        ServiceJWTDTO renewedTokens = jWTHelper.renewServiceToken(jwt, token, user, request.getRemoteHost());
        return Response.ok().entity(renewedTokens).build();
    } catch (JWTException | NoSuchAlgorithmException ex) {
        throw new HopsSecurityException(RESTCodes.SecurityErrorCode.NOT_RENEWABLE_TOKEN, Level.WARNING, "Could not renew service JWT", "Could not renew service JWT for " + request.getRemoteHost());
    }
}
Also used : JWTException(io.hops.hopsworks.jwt.exception.JWTException) Users(io.hops.hopsworks.persistence.entity.user.Users) ServiceJWTDTO(io.hops.hopsworks.api.user.ServiceJWTDTO) NoSuchAlgorithmException(java.security.NoSuchAlgorithmException) DecodedJWT(com.auth0.jwt.interfaces.DecodedJWT) HopsSecurityException(io.hops.hopsworks.exceptions.HopsSecurityException) Path(javax.ws.rs.Path) ApiOperation(io.swagger.annotations.ApiOperation) PUT(javax.ws.rs.PUT)

Example 7 with JWTException

use of io.hops.hopsworks.jwt.exception.JWTException in project hopsworks by logicalclocks.

the class AirflowManager method recover.

/**
 * Recover security material for Airflow after restart. Read all active material from the database.
 * Check if JWT exists in the local filesystem and it is valid.
 * If not try to create a new one. Finally, materialize X.509 for project specific user.
 */
private void recover() {
    LOG.log(Level.FINE, "Starting Airflow manager recovery");
    List<MaterializedJWT> failed2recover = new ArrayList<>();
    Project project = null;
    Users user = null;
    // Get last known state from storage
    for (MaterializedJWT material : materializedJWTFacade.findAll4Airflow()) {
        LOG.log(Level.FINEST, "Recovering material: " + material.getIdentifier().getProjectId() + " - " + material.getIdentifier().getUserId());
        project = projectFacade.find(material.getIdentifier().getProjectId());
        user = userFacade.find(material.getIdentifier().getUserId());
        if (project == null || user == null) {
            LOG.log(Level.WARNING, "Error while recovering Project with ID: " + material.getIdentifier().getProjectId() + " and User ID: " + material.getIdentifier().getUserId() + ". Project or user is null");
            failed2recover.add(material);
            continue;
        }
        Path tokenFile = Paths.get(getProjectSecretsDirectory(user.getUsername()).toString(), getTokenFileName(project.getName(), user.getUsername()));
        AirflowJWT airflowJWT;
        String token = null;
        String materialIdentifier = "Project: " + project.getName() + " - User: " + user.getUsername();
        try {
            // First try to read JWT from the filesystem. We expect most of the cases this will succeed.
            token = FileUtils.readFileToString(tokenFile.toFile(), Charset.defaultCharset());
            DecodedJWT decoded = jwtController.verifyToken(token, settings.getJWTIssuer());
            airflowJWT = new AirflowJWT(user.getUsername(), project.getId(), project.getName(), DateUtils.date2LocalDateTime(decoded.getExpiresAt()), user.getUid());
            airflowJWT.tokenFile = tokenFile;
            airflowJWT.token = token;
            LOG.log(Level.FINE, "Successfully read existing JWT from local filesystem for " + materialIdentifier);
        } catch (IOException | JWTException | JWTDecodeException ex) {
            // JWT does not exist in the filesystem or we cannot read them or it is not valid any longer
            // We will create a new one
            // TODO(Antonis): Not very good that audience is hardcoded, but it is not accessible from hopsworks-common
            String[] audience = new String[] { "api" };
            LocalDateTime expirationDate = DateUtils.getNow().plus(settings.getJWTLifetimeMs(), ChronoUnit.MILLIS);
            String[] roles = getUserRoles(user);
            try {
                LOG.log(Level.FINEST, "JWT for " + materialIdentifier + " does not exist in the local FS or it is not " + "valid any longer, creating new one...");
                Map<String, Object> claims = new HashMap<>(3);
                claims.put(Constants.RENEWABLE, false);
                claims.put(Constants.EXPIRY_LEEWAY, settings.getJWTExpLeewaySec());
                claims.put(Constants.ROLES, roles);
                token = jwtController.createToken(settings.getJWTSigningKeyName(), false, settings.getJWTIssuer(), audience, DateUtils.localDateTime2Date(expirationDate), DateUtils.localDateTime2Date(DateUtils.getNow()), user.getUsername(), claims, SignatureAlgorithm.valueOf(settings.getJWTSignatureAlg()));
                airflowJWT = new AirflowJWT(user.getUsername(), project.getId(), project.getName(), expirationDate, user.getUid());
                airflowJWT.tokenFile = tokenFile;
                airflowJWT.token = token;
                writeTokenToFile(airflowJWT);
                LOG.log(Level.FINE, "Created new JWT for " + materialIdentifier + " and flushed to local FS");
            } catch (IOException ex1) {
                // Managed to create token but failed to write
                LOG.log(Level.WARNING, "Could not write to local FS new JWT for recovered material " + materialIdentifier + ". We will invalidate it and won't renew it.", ex1);
                if (token != null) {
                    try {
                        LOG.log(Level.FINE, "Failed to write JWT for " + materialIdentifier + ". Invalidating it...");
                        jwtController.invalidate(token);
                    } catch (InvalidationException ex2) {
                    // Not much we can do about it
                    }
                }
                failed2recover.add(material);
                continue;
            } catch (GeneralSecurityException | JWTException ex1) {
                LOG.log(Level.WARNING, "Tried to recover JWT for " + materialIdentifier + " but we failed. Giving up... " + "JWT will not be available for Airflow DAGs", ex1);
                // Initial token is invalid and could not create new. Give up
                failed2recover.add(material);
                continue;
            }
        }
        // If everything went fine with JWT, proceed with X.509
        try {
            LOG.log(Level.FINEST, "Materializing X.509 for " + materialIdentifier);
            certificateMaterializer.materializeCertificatesLocalCustomDir(user.getUsername(), project.getName(), getProjectSecretsDirectory(user.getUsername()).toString());
            LOG.log(Level.FINE, "Materialized X.509 for " + materialIdentifier);
            airflowJWTs.add(airflowJWT);
        } catch (IOException ex) {
            LOG.log(Level.WARNING, "Could not materialize X.509 for " + materialIdentifier + " Invalidating JWT and deleting from FS. JWT and X.509 will not be available for Airflow DAGs.", ex);
            // Could not materialize X.509
            if (token != null) {
                try {
                    LOG.log(Level.FINE, "Failed to materialize X.509 for " + materialIdentifier + " Invalidating JWT and deleting it from local FS.");
                    jwtController.invalidate(token);
                    FileUtils.deleteDirectory(getProjectSecretsDirectory(user.getUsername()).toFile());
                } catch (InvalidationException | IOException ex1) {
                // Not much we can do about it
                }
            }
            failed2recover.add(material);
        }
    }
    // Remove failed material from persistent storage
    for (MaterializedJWT failed : failed2recover) {
        materializedJWTFacade.delete(failed.getIdentifier());
    }
}
Also used : Path(java.nio.file.Path) LocalDateTime(java.time.LocalDateTime) JWTException(io.hops.hopsworks.jwt.exception.JWTException) ArrayList(java.util.ArrayList) Users(io.hops.hopsworks.persistence.entity.user.Users) IOException(java.io.IOException) MaterializedJWT(io.hops.hopsworks.persistence.entity.airflow.MaterializedJWT) Project(io.hops.hopsworks.persistence.entity.project.Project) JWTDecodeException(com.auth0.jwt.exceptions.JWTDecodeException) InvalidationException(io.hops.hopsworks.jwt.exception.InvalidationException) DecodedJWT(com.auth0.jwt.interfaces.DecodedJWT) Map(java.util.Map) HashMap(java.util.HashMap)

Example 8 with JWTException

use of io.hops.hopsworks.jwt.exception.JWTException in project hopsworks by logicalclocks.

the class JupyterJWTManager method materializeJWT.

@Lock(LockType.WRITE)
@AccessTimeout(value = 2000)
public void materializeJWT(Users user, Project project, JupyterSettings jupyterSettings, String cid, Integer port, String[] audience) throws ServiceException {
    MaterializedJWTID materialID = new MaterializedJWTID(project.getId(), user.getUid(), MaterializedJWTID.USAGE.JUPYTER);
    if (!materializedJWTFacade.exists(materialID)) {
        LocalDateTime expirationDate = LocalDateTime.now().plus(settings.getJWTLifetimeMs(), ChronoUnit.MILLIS);
        JupyterJWT jupyterJWT = new JupyterJWT(project, user, expirationDate, new CidAndPort(cid, port));
        try {
            String[] roles = usersController.getUserRoles(user).toArray(new String[1]);
            MaterializedJWT materializedJWT = new MaterializedJWT(materialID);
            materializedJWTFacade.persist(materializedJWT);
            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()));
            jupyterJWT.tokenFile = constructTokenFilePath(jupyterSettings);
            jupyterJWT.token = token;
            jwtTokenWriter.writeToken(settings, jupyterJWT);
            addToken(jupyterJWT);
        } catch (GeneralSecurityException | JWTException ex) {
            LOG.log(Level.SEVERE, "Error generating Jupyter JWT for " + jupyterJWT, ex);
            materializedJWTFacade.delete(materialID);
            throw new ServiceException(RESTCodes.ServiceErrorCode.JUPYTER_START_ERROR, Level.SEVERE, "Could not generate Jupyter JWT", ex.getMessage(), ex);
        } catch (IOException ex) {
            LOG.log(Level.SEVERE, "Error writing Jupyter JWT to file for " + jupyterJWT, ex);
            materializedJWTFacade.delete(materialID);
            try {
                jwtController.invalidate(jupyterJWT.token);
            } catch (InvalidationException invEx) {
                LOG.log(Level.FINE, "Could not invalidate Jupyter JWT after failure to write to file", ex);
            }
            throw new ServiceException(RESTCodes.ServiceErrorCode.JUPYTER_START_ERROR, Level.SEVERE, "Could not write Jupyter JWT to file", 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) ServiceException(io.hops.hopsworks.exceptions.ServiceException) InvalidationException(io.hops.hopsworks.jwt.exception.InvalidationException) AccessTimeout(javax.ejb.AccessTimeout) Lock(javax.ejb.Lock)

Example 9 with JWTException

use of io.hops.hopsworks.jwt.exception.JWTException in project hopsworks by logicalclocks.

the class JWTController method renewServiceToken.

public Pair<String, String[]> renewServiceToken(String oneTimeRenewalToken, String serviceToken, Date newExpiration, Date newNotBefore, Long serviceJWTLifetimeMS, String username, List<String> userRoles, List<String> audience, String remoteHostname, String issuer, String defaultJWTSigningKeyName, boolean force) throws JWTException, NoSuchAlgorithmException {
    Map<String, Object> claims = new HashMap<>(4);
    claims.put(Constants.RENEWABLE, false);
    claims.put(Constants.EXPIRY_LEEWAY, 3600);
    claims.put(Constants.ROLES, userRoles.toArray(new String[1]));
    String renewalKeyName = getServiceOneTimeJWTSigningKeyname(username, remoteHostname);
    LocalDateTime masterExpiration = newExpiration.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
    LocalDateTime notBefore = computeNotBefore4ServiceRenewalTokens(masterExpiration);
    LocalDateTime expiresAt = notBefore.plus(serviceJWTLifetimeMS, ChronoUnit.MILLIS);
    JsonWebToken jwtSpecs = new JsonWebToken();
    jwtSpecs.setSubject(username);
    jwtSpecs.setIssuer(issuer);
    jwtSpecs.setAudience(audience);
    jwtSpecs.setKeyId(renewalKeyName);
    jwtSpecs.setNotBefore(localDateTime2Date(notBefore));
    jwtSpecs.setExpiresAt(localDateTime2Date(expiresAt));
    try {
        // Then generate the new one-time tokens
        String[] renewalTokens = generateOneTimeTokens4ServiceJWTRenewal(jwtSpecs, claims, defaultJWTSigningKeyName);
        String signingKeyId = getSignKeyID(renewalTokens[0]);
        DecodedJWT serviceJWT = decodeToken(serviceToken);
        claims.clear();
        claims.put(Constants.RENEWABLE, false);
        claims.put(Constants.SERVICE_JWT_RENEWAL_KEY_ID, signingKeyId);
        claims.put(Constants.EXPIRY_LEEWAY, getExpLeewayClaim(serviceJWT));
        // Finally renew the service master token
        String renewedServiceToken = renewToken(serviceToken, newExpiration, newNotBefore, false, claims, force);
        invalidate(oneTimeRenewalToken);
        return Pair.of(renewedServiceToken, renewalTokens);
    } catch (JWTException | NoSuchAlgorithmException ex) {
        if (renewalKeyName != null) {
            deleteSigningKey(renewalKeyName);
        }
        throw ex;
    }
}
Also used : LocalDateTime(java.time.LocalDateTime) HashMap(java.util.HashMap) JWTException(io.hops.hopsworks.jwt.exception.JWTException) NoSuchAlgorithmException(java.security.NoSuchAlgorithmException) DecodedJWT(com.auth0.jwt.interfaces.DecodedJWT)

Aggregations

JWTException (io.hops.hopsworks.jwt.exception.JWTException)9 LocalDateTime (java.time.LocalDateTime)8 InvalidationException (io.hops.hopsworks.jwt.exception.InvalidationException)6 IOException (java.io.IOException)6 HashMap (java.util.HashMap)6 DecodedJWT (com.auth0.jwt.interfaces.DecodedJWT)5 JWTDecodeException (com.auth0.jwt.exceptions.JWTDecodeException)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 NoSuchAlgorithmException (java.security.NoSuchAlgorithmException)3 AirflowException (io.hops.hopsworks.exceptions.AirflowException)2 ServiceException (io.hops.hopsworks.exceptions.ServiceException)2 MaterializedJWTID (io.hops.hopsworks.persistence.entity.airflow.MaterializedJWTID)2 Project (io.hops.hopsworks.persistence.entity.project.Project)2 Path (java.nio.file.Path)2 ArrayList (java.util.ArrayList)2 Date (java.util.Date)2