All Downloads are FREE. Search and download functionalities are using the official Maven repository.

edu.uiuc.ncsa.sas.SASServlet Maven / Gradle / Ivy

package edu.uiuc.ncsa.sas;

import edu.uiuc.ncsa.sas.client.ClientProvider;
import edu.uiuc.ncsa.sas.client.SASClient;
import edu.uiuc.ncsa.sas.example.EchoExecutable;
import edu.uiuc.ncsa.sas.loader.SASExceptionHandler;
import edu.uiuc.ncsa.sas.thing.action.*;
import edu.uiuc.ncsa.sas.thing.response.*;
import edu.uiuc.ncsa.security.core.Identifier;
import edu.uiuc.ncsa.security.core.exceptions.GeneralException;
import edu.uiuc.ncsa.security.core.exceptions.NFWException;
import edu.uiuc.ncsa.security.core.exceptions.UnknownClientException;
import edu.uiuc.ncsa.security.servlet.AbstractServlet;
import edu.uiuc.ncsa.security.servlet.ExceptionHandlerThingie;
import edu.uiuc.ncsa.security.servlet.HeaderUtils;
import edu.uiuc.ncsa.security.util.crypto.KeyUtil;
import edu.uiuc.ncsa.security.util.jwk.JSONWebKeys;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.security.SecureRandom;
import java.security.interfaces.RSAPublicKey;
import java.util.*;

/**
 * 

Created by Jeff Gaynor
* on 8/15/22 at 8:29 AM */ public class SASServlet extends AbstractServlet { JSONWebKeys jsonWebKeys; @Override public void loadEnvironment() throws IOException { setEnvironment(getConfigurationLoader().load()); } protected SASEnvironment getSASE() { return (SASEnvironment) getEnvironment(); } String ACTION_TAG = "action="; String SESSION_TAG = "session_id="; String ID_TAG = "id="; @Override public void doGet(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws ServletException, IOException { throw new UnsupportedOperationException("This server does not support GET"); } protected LogonResponse doLogon(LogonAction logonAction, SessionRecord sessionRecord) throws IOException { // check they are a user UUID sessionID = UUID.randomUUID(); // have to figure out the public key size. int totalBytes = ((RSAPublicKey) sessionRecord.client.getPublicKey()).getModulus().bitLength() / 8; LogonResponse logonResponse = new LogonResponse(logonAction, sessionID, new byte[]{32, 64}); // Take the total bytes (limited by RSA key size), remove the response size without key, // estimate key. The 3/4 accounts for the amount of space lost to base 64 encoding. // System.out.println(getClass().getCanonicalName() + " stats:") // System.out.println(getClass().getCanonicalName() + " total bytes:" + totalBytes); // System.out.println(getClass().getCanonicalName() + " JSON =" + logonResponse.serialize().toString(1)); // System.out.println(getClass().getCanonicalName() + " JSON size=" + logonResponse.serialize().toString(1).length()); int keySize = (totalBytes - logonResponse.serialize().toString(1).length()) * 3 / 4; // System.out.println(getClass().getCanonicalName() + " key size:" + keySize); // System.out.println(getClass().getSimpleName() + ": s key size =" + keySize); byte[] sKey = KeyUtil.generateSKey(keySize); //1024 bits, probably. logonResponse = new LogonResponse(logonAction, sessionID, sKey); sessionRecord.executable = createExecutable(); sessionRecord.sKey = sKey; sessionRecord.sessionID = sessionID; sessions.put(sessionID, sessionRecord); return logonResponse; } public Executable createExecutable() { return new EchoExecutable(); // Just to demo this. Write your own and override. } protected LogoffResponse doLogoff(SASClient client, LogoffAction logoffAction, HttpServletResponse response, SessionRecord sessionRecord, String message) throws IOException { LogoffResponse logoffResponse = new LogoffResponse(logoffAction, message); //getSATE().getResponseSerializer().serialize(logoffResponse, response, sessionRecord); sessions.remove(sessionRecord.sessionID); return logoffResponse; } @Override protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { throw new UnsupportedOperationException("This server does not support DELETE"); } @Override public void doPost(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws ServletException, IOException { try { doIt(httpServletRequest, httpServletResponse); } catch (Throwable e) { e.printStackTrace(); handleException(new SASExceptionHandlerThingie(e, httpServletRequest, httpServletResponse, null)); } } @Override protected void doIt(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Throwable { // Either there is basic auth (to do logon) or there is a session id. The payload is always a blob. String rawSessionID = httpServletRequest.getHeader(SASConstants.HEADER_SESSION_ID); Identifier clientID = null; UUID sessionID = null; boolean isLogon = false; if (rawSessionID == null) { clientID = HeaderUtils.getIDFromHeaders(httpServletRequest); isLogon = true; } else { sessionID = UUID.fromString(rawSessionID); } SASClient client = null; if (clientID != null) { if (!ClientProvider.isClientID(clientID.getUri().toString())) { throw new UnknownClientException("unknown client \"" + clientID + "\""); } if (!getSASE().getClientStore().containsKey(clientID)) { throw new UnknownClientException("unknown client \"" + clientID + "\""); } client = getSASE().getClientStore().get(clientID); /* doLogon(client, httpServletResponse); return;*/ } // If it gets to here, then this is a pending session. SessionRecord sessionRecord = null; if (sessionID != null) { sessionRecord = sessions.get(sessionID); } else { sessionRecord = new SessionRecord(client, null); } // From this point forward, we can do secure error responses. Up until here we don't // have the key, etc. try { List actions; if (isLogon) { actions = getSASE().getRequestDeserializer().rsaDeserialize(sessionRecord, httpServletRequest); } else { actions = getSASE().getRequestDeserializer().sDeserialize(sessionRecord, httpServletRequest); } List responses = new ArrayList<>(); for (int i = 0; i < actions.size(); i++) { Response response = null; switch (actions.get(i).getType()) { case SASConstants.ACTION_LOGON: response = doLogon((LogonAction) actions.get(i), sessionRecord); break; case SASConstants.ACTION_LOGOFF: response = doLogoff(client, (LogoffAction) actions.get(i), httpServletResponse, sessionRecord, "log off"); break; case SASConstants.ACTION_NEW_KEY: response = doNewKey(client, (NewKeyAction) actions.get(i), httpServletResponse, sessionRecord); break; default: case SASConstants.ACTION_EXECUTE: response = doExecute(sessionRecord, actions.get(i)); break; } responses.add(response); } sessionRecord.lastAccessed = new Date(); getSASE().getResponseSerializer().serialize(responses, httpServletResponse, sessionRecord); } catch (Throwable t) { handleException(new SASExceptionHandlerThingie(t, httpServletRequest, httpServletResponse, sessionRecord)); } } protected Response doNewKey(SASClient client, NewKeyAction newKeyAction, HttpServletResponse httpServletResponse, SessionRecord sessionRecord) { SecureRandom secureRandom = new SecureRandom(); byte[] key = new byte[newKeyAction.getSize()]; secureRandom.nextBytes(key); return new NewKeyResponse(newKeyAction, key); } /** * Invoke a specific method in the {@link Executable} * * @param sessionRecord * @param invokeAction * @return */ protected OutputResponse invoke(SessionRecord sessionRecord, InvokeAction invokeAction) { Executable exe = sessionRecord.executable; exe.getIO().flush(); sessionRecord.executable.execute(invokeAction); sessionRecord.lastAccessed = new Date(); StringBuilder output = ((StringIO) exe.getIO()).getOutput(); return new OutputResponse(invokeAction, output.toString()); } Map sessions = new HashMap<>(); protected Response doExecute(SessionRecord sessionRecord, Action action) { Executable exe = sessionRecord.executable; if (exe == null) { throw new GeneralException("Session with id " + sessionRecord.sessionID + " not found"); } exe.getIO().flush(); Response r = exe.execute(action); sessionRecord.lastAccessed = new Date(); return r; } protected void handleException(ExceptionHandlerThingie xh) throws IOException, ServletException { Throwable t = xh.throwable; if (t instanceof NFWException) { error("INTERNAL ERROR: " + (t.getMessage() == null ? "(no message)" : t.getMessage()), t); // log it appropriately } // ok, if it is a strange error, print a stack if you need to. getExceptionHandler().handleException((SASExceptionHandlerThingie) xh); } public static class SASExceptionHandlerThingie extends ExceptionHandlerThingie { public SASExceptionHandlerThingie(Throwable throwable, HttpServletRequest request, HttpServletResponse response, SessionRecord sessionRecord) { super(throwable, request, response); this.sessionRecord = sessionRecord; } public boolean hasSessionRecord() { return sessionRecord != null; } public SessionRecord sessionRecord; } @Override public SASExceptionHandler getExceptionHandler() { return (SASExceptionHandler) super.getExceptionHandler(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy