package nxm.sys.intr;

import nxm.sys.lib.Intrinsic;
import nxm.sys.lib.SSLUtilities;
import nxm.sys.lib.Table;

/**
 * Command used for attribute based authentication
 * <pre>
 * - Thick-client (NeXtMidas-based) application
 * - User with PKI connects to a (NeXtMidas-based) service using an SSL
 *   socket with their PKI (NOT YET IMPLEMETED)
 * </pre>
 *
 * This is to be used after the SSL is Configured
 *
 * @author Jeanette McCall
 * @since NextMidas 3.7.3 (Note: This command first appeared in the NeXtMidas
 * 3.7.2 release as a "hidden" command.  However, major changes were made
 * before the NeXtMidas 3.7.3 release where it was first considered part of
 * the "public" API for NeXtMidas.)
 *
 */
public class Checkauthorization extends Intrinsic{

  private String userDN;
  private Table  attrValueTbl;
  private String project;
  private String group;
  private String jsonResponse;
  private String jsonPathProps;
  private boolean allowPartialMatch;
  private boolean authorized;

  private boolean DEBUG = false;

  /** default constructor */
  public Checkauthorization () {}

  public int open () {

    authorized = false;

    // Result name for result of attribute check
    String label = MA.getU("LABEL","AUTHORIZED");

    // User's Distinguished Name from certificate
    userDN = MA.getCS("USERDN"); // covers case 3 or case 2 if separate command

    // Attributes that are to ALL be VALID in order for Access to be granted
    // Note: If another style check (i.e. OR rather than AND) is desired, the
    // static methods in SSLUtilities can be called directly
    attrValueTbl = MA.getTable("USERATTR",null);
    if (DEBUG && attrValueTbl != null) {
      M.info("____Attributelogin: attrValueTbl: "+attrValueTbl.toString());
    }

    // Allow the attribute value to just be contained
    // (i.e. a sub-string of) the JASON value
    allowPartialMatch = MA.getState("/ALLOWPARTIAL", false);

    project = MA.getCS("PROJECT");
    group = MA.getCS("GROUP");
    jsonResponse = MA.getCS("JSONRESPONSE");
    jsonPathProps = MA.getCS("JSONPATHPROPS");

    if (!jsonResponse.isEmpty()) {
      // If a JSON response is provided, assume we are validating against that
      if (!jsonPathProps.isEmpty()) SSLUtilities.overideJsonKeyPaths(jsonPathProps);
      authorized =
          SSLUtilities.verifyJsonHasAttributes(jsonResponse,attrValueTbl,allowPartialMatch);
    }
    else if (!project.isEmpty() || !group.isEmpty()) {
      // If project or group is specified assume this is a project/group
      // validation
      if (project.isEmpty() || group.isEmpty()) {
        M.error("Attributelogin PROJ_GROUP: must specify project and group to "
            + "authenticate against");
        return FINISH;
      }
      if (DEBUG) M.info("____Attributelogin: group: "+group+
          " project: "+project);

      if (DEBUG) M.info("____Attributelogin userDN:"+userDN+" group:"+group
          +" project:"+project);

      authorized = SSLUtilities.verifyGroupProject(userDN,group,project);

      if (DEBUG) M.info("____Attributelogin: verified: "+authorized);

    }
    // Assume this is a validation based on user attribute query
    else {
      CharSequence clientAttributes = SSLUtilities.getClientAttributes(userDN);
      if (DEBUG) M.info("____Attributelogin: clientAttributes:"+clientAttributes);

      boolean noAttributeFailures = true;

      // All attributes have to match for access
      if (attrValueTbl != null) {
        for (String attr: attrValueTbl) {
          boolean thisAttributeIsValid = SSLUtilities
              .verifyUserAttribute(clientAttributes, attr,
                  attrValueTbl.get(attr), allowPartialMatch);
          if (DEBUG) M.info("____Attributelogin: attr:"+attr+" value:"+
                  attrValueTbl.get(attr)+" valid:"+thisAttributeIsValid);
          if (!thisAttributeIsValid) {
            noAttributeFailures = false;
            break;
          }
        }
        authorized = noAttributeFailures;
      }
      else {
        // validation is based just on the user attributes existing
        authorized =  ((clientAttributes != null) &&
            (clientAttributes.length() == 0));
      }
    }

    if (label != null) {
      MR.put(label, authorized);
    }

    return FINISH;
  }


}
