use of it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException in project SEPA by arces-wot.
the class LdapSecurityManager method isForTesting.
/*
* An identity is defined for testing if cn = TEST This entity should be used
* only for debugging. A testing identity is not deleted after registration:
* avoid in production!
*
* uid: uid ou: authorizedIdentities cn: SEPATest
*/
@Override
public boolean isForTesting(String uid) throws SEPASecurityException {
logger.log(Level.getLevel("ldap"), "[LDAP] isForTesting " + uid + " uid=" + uid + ",ou=authorizedIdentities," + prop.getBase(), "(objectclass=*)");
bind();
try {
cursor = ldap.search("uid=" + uid + ",ou=authorizedIdentities," + prop.getBase(), "(objectclass=*)", SearchScope.OBJECT, "*");
if (!cursor.next())
return false;
return cursor.get().get("cn").getString().equals("SEPATest");
} catch (LdapException | CursorException e) {
logger.error("[LDAP] isAuthorized exception " + e.getMessage());
throw new SEPASecurityException("isAuthorized exception " + e.getMessage());
} finally {
unbind();
}
}
use of it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException in project SEPA by arces-wot.
the class SecurityManager method getToken.
/**
* It requests a token to the Authorization Server. A token request should be
* made when the current token is expired or it is the first token. If the token
* is not expired, the "invalid_grant" error is returned.
*
* @param encodedCredentials the client credentials encoded using Base64
* @return JWTResponse in case of success, ErrorResponse otherwise
* @throws SEPASecurityException
* @see JWTResponse
* @see ErrorResponse
*
* <pre>
* POST https://wot.arces.unibo.it:8443/oauth/token
*
* Content-Type: application/x-www-form-urlencoded
* Accept: application/json
* Authorization: Basic Basic64(id:secret)
*
* Response example:
* {
* "access_token": "eyJraWQiOiIyN.........",
* "token_type": "bearer",
* "expires_in": 3600
* }
*
* Error response example:
* {
* "error":"Unless specified otherwise see RFC6749. Otherwise, this is specific of the SPARQL 1.1 SE Protocol",
* "error_description":"Unless specified otherwise, see RFC6749. Otherwise, this is specific to the SPARQL 1.1 SE Protocol", (OPTIONAL)
* "status_code" : the HTTP status code (should be 400 for all Oauth 2.0 errors).
* }
*
* According to RFC6749, the error member can assume the following values: invalid_request, invalid_client, invalid_grant, unauthorized_client, unsupported_grant_type, invalid_scope.
*
* invalid_request
* The request is missing a required parameter, includes an
* unsupported parameter value (other than grant type),
* repeats a parameter, includes multiple credentials,
* utilizes more than one mechanism for authenticating the
* client, or is otherwise malformed.
*
* invalid_client
* Client authentication failed (e.g., unknown client, no
* client authentication included, or unsupported
* authentication method). The authorization server MAY
* return an HTTP 401 (Unauthorized) status code to indicate
* which HTTP authentication schemes are supported. If the
* client attempted to authenticate via the "Authorization"
* request header field, the authorization server MUST
* respond with an HTTP 401 (Unauthorized) status code and
* include the "WWW-Authenticate" response header field
* matching the authentication scheme used by the client.
*
* invalid_grant
* The provided authorization grant (e.g., authorization
* code, resource owner credentials) or refresh token is
* invalid, expired, revoked, does not match the redirection
* URI used in the authorization request, or was issued to
* another client.
*
* unauthorized_client
* The authenticated client is not authorized to use this
* authorization grant type.
*
* unsupported_grant_type
* The authorization grant type is not supported by the
* authorization server.
* </pre>
*/
public synchronized Response getToken(String encodedCredentials) {
logger.debug("GET TOKEN");
// Decode credentials
byte[] decoded = null;
try {
decoded = Base64.getDecoder().decode(encodedCredentials);
} catch (IllegalArgumentException e) {
logger.error("Not authorized");
return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED, "invalid_request", e.getMessage());
}
// Parse credentials
String decodedCredentials = new String(decoded);
// BUG SOLVED. The clientID may contain ":"
// e.g., urn:epc:id:gid:0.1.410D23751450344850323220
int marker = decodedCredentials.lastIndexOf(":");
if (marker == -1) {
logger.error("Wrong Basic authorization");
return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED, "invalid_client", "Delimiter ':' is missing. Wrong credentials format: " + decodedCredentials);
}
// String[] clientID = decodedCredentials.split(":");
// if (clientID == null) {
// logger.error("Wrong Basic authorization");
// return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED, "invalid_client",
// "Client id not found: " + decodedCredentials);
// }
// if (clientID.length != 2) {
// logger.error("Wrong Basic authorization");
// return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED, "invalid_client",
// "Wrong credentials: " + decodedCredentials);
// }
// String id = decodedCredentials.split(":")[0];
// String secret = decodedCredentials.split(":")[1];
String id = decodedCredentials.substring(0, marker);
String secret = decodedCredentials.substring(marker + 1);
logger.debug("Credentials: " + id + " " + secret);
// Verify credentials
try {
if (!containsCredentials(id)) {
logger.error("Client id: " + id + " is not registered");
return new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "unauthorized_client", "Client identity " + id + " not found");
}
} catch (SEPASecurityException e2) {
return new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "unauthorized_client", e2.getMessage());
}
try {
if (!checkCredentials(id, secret)) {
logger.error("Wrong secret: " + secret + " for client id: " + id);
return new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "unauthorized_client", "Wrong credentials for identity " + id);
}
} catch (SEPASecurityException e2) {
return new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "unauthorized_client", e2.getMessage());
}
// Prepare JWT with claims set
JWTClaimsSet.Builder claimsSetBuilder = new JWTClaimsSet.Builder();
Date now = new Date();
// Check if the token is not expired
try {
if (containsJwt(id)) {
Date expiring = getTokenExpiringDate(id);
long expiringUnixSeconds = (expiring.getTime() / 1000) * 1000;
long nowUnixSeconds = (now.getTime() / 1000) * 1000;
long delta = expiringUnixSeconds - nowUnixSeconds;
// Expires if major than current time
logger.debug("ID: " + id + " ==> Token will expire in: " + delta + " ms");
if (delta > 0) {
logger.warn("Token is NOT EXPIRED. Return the current token.");
JWTResponse jwt = null;
try {
jwt = new JWTResponse(getJwt(id));
} catch (SEPASecurityException e) {
logger.error(e.getMessage());
return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "security_error", "Failed to retrieve expiring period");
}
return jwt;
}
logger.debug("Token is EXPIRED. Release a fresh token.");
}
} catch (SEPASecurityException e2) {
return new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "security_error", e2.getMessage());
}
/*
* 4.1.1. "iss" (Issuer) Claim
*
* The "iss" (issuer) claim identifies the principal that issued the JWT. The
* processing of this claim is generally application specific. The "iss" value
* is a case-sensitive string containing a StringOrURI value. Use of this claim
* is OPTIONAL.
*/
// try {
// claimsSetBuilder.issuer(auth.getIssuer());
// } catch (SEPASecurityException e1) {
// return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "invalid_issuer",
// e1.getMessage());
// }
/*
* 4.1.2. "sub" (Subject) Claim
*
* The "sub" (subject) claim identifies the principal that is the subject of the
* JWT. The Claims in a JWT are normally statements about the subject. The
* subject value MUST either be scoped to be locally unique in the context of
* the issuer or be globally unique. The processing of this claim is generally
* application specific. The "sub" value is a case-sensitive string containing a
* StringOrURI value. Use of this claim is OPTIONAL.
*/
claimsSetBuilder.subject(id);
/*
* 4.1.3. "aud" (Audience) Claim
*
* The "aud" (audience) claim identifies the recipients that the JWT is intended
* for. Each principal intended to process the JWT MUST identify itself with a
* value in the audience claim. If the principal processing the claim does not
* identify itself with a value in the "aud" claim when this claim is present,
* then the JWT MUST be rejected. In the general case, the "aud" value is an
* array of case- sensitive strings, each containing a StringOrURI value. In the
* special case when the JWT has one audience, the "aud" value MAY be a single
* case-sensitive string containing a StringOrURI value. The interpretation of
* audience values is generally application specific. Use of this claim is
* OPTIONAL.
*/
// ArrayList<String> audience = new ArrayList<String>();
// try {
// audience.add(auth.getHttpsAudience());
// } catch (SEPASecurityException e1) {
// return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "invalid_https_audience",
// e1.getMessage());
// }
// try {
// audience.add(auth.getWssAudience());
// } catch (SEPASecurityException e1) {
// return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "invalid_wss_audience",
// e1.getMessage());
// }
// claimsSetBuilder.audience(audience);
/*
* 4.1.4. "exp" (Expiration Time) Claim
*
* The "exp" (expiration time) claim identifies the expiration time on or after
* which the JWT MUST NOT be accepted for processing. The processing of the
* "exp" claim requires that the current date/time MUST be before the expiration
* date/time listed in the "exp" claim. Implementers MAY provide for some small
* leeway, usually no more than a few minutes, to account for clock skew. Its
* value MUST be a number containing a NumericDate value. Use of this claim is
* OPTIONAL.
*/
/*
* NOTICE: this date is serialized as SECONDS from UNIX time NOT milliseconds!
*/
// Define the expiration time
Date expires;
try {
expires = new Date(now.getTime() + (getTokenExpiringPeriod(id) * 1000));
} catch (SEPASecurityException e1) {
logger.error(e1.getMessage());
return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "security_error", "Failed to retrieve expiring period");
}
claimsSetBuilder.expirationTime(expires);
/*
* 4.1.5. "nbf" (Not Before) Claim
*
* The "nbf" (not before) claim identifies the time before which the JWT MUST
* NOT be accepted for processing. The processing of the "nbf" claim requires
* that the current date/time MUST be after or equal to the not-before date/time
* listed in the "nbf" claim. Implementers MAY provide for some small leeway,
* usually no more than a few minutes, to account for clock skew. Its value MUST
* be a number containing a NumericDate value. Use of this claim is OPTIONAL.
*/
// claimsSetBuilder.notBeforeTime(before);
/*
* 4.1.6. "iat" (Issued At) Claim
*
* The "iat" (issued at) claim identifies the time at which the JWT was issued.
* This claim can be used to determine the age of the JWT. Its value MUST be a
* number containing a NumericDate value. Use of this claim is OPTIONAL.
*/
claimsSetBuilder.issueTime(now);
/*
* 4.1.7. "jti" (JWT ID) Claim
*
* The "jti" (JWT ID) claim provides a unique identifier for the JWT. The
* identifier value MUST be assigned in a manner that ensures that there is a
* negligible probability that the same value will be accidentally assigned to a
* different data object; if the application uses multiple issuers, collisions
* MUST be prevented among values produced by different issuers as well. The
* "jti" claim can be used to prevent the JWT from being replayed. The "jti"
* value is a case- sensitive string. Use of this claim is OPTIONAL.
*/
// claimsSetBuilder.jwtID(id);
JWTClaimsSet jwtClaims = claimsSetBuilder.build();
// ******************************
// Sign JWT with private RSA key
// ******************************
SignedJWT signedJWT;
try {
signedJWT = new SignedJWT(new JWSHeader(JWSAlgorithm.RS256), JWTClaimsSet.parse(jwtClaims.toString()));
} catch (ParseException e) {
logger.error(e.getMessage());
return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "parsing_exception", "ParseException: " + e.getMessage());
}
try {
signedJWT.sign(signer);
} catch (JOSEException e) {
logger.error(e.getMessage());
return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "sign_exception", "JOSEException: " + e.getMessage());
}
// Add the token to the released tokens
try {
addJwt(id, signedJWT);
} catch (SEPASecurityException e1) {
logger.error(e1.getMessage());
return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "security_error", e1.getMessage());
}
JWTResponse jwt = null;
try {
jwt = new JWTResponse(signedJWT);
} catch (SEPASecurityException e) {
logger.error(e.getMessage());
return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "security_error", "Failed to retrieve expiring period");
}
logger.debug("Released token: " + jwt);
return jwt;
}
use of it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException in project SEPA by arces-wot.
the class Gate method authorize.
/**
* <pre>
* Specific to SPARQL 1.1 SE Subscribe request:
* 1. Check if the request contains an "authorization" member.
* 2. Check if the request contains an "authorization" member that start with "Bearer"
* 3. Check if the value of the "authorization" member is a JWT object ==> VALIDATE TOKEN
*
* Token validation:
* 4. Check if the JWT object is signed
* 5. Check if the signature of the JWT object is valid. This is to be checked with AS public signature verification key
* 6. Check the contents of the JWT object
* 7. Check if the value of "iss" is https://wot.arces.unibo.it:8443/oauth/token
* 8. Check if the value of "aud" contains https://wot.arces.unibo.it:8443/sparql
* 9. Accept the request as well as "sub" as the originator of the request and process it as usual
*
* Respond with 401 if not
*
* According to RFC6749, the error member can assume the following values: invalid_request, invalid_client, invalid_grant, unauthorized_client, unsupported_grant_type, invalid_scope.
*
* invalid_request
* The request is missing a required parameter, includes an
* unsupported parameter value (other than grant type),
* repeats a parameter, includes multiple credentials,
* utilizes more than one mechanism for authenticating the
* client, or is otherwise malformed.
*
* invalid_client
* Client authentication failed (e.g., unknown client, no
* client authentication included, or unsupported
* authentication method). The authorization server MAY
* return an HTTP 401 (Unauthorized) status code to indicate
* which HTTP authentication schemes are supported. If the
* client attempted to authenticate via the "Authorization"
* request header field, the authorization server MUST
* respond with an HTTP 401 (Unauthorized) status code and
* include the "WWW-Authenticate" response header field
* matching the authentication scheme used by the client.
*
* invalid_grant
* The provided authorization grant (e.g., authorization
* code, resource owner credentials) or refresh token is
* invalid, expired, revoked, does not match the redirection
* URI used in the authorization request, or was issued to
* another client.
*
* unauthorized_client
* The authenticated client is not authorized to use this
* authorization grant type.
*
* unsupported_grant_type
* The authorization grant type is not supported by the
* authorization server.
*
* </pre>
* @throws SEPASecurityException
*/
protected final ClientAuthorization authorize(String message) throws SEPASecurityException {
if (!authorizationRequired)
return new ClientAuthorization();
JsonObject request;
try {
request = new JsonParser().parse(message).getAsJsonObject();
} catch (Exception e) {
logger.error(e.getMessage());
return new ClientAuthorization("invalid_request", "Failed to parse JSON message: " + message);
}
String bearer = null;
JsonObject subUnsub = null;
if (request.has("subscribe"))
subUnsub = request.get("subscribe").getAsJsonObject();
else if (request.has("unsubscribe"))
subUnsub = request.get("unsubscribe").getAsJsonObject();
if (subUnsub == null) {
logger.error("Neither subscribe or unsuscribe found");
return new ClientAuthorization("invalid_request", "Neither subscribe or unsuscribe found");
}
if (!subUnsub.has("authorization")) {
logger.error("authorization member is missing");
return new ClientAuthorization("invalid_request", "authorization member is missing");
}
try {
bearer = subUnsub.get("authorization").getAsString();
} catch (Exception e) {
logger.error("Authorization member is not a string");
return new ClientAuthorization("invalid_request", "authorization member is not a string");
}
if (!bearer.toUpperCase().startsWith("BEARER ")) {
logger.error("Authorization value MUST be of type Bearer");
return new ClientAuthorization("unsupported_grant_type", "Authorization value MUST be of type Bearer");
}
String jwt = bearer.substring(7);
if (jwt == null) {
logger.error("Token is null");
return new ClientAuthorization("invalid_request", "Token is null");
}
if (jwt.equals("")) {
logger.error("Token is empty");
return new ClientAuthorization("invalid_request", "Token is empty");
}
// Token validation
return Dependability.validateToken(jwt);
}
use of it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException in project SEPA by arces-wot.
the class WebsocketServer method onMessage.
@Override
public void onMessage(WebSocket conn, String message) {
GateBeans.onMessage();
// Check path
if (!conn.getResourceDescriptor().equals(path)) {
logger.warn("@onMessage bad resource descriptor: " + conn.getResourceDescriptor() + " Use: " + path);
ErrorResponse response = new ErrorResponse(HttpStatus.SC_NOT_FOUND, "wrong_path", "Bad resource descriptor: " + conn.getResourceDescriptor() + " Use: " + path);
try {
conn.send(response.toString());
} catch (Exception e) {
logger.warn(e.getMessage());
}
return;
}
synchronized (gates) {
try {
if (gates.get(conn) != null)
gates.get(conn).onMessage(message);
else {
logger.error("Gate NOT FOUND: " + conn);
}
} catch (SEPAProtocolException | SEPASecurityException | SEPASparqlParsingException e) {
logger.error(e);
ErrorResponse response = new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "parsing failed", e.getMessage());
try {
conn.send(response.toString());
} catch (Exception e1) {
logger.warn(e1.getMessage());
}
}
}
}
use of it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException in project SEPA by arces-wot.
the class Processor method processUpdate.
public synchronized Response processUpdate(InternalUpdateRequest update) {
InternalUpdateRequest preRequest = update;
if (spuManager.doUpdateARQuadsExtraction(update)) {
try {
preRequest = ARQuadsAlgorithm.extractARQuads(update, queryProcessor);
} catch (SEPAProcessingException | SPARQL11ProtocolException | SEPASparqlParsingException e) {
return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "update_processing", e.getMessage());
}
}
// PRE-UPDATE processing
spuManager.subscriptionsProcessingPreUpdate(preRequest);
// Endpoint UPDATE
Response ret;
try {
ret = updateEndpoint(preRequest);
} catch (SEPASecurityException | IOException e) {
return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "sparql11endpoint", e.getMessage());
}
// STOP processing?
if (ret.isError()) {
logger.error("*** UPDATE ENDPOINT PROCESSING FAILED *** " + ret);
spuManager.abortSubscriptionsProcessing();
return ret;
}
// POST-UPDATE processing
spuManager.subscriptionsProcessingPostUpdate(ret);
return ret;
}
Aggregations