/*
* Copyright (C) 2013 Intel Corporation
* All rights reserved.
*/
package com.intel.mtwilson.shiro.jaxrs;
import com.intel.mtwilson.shiro.EncryptedTokenContent;
import com.intel.dcsg.cpg.authz.token.TokenFactory;
import com.intel.mtwilson.jaxrs2.mediatype.DataMediaType;
import com.intel.mtwilson.launcher.ws.ext.V2;
import com.intel.mtwilson.shiro.authc.password.LoginPasswordId;
import com.intel.mtwilson.shiro.UserId;
import com.intel.mtwilson.shiro.Username;
import com.thoughtworks.xstream.XStream;
import java.security.GeneralSecurityException;
import java.util.Collection;
import java.util.Iterator;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.BeanParam;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response.Status;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.annotation.RequiresGuest;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
/**
* The token generated by this class contains the following information: UserId
* Username LoginPasswordId
*
* These correspond to the principals that are set by the JdbcPasswordRealm
* against which we are authenticating the user.
*
* We explicitly serialize these three values and then reconstruct the
* principals from them as necessary because the authorization token should not
* allow arbitrary (blind) reconstruction of principals, or else an attacker who
* compromises the authorization token key would be able to construct any set of
* principals and pass them to the server in an attack token and elevate
* privileges. By explicitly storing the LoginPasswordId, UserId, and Username
* we limit any hacking of the token to the same privileges that would have been
* available if the attacker had stolen the user's password.
*
* @author jbuhacoff
*/
@V2
@Path("/login")
public class PasswordLogin {
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(PasswordLogin.class);
// private TokenFactory factory;
@RequiresGuest
@POST
@Consumes({MediaType.APPLICATION_FORM_URLENCODED})
// public void submitLoginForm(@Context final HttpServletRequest request, @Context final HttpServletResponse response, @FormParam("username") String username, @FormParam("password") String password) {
public void submitLoginForm(@Context final HttpServletRequest request, @Context final HttpServletResponse response, @BeanParam PasswordLoginRequest passwordLoginRequest) throws GeneralSecurityException {
// log.debug("submitLoginForm username {} password {}", username, password);
log.debug("submitLoginForm beanparam username {} password {}", passwordLoginRequest.getUsername(), passwordLoginRequest.getPassword());
log.debug("request from {}", request.getRemoteHost());
PasswordLoginResponse passwordLoginResponse = loginRequest(request, response, passwordLoginRequest);
log.debug("Successfully processed login request with auth token {}.", passwordLoginResponse.getAuthorizationToken());
}
@RequiresGuest
@POST
@Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, DataMediaType.APPLICATION_YAML, DataMediaType.TEXT_YAML})
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, DataMediaType.APPLICATION_YAML, DataMediaType.TEXT_YAML})
public PasswordLoginResponse loginRequest(@Context final HttpServletRequest request, @Context final HttpServletResponse response, PasswordLoginRequest loginForm) throws GeneralSecurityException {
log.debug("loginRequest username {} password {}", loginForm.getUsername(), loginForm.getPassword());
log.debug("request from {}", request.getRemoteHost());
// authenticate the user with JdbcPasswordRealm and PasswordCredentialsMatcher (configured in shiro.ini)
Subject currentUser = SecurityUtils.getSubject();
// if( !currentUser.isAuthenticated() ) { // shouldn't need this because we have @RequiresGuest annotation...
log.debug("authenticating...");
// for this junit test we're using mtwilson.api.username and mtwilson.api.password properties from mtwilson.properties on the local system, c:/mtwilson/configuration/mtwilson.properties is default location on windows
UsernamePasswordToken loginToken = new UsernamePasswordToken(loginForm.getUsername(), loginForm.getPassword());
// UsernamePasswordToken token = new UsernamePasswordToken("root", "root"); // guest doesn't need a password
loginToken.setRememberMe(false); // we could pass in a parameter with the form but we don't need this
currentUser.login(loginToken); // throws UnknownAccountException , IncorrectCredentialsException , LockedAccountException , other specific exceptions, and AuthenticationException
if (!currentUser.isAuthenticated()) {
throw new WebApplicationException(Status.UNAUTHORIZED);
}
log.info("logged in as {}", currentUser.getPrincipal());
PrincipalCollection principals = currentUser.getPrincipals();
Collection<Username> usernames = principals.byType(Username.class);
Collection<UserId> userIds = principals.byType(UserId.class);
Collection<LoginPasswordId> loginPasswordIds = principals.byType(LoginPasswordId.class);
Username username = getFirstElementFromCollection(usernames);
UserId userId = getFirstElementFromCollection(userIds);
LoginPasswordId loginPasswordId = getFirstElementFromCollection(loginPasswordIds);
if ( username == null || userId == null || loginPasswordId == null ) {
log.error("One of the required parameters is missing. Login request cannot be processed");
throw new IllegalStateException();
}
// this block of code repeated in EncryptedTokenAuthenticationFilter
EncryptedTokenContent tokenContent = new EncryptedTokenContent();
tokenContent.loginPasswordId = loginPasswordId.getLoginPasswordId().toString(); // passwordLoginIds.iterator().next().getLoginPasswordId().toString();
tokenContent.userId = userId.getUserId().toString(); // userIds.iterator().next().getUserId().toString();
tokenContent.username = username.getUsername(); // usernames.iterator().next().getUsername();
XStream xs = new XStream();
String tokenContentXml = xs.toXML(tokenContent);
log.debug("tokenContent xml: {}", tokenContentXml);
// create the token
TokenFactory factory = new TokenFactory();
String authorizationToken = factory.create(tokenContentXml);
// include token in response headers
response.addHeader("Authorization-Token", authorizationToken);
PasswordLoginResponse passwordLoginResponse = new PasswordLoginResponse();
passwordLoginResponse.setAuthorizationToken(authorizationToken);
return passwordLoginResponse;
}
private <T> T getFirstElementFromCollection(Collection<T> collection) {
if( collection != null ) {
Iterator<T> iterator = collection.iterator();
if (iterator.hasNext()) {
return iterator.next();
}
}
return null;
}
}