package com.monkeyk.os.oauth.authorize;
import com.monkeyk.os.web.WebUtils;
import com.monkeyk.os.oauth.OAuthAuthxRequest;
import com.monkeyk.os.oauth.OAuthHandler;
import com.monkeyk.os.oauth.validator.AbstractClientDetailsValidator;
import org.apache.oltu.oauth2.as.response.OAuthASResponse;
import org.apache.oltu.oauth2.common.error.OAuthError;
import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
import org.apache.oltu.oauth2.common.message.OAuthResponse;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import static com.monkeyk.os.domain.oauth.Constants.*;
/**
* 2015/6/25
*
* @author Shengzhao Li
*/
public abstract class AbstractAuthorizeHandler extends OAuthHandler {
private static final Logger LOG = LoggerFactory.getLogger(AbstractAuthorizeHandler.class);
protected OAuthAuthxRequest oauthRequest;
protected HttpServletResponse response;
protected boolean userFirstLogged = false;
protected boolean userFirstApproved = false;
public AbstractAuthorizeHandler(OAuthAuthxRequest oauthRequest, HttpServletResponse response) {
this.oauthRequest = oauthRequest;
this.response = response;
}
protected boolean validateFailed() throws OAuthSystemException {
AbstractClientDetailsValidator validator = getValidator();
LOG.debug("Use [{}] validate client: {}", validator, oauthRequest.getClientId());
final OAuthResponse oAuthResponse = validator.validate();
return checkAndResponseValidateFailed(oAuthResponse);
}
protected abstract AbstractClientDetailsValidator getValidator();
protected boolean checkAndResponseValidateFailed(OAuthResponse oAuthResponse) {
if (oAuthResponse != null) {
LOG.debug("Validate OAuthAuthzRequest(client_id={}) failed", oauthRequest.getClientId());
WebUtils.writeOAuthJsonResponse(response, oAuthResponse);
return true;
}
return false;
}
protected String clientId() {
return oauthRequest.getClientId();
}
protected boolean isUserAuthenticated() {
final Subject subject = SecurityUtils.getSubject();
return subject.isAuthenticated();
}
protected boolean isNeedUserLogin() {
return !isUserAuthenticated() && !isPost();
}
protected boolean goApproval() throws ServletException, IOException {
if (userFirstLogged && !clientDetails().trusted()) {
//go to approval
LOG.debug("Go to oauth_approval, clientId: '{}'", clientDetails().getClientId());
final HttpServletRequest request = oauthRequest.request();
request.getRequestDispatcher(OAUTH_APPROVAL_VIEW)
.forward(request, response);
return true;
}
return false;
}
//true is submit failed, otherwise return false
protected boolean submitApproval() throws IOException, OAuthSystemException {
if (isPost() && !clientDetails().trusted()) {
//submit approval
final HttpServletRequest request = oauthRequest.request();
final String oauthApproval = request.getParameter(REQUEST_USER_OAUTH_APPROVAL);
if (!"true".equalsIgnoreCase(oauthApproval)) {
//Deny action
LOG.debug("User '{}' deny access", SecurityUtils.getSubject().getPrincipal());
responseApprovalDeny();
return true;
} else {
userFirstApproved = true;
return false;
}
}
return false;
}
protected void responseApprovalDeny() throws IOException, OAuthSystemException {
final OAuthResponse oAuthResponse = OAuthASResponse.errorResponse(HttpServletResponse.SC_FOUND)
.setError(OAuthError.CodeResponse.ACCESS_DENIED)
.setErrorDescription("User denied access")
.location(clientDetails().getRedirectUri())
.setState(oauthRequest.getState())
.buildQueryMessage();
LOG.debug("'ACCESS_DENIED' response: {}", oAuthResponse);
WebUtils.writeOAuthQueryResponse(response, oAuthResponse);
//user logout when deny
final Subject subject = SecurityUtils.getSubject();
subject.logout();
LOG.debug("After 'ACCESS_DENIED' call logout. user: {}", subject.getPrincipal());
}
protected boolean goLogin() throws ServletException, IOException {
if (isNeedUserLogin()) {
//go to login
LOG.debug("Forward to Oauth login by client_id '{}'", oauthRequest.getClientId());
final HttpServletRequest request = oauthRequest.request();
request.getRequestDispatcher(OAUTH_LOGIN_VIEW)
.forward(request, response);
return true;
}
return false;
}
//true is login failed, false is successful
protected boolean submitLogin() throws ServletException, IOException {
if (isSubmitLogin()) {
//login flow
try {
UsernamePasswordToken token = createUsernamePasswordToken();
SecurityUtils.getSubject().login(token);
LOG.debug("Submit login successful");
this.userFirstLogged = true;
return false;
} catch (Exception ex) {
//login failed
LOG.debug("Login failed, back to login page too", ex);
final HttpServletRequest request = oauthRequest.request();
request.setAttribute("oauth_login_error", true);
request.getRequestDispatcher(OAUTH_LOGIN_VIEW)
.forward(request, response);
return true;
}
}
return false;
}
private UsernamePasswordToken createUsernamePasswordToken() {
final HttpServletRequest request = oauthRequest.request();
final String username = request.getParameter(REQUEST_USERNAME);
final String password = request.getParameter(REQUEST_PASSWORD);
return new UsernamePasswordToken(username, password);
}
private boolean isSubmitLogin() {
return !isUserAuthenticated() && isPost();
}
protected boolean isPost() {
return RequestMethod.POST.name().equalsIgnoreCase(oauthRequest.request().getMethod());
}
public void handle() throws OAuthSystemException, ServletException, IOException {
//validate
if (validateFailed()) {
return;
}
//Check need usr login
if (goLogin()) {
return;
}
//submit login
if (submitLogin()) {
return;
}
// Check approval
if (goApproval()) {
return;
}
//Submit approval
if (submitApproval()) {
return;
}
//handle response
handleResponse();
}
//Handle custom response content
protected abstract void handleResponse() throws OAuthSystemException, IOException;
}