Extend Waffle to Limit Which Window User can Access


We want to use windows integrated authentication to authenticate user and only allow user who started the application to access it. 

We can easily implement this by extending Waffle.
Waffle can do windows integrated authentication for us, after that we just need check whether the user name and domain of the logged-on user is same as the account who starts the application.
Implementation Code
After waffle.servlet.NegotiateSecurityFilter, the following filter would check whether user name and domain of the remote user and the user who starts the web application matches.
The complete code can be found at Github.
We can get the user who starts the web application by the following code:
NTSystem system = new NTSystem();
String runasUser = system.getName();
String runasDomain = system.getDomain();

There are several ways to get user name and domain of remote logged-on user:
1.  waffle.servlet.NegotiateSecurityFilter save waffle.servlet.WindowsPrincipal instance in session. We can get user name and domain info from WindowsPrincipal.
request.getSession().setAttribute(PRINCIPAL_SESSION_KEY,windowsPrincipal);
2.  waffle.servlet.NegotiateSecurityFilter add windowsPrincipal into subject, and save subject into session. Form subject instance, we can get needed info.
subject.getPrincipals().add(windowsPrincipal);
session.setAttribute("javax.security.auth.subject", subject);

package src.main.java.org.codeexample.jeffery.misc.waffle;

import waffle.servlet.NegotiateSecurityFilter;
import waffle.servlet.WindowsPrincipal;
import com.sun.jna.platform.win32.Secur32;
import com.sun.jna.platform.win32.Secur32Util;
import com.sun.security.auth.module.NTSystem;

public class OnlyAllowUserStartItFilter implements Filter {
  protected static final Logger logger = LoggerFactory
      .getLogger(NegotiateSecurityFilter.class);
  private static final String PRINCIPAL_SESSION_KEY = NegotiateSecurityFilter.class
      .getName() + ".PRINCIPAL";
  
  @Override
  public void doFilter(ServletRequest sreq, ServletResponse sres,
      FilterChain chain) throws IOException, ServletException {
    HttpServletRequest request = (HttpServletRequest) sreq;
    boolean valid = false;    
    HttpSession session = request.getSession(false);
    if (session != null) {
      WindowsPrincipal winPrincipal = (WindowsPrincipal) session
          .getAttribute(PRINCIPAL_SESSION_KEY);
      valid = validateRemoteUser(winPrincipal);
    }
    if (!valid) {
      sendUnauthorized(sres);
    } else {
      chain.doFilter(sreq, sres);
    }
  }
  private boolean validateRemoteUserViaWinPrincipal(Subject subject) {
    boolean valid = false;
    Set<Principal> principals = subject.getPrincipals();
    WindowsPrincipal winPrincipal = null;
    for (Principal principal : principals) {
      if (principal instanceof WindowsPrincipal) {
        winPrincipal = (WindowsPrincipal) principal;
      }
    }
    valid = validateRemoteUser(winPrincipal);
    return valid;
  }
  private boolean validateRemoteUser(WindowsPrincipal winPrincipal) {
    boolean valid = false;
    if (winPrincipal != null) {
      String fqn = winPrincipal.getName();
      int atIdx = fqn.indexOf('\\');
      String remoteDomain = null, remoteUser = null;
      if (atIdx > -1) {
        remoteDomain = fqn.substring(0, atIdx);
        remoteUser = fqn.substring(atIdx + 1);
      } else {
        remoteUser = fqn;
      }
      NTSystem system = new NTSystem();
      valid = validDomain(remoteDomain, system)
          && validateUser(remoteUser, system);
    }
    return valid;
  }
  private boolean validateRemoteUserViaSecur32() {
    boolean valid = false;
    String remoteUserInfo = Secur32Util
        .getUserNameEx(Secur32.EXTENDED_NAME_FORMAT.NameSamCompatible);
    if (remoteUserInfo != null) {
      String remoteDomain = null, remoteUser = null;
      if (atIdx > -1) {
        remoteDomain = remoteUserInfo.substring(0, atIdx);
        remoteUser = remoteUserInfo.substring(atIdx + 1);
      } else {
        remoteUser = remoteUserInfo;
      }
      
      NTSystem system = new NTSystem();
      valid = validDomain(remoteDomain, system)
          && validateUser(remoteUser, system);
    }
    return valid;
  }
  
  private boolean validateUser(String remoteUser, NTSystem system) {
    boolean valid = false;
    String runasUser = system.getName();
    if (runasUser != null) {
      if (runasUser.equals(remoteUser)) {
        valid = true;
      }
    } else {
      // this is unlikely to happen
      logger.error("runasUser is null, remoteUser: " + remoteUser);
      if (remoteUser == null) {
        valid = true;
      }
    }
    return valid;
  }
  private boolean validDomain(String remoteDomain, NTSystem system) {
    boolean valid = false;
    String runasDomain = system.getDomain();
    if (runasDomain != null) {
      if (runasDomain.equalsIgnoreCase(remoteDomain)) {
        valid = true;
      }
    } else {
      if (remoteDomain == null) {
        valid = true;
      }
    }
    return valid;
  }
  private void sendUnauthorized(ServletResponse sres) throws IOException {
    HttpServletResponse response = (HttpServletResponse) sres;
    response.setHeader("Connection", "close");
    response.sendError(HttpServletResponse.SC_UNAUTHORIZED,
        "This application can be only accessed by user who started it.");
    response.flushBuffer();
  }
}
Define waffle.servlet.NegotiateSecurityFilter and OnlyAllowUserStartItFilter
Next, we need define these 2 filters in web.xml, we need make sure we first define waffle.servlet.NegotiateSecurityFilter, then define OnlyAllowUserStartItFilter, to make NegotiateSecurityFilter run first, then run OnlyAllowUserStartItFilter.

As we are using jetty, and all applications in the jetty need this feature, we define these 2 filters in our own webdefault.xml.

Labels

adsense (5) Algorithm (69) Algorithm Series (35) Android (7) ANT (6) bat (8) Big Data (7) Blogger (14) Bugs (6) Cache (5) Chrome (19) Code Example (29) Code Quality (7) Coding Skills (5) Database (7) Debug (16) Design (5) Dev Tips (63) Eclipse (32) Git (5) Google (33) Guava (7) How to (9) Http Client (8) IDE (7) Interview (88) J2EE (13) J2SE (49) Java (186) JavaScript (27) JSON (7) Learning code (9) Lesson Learned (6) Linux (26) Lucene-Solr (112) Mac (10) Maven (8) Network (9) Nutch2 (18) Performance (9) PowerShell (11) Problem Solving (11) Programmer Skills (6) regex (5) Scala (6) Security (9) Soft Skills (38) Spring (22) System Design (11) Testing (7) Text Mining (14) Tips (17) Tools (24) Troubleshooting (29) UIMA (9) Web Development (19) Windows (21) xml (5)