Skip to content
This repository has been archived by the owner on Nov 9, 2017. It is now read-only.

Commit

Permalink
refactor ZanataRestSecurityInterceptor
Browse files Browse the repository at this point in the history
  • Loading branch information
Patrick Huang committed May 24, 2016
1 parent 933d111 commit 576fc0b
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 24 deletions.
Expand Up @@ -23,12 +23,14 @@
import org.apache.oltu.oauth2.rs.response.OAuthRSResponse;
import org.jboss.resteasy.annotations.interception.SecurityPrecedence;
import org.zanata.model.HAccount;
import org.zanata.rest.oauth.OAuthUtil;
import org.zanata.security.SecurityFunctions;
import org.zanata.security.ZanataIdentity;
import org.zanata.security.annotations.Authenticated;
import org.zanata.security.oauth.SecurityTokens;
import org.zanata.util.HttpUtil;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.MoreObjects;
import com.google.common.base.Strings;
import com.googlecode.totallylazy.Either;
import lombok.extern.slf4j.Slf4j;
Expand Down Expand Up @@ -73,34 +75,37 @@ public void filter(ContainerRequestContext context)
return;
}

String username = HttpUtil.getUsername(context.getHeaders());
String apiKey = HttpUtil.getApiKey(context.getHeaders());
Tokens tokens = new Tokens(context, request);

if (StringUtils.isNotEmpty(username) && StringUtils.isNotEmpty(apiKey)) {
if (!tokens.canAuthenticate() &&
SecurityFunctions.doesRestPathAllowAnonymousAccess(
context.getMethod(), context.getUriInfo().getPath())) {
// if we don't have any information to authenticate but the
// requesting API allows anonymous access, we let it go
return;
}

if (tokens.canAuthenticateUsingApiKey()) {
// if apiKey presents, we use apiKey for security check
zanataIdentity.getCredentials().setUsername(username);
zanataIdentity.setApiKey(apiKey);
zanataIdentity.getCredentials().setUsername(tokens.username.get());
zanataIdentity.setApiKey(tokens.apiKey.get());
zanataIdentity.tryLogin();
} else {
Either<String, Response> usernameOrResponse =
getUsernameOrResponse();
if (usernameOrResponse.isRight()) {
if (SecurityFunctions
.canAccessRestPath(zanataIdentity, context.getMethod(),
context.getUriInfo().getPath())) {
return;
}
context.abortWith(usernameOrResponse.right());
return;
}
username = usernameOrResponse.left();
String username = usernameOrResponse.left();
zanataIdentity.getCredentials().setUsername(username);
zanataIdentity.setOAuthRequest(true);
zanataIdentity.tryLogin();
}

zanataIdentity.tryLogin();
if (!SecurityFunctions.canAccessRestPath(zanataIdentity,
context.getMethod(), context.getUriInfo().getPath())) {
log.info(InvalidApiKeyUtil.getMessage(username, apiKey));
log.info("can not authenticate REST request: {}", tokens);
context.abortWith(Response.status(Status.UNAUTHORIZED)
.entity("Invalid token")
.build());
Expand Down Expand Up @@ -171,4 +176,48 @@ private Response buildUnauthorizedResponse(String message) {
private Response buildServerErrorResponse(String message) {
return Response.serverError().entity(message).build();
}

private static class Tokens {

private final Optional<String> username;
private final Optional<String> apiKey;
private final Optional<String> accessToken;
private final Optional<String> authorizationCode;

Tokens(ContainerRequestContext context, HttpServletRequest request) {
String username = HttpUtil.getUsername(context.getHeaders());
String apiKey = HttpUtil.getApiKey(context.getHeaders());
this.username = optionalNotEmptyString(username);
this.apiKey = optionalNotEmptyString(apiKey);
accessToken = OAuthUtil.getAccessToken(request);
String authorizationCode = request.getParameter(OAuth.OAUTH_CODE);
this.authorizationCode = optionalNotEmptyString(authorizationCode);
}

private static Optional<String> optionalNotEmptyString(String value) {
return StringUtils.isNotEmpty(value) ? Optional.of(value) : Optional.empty();
}

boolean canAuthenticateUsingApiKey() {
return username.isPresent() && apiKey.isPresent();
}

boolean canAuthenticateUsingOAuth() {
return authorizationCode.isPresent() || accessToken.isPresent();
}

boolean canAuthenticate() {
return canAuthenticateUsingApiKey() || authorizationCode.isPresent() || accessToken.isPresent();
}

@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("username", username)
.add("apiKey", apiKey)
.add("accessToken", accessToken)
.add("authorizationCode", authorizationCode)
.toString();
}
}
}
25 changes: 14 additions & 11 deletions zanata-war/src/main/java/org/zanata/security/SecurityFunctions.java
Expand Up @@ -42,9 +42,7 @@
import org.zanata.util.ServiceLocator;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.ws.rs.HttpMethod;

import java.util.Optional;

Expand Down Expand Up @@ -568,19 +566,23 @@ public boolean canDownloadTMX() {
public static boolean canAccessRestPath(
@Nonnull ZanataIdentity identity,
String httpMethod, String restServicePath) {
// This is to allow data injection for function-test/rest-test
if (isTestServicePath(restServicePath)) {
log.debug("Allow rest access for Zanata test");
return true;
}
if (identity.isLoggedIn()) {
if (doesRestPathAllowAnonymousAccess(httpMethod, restServicePath)) {
return true;
}
return isRestPathAllowedAnonymousAccess(httpMethod);
return identity.isLoggedIn();
}

private static boolean isRestPathAllowedAnonymousAccess(String httpMethod) {
return HttpUtil.isReadMethod(httpMethod);
/**
* Check if the REST api allow anonymous access
* @param httpMethod {@link javax.ws.rs.HttpMethod}
* @param servicePath service path of rest request.
* See annotation @Path in REST service class.
* @return
*/
public static boolean doesRestPathAllowAnonymousAccess(String httpMethod,
String servicePath) {
return HttpUtil.isReadMethod(httpMethod) ||
isTestServicePath(servicePath);
}

/**
Expand All @@ -590,6 +592,7 @@ private static boolean isRestPathAllowedAnonymousAccess(String httpMethod) {
* See annotation @Path in REST service class.
*/
private static boolean isTestServicePath(String servicePath) {
// This is to allow data injection for function-test/rest-test
return servicePath != null
&& (
// when being called in RestLimitingFilter
Expand Down

0 comments on commit 576fc0b

Please sign in to comment.