Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: edited erroneous code example

...

Code Block
languagejava
themeEclipse
titleSAM
collapsetrue
package org.my;

import java.io.IOException;
import java.security.Principal;
import java.util.Map;

import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.message.AuthException;
import javax.security.auth.message.AuthStatus;
import javax.security.auth.message.MessageInfo;
import javax.security.auth.message.MessagePolicy;
import javax.security.auth.message.callback.CallerPrincipalCallback;
import javax.security.auth.message.callback.GroupPrincipalCallback;
import javax.security.auth.message.module.ServerAuthModule;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;


public class SpAsBridgeSam implements ServerAuthModule {

    public static class MyOrgPrincipal implements Principal {

        public MyOrgPrincipal(String name) {
            this.name = name;
        }
        private final String name;
        @Override
        public String getName() {
            return name;
        }
	
	}

	private static final Class<?>[] SUPPORTED_MESSAGE_TYPES = new Class<?>[] { HttpServletRequest.class, HttpServletResponse.class };
    private static final String LOGIN_URL = "http://my.org/Shibboleth.sso/Login";
    private static final String LOGOUT_URL = "http://my.org/Shibboleth.sso/Logout";
    private boolean isProtectedResource;
    private CallbackHandler ch;

	// Post-construct initialization goes here
    @Override
    public void initialize(MessagePolicy requestPolicy, MessagePolicy responsePolicy, CallbackHandler ch, Map options) throws AuthException {
        this.ch = ch;
    	isProtectedResource = requestPolicy.isMandatory();
	}

	// Authentication logic and optional request pre-processing goes here
    @Override
    public AuthStatus validateRequest(MessageInfo mi, Subject client, Subject service) throws AuthException {
        HttpServletRequest hreq = (HttpServletRequest) mi.getRequestMessage();
        HttpServletResponse hres = (HttpServletResponse) mi.getResponseMessage();
        String username = getUsername(hreq);
        String[] groups = getUsergroups(hreq);
        if ((username != null) && (!username.trim().isEmpty()) && (groups != null)) {
            try {
                ch.handle(new Callback[] {
                        new CallerPrincipalCallback(client, new MyOrgPrincipal(username)),
                        new GroupPrincipalCallback(client, groups) });
            }
            catch (UnsupportedCallbackException | IOException e) {
                e.printStackTrace();
                throw new AuthException("Could not authenticate user.");
            }
            return AuthStatus.SUCCESS;
        }
        else if (!isProtectedResource) {
            return AuthStatus.SUCCESS;
        }
        else {
            try {
                hres.sendRedirect(LOGIN_URL);
            }
            catch (IOException ioe) {
                ioe.printStackTrace();
                throw new AuthException("Could not authenticate user.");
            }
            return AuthStatus.SEND_CONTINUE;
        }
    }

	// Validation of response and optional post-processing goes here
	// Not typically used, as --unless it had initially been wrapped-- the response is already committed
	// by the time this method is called
    @Override
    public AuthStatus secureResponse(MessageInfo messageInfo, Subject service) throws AuthException {
        return AuthStatus.SEND_SUCCESS;
    }

	// Logout logic goes here
	// The spec is a bit unclear regarding this method, thus I'm unsure whether it's best to directly modify the Subject
	// or use container facilities
	@Override
    public void cleanSubject(MessageInfo mi, Subject client) throws AuthException {
        HttpServletRequest hreq = (HttpServletRequest) mi.getRequestMessage();
        HttpServletResponse hres = (HttpServletResponse) mi.getResponseMessage();
        HttpSession hs = hreq.getSession(false);
        if (hs != null) {
            try {
                hs.invalidate();
            }
            catch (IllegalStateException ise) {
                ise.printStackTrace();
                throw new AuthException("Could not invalidate user session.");
            }
        }
        try {
            hreq.logout();
            hres.sendRedirect(LOGOUT_URL);
        }
        catch (ServletException | IOException e) {
            e.printStackTrace();
            throw new AuthException("Could not complete user logout.");
        }
    }

    @Override
    public Class<?>[] getSupportedMessageTypes() {
        return SUPPORTED_MESSAGE_TYPES;
    }

    private String getUsername(HttpServletRequest hreq) {
        return (String) hreq.getAttribute("eduPersonPrincipalName");
    }

    private String[] getUsergroups(HttpServletRequest hreq) {
        String groupsAttribute = (String) hreq.getAttribute("eduPersonAffiliation");
        String[] groups = null;
        if (groupsAttribute != null) {
            groups = groupsAttribute.split(";");
            for (int i = 0; i < groups.length; i++) {
                groups[i] = groups[i].trim();
            }
        }
        return groups;
    }

}

...