X-Git-Url: https://git.argeo.org/?a=blobdiff_plain;f=org.argeo.cms%2Fsrc%2Forg%2Fargeo%2Fcms%2Fauth%2FRemoteAuthUtils.java;h=3c436ba1fc40edd772e161d65ea1c70bc5f39cea;hb=b62b143dd3589245f088fb260d6d14edf7a87867;hp=6274bb146e736165214fcabd52f49c20edb3e5d8;hpb=c2366f32052deada7d96f635e86f745f438f094e;p=lgpl%2Fargeo-commons.git diff --git a/org.argeo.cms/src/org/argeo/cms/auth/RemoteAuthUtils.java b/org.argeo.cms/src/org/argeo/cms/auth/RemoteAuthUtils.java index 6274bb146..3c436ba1f 100644 --- a/org.argeo.cms/src/org/argeo/cms/auth/RemoteAuthUtils.java +++ b/org.argeo.cms/src/org/argeo/cms/auth/RemoteAuthUtils.java @@ -1,62 +1,189 @@ package org.argeo.cms.auth; -import java.security.AccessControlContext; -import java.security.AccessController; import java.security.PrivilegedAction; +import java.util.Base64; import java.util.function.Supplier; import javax.security.auth.Subject; +import javax.security.auth.kerberos.KerberosTicket; +import javax.security.auth.login.LoginContext; +import javax.security.auth.login.LoginException; +import org.argeo.api.cms.CmsAuth; +import org.argeo.api.cms.CmsLog; import org.argeo.api.cms.CmsSession; +import org.argeo.cms.http.HttpHeader; +import org.argeo.cms.http.HttpStatus; import org.argeo.cms.internal.runtime.CmsContextImpl; +import org.argeo.cms.util.CurrentSubject; +import org.ietf.jgss.GSSContext; +import org.ietf.jgss.GSSException; +import org.ietf.jgss.GSSManager; +import org.ietf.jgss.GSSName; +import org.ietf.jgss.Oid; /** Remote authentication utilities. */ public class RemoteAuthUtils { + private final static CmsLog log = CmsLog.getLog(RemoteAuthUtils.class); + static final String REMOTE_USER = "org.osgi.service.http.authentication.remote.user"; -// private static BundleContext bundleContext = FrameworkUtil.getBundle(RemoteAuthUtils.class).getBundleContext(); + private final static Oid KERBEROS_OID; +// private final static Oid KERB_V5_OID, KRB5_PRINCIPAL_NAME_OID; + static { + try { + KERBEROS_OID = new Oid("1.3.6.1.5.5.2"); +// KERB_V5_OID = new Oid("1.2.840.113554.1.2.2"); +// KRB5_PRINCIPAL_NAME_OID = new Oid("1.2.840.113554.1.2.2.1"); + } catch (GSSException e) { + throw new IllegalStateException("Cannot create Kerberos OID", e); + } + } /** * Execute this supplier, using the CMS class loader as context classloader. * Useful to log in to JCR. */ public final static T doAs(Supplier supplier, RemoteAuthRequest req) { - ClassLoader currentContextCl = Thread.currentThread().getContextClassLoader(); - Thread.currentThread().setContextClassLoader(RemoteAuthUtils.class.getClassLoader()); - try { - return Subject.doAs( - Subject.getSubject((AccessControlContext) req.getAttribute(AccessControlContext.class.getName())), - new PrivilegedAction() { + CmsSession cmsSession = getCmsSession(req); + return CurrentSubject.callAs(cmsSession.getSubject(), () -> supplier.get()); +// ClassLoader currentContextCl = Thread.currentThread().getContextClassLoader(); +// Thread.currentThread().setContextClassLoader(RemoteAuthUtils.class.getClassLoader()); +// try { +// return Subject.doAs( +// Subject.getSubject((AccessControlContext) req.getAttribute(AccessControlContext.class.getName())), +// new PrivilegedAction() { +// +// @Override +// public T run() { +// return supplier.get(); +// } +// +// }); +// } finally { +// Thread.currentThread().setContextClassLoader(currentContextCl); +// } + } + +// public final static void configureRequestSecurity(RemoteAuthRequest req) { +// if (req.getAttribute(AccessControlContext.class.getName()) != null) +// throw new IllegalStateException("Request already authenticated."); +// AccessControlContext acc = AccessController.getContext(); +// req.setAttribute(REMOTE_USER, CurrentUser.getUsername()); +// req.setAttribute(AccessControlContext.class.getName(), acc); +// } +// +// public final static void clearRequestSecurity(RemoteAuthRequest req) { +// if (req.getAttribute(AccessControlContext.class.getName()) == null) +// throw new IllegalStateException("Cannot clear non-authenticated request."); +// req.setAttribute(REMOTE_USER, null); +// req.setAttribute(AccessControlContext.class.getName(), null); +// } + + public static CmsSession getCmsSession(RemoteAuthRequest req) { + CmsSession cmsSession = (CmsSession) req.getAttribute(CmsSession.class.getName()); + if (cmsSession == null) + throw new IllegalStateException("Request must have a CMS session attribute"); + return cmsSession; + } + + public static String createGssToken(Subject subject, String service, String server) { + if (subject.getPrivateCredentials(KerberosTicket.class).isEmpty()) + throw new IllegalArgumentException("Subject " + subject + " is not GSS authenticated."); + return Subject.doAs(subject, (PrivilegedAction) () -> { + // !! different format than Kerberos + String serverPrinc = service + "@" + server; + GSSContext context = null; + String tokenStr = null; + + try { + // Get service's principal name + GSSManager manager = GSSManager.getInstance(); + // GSSName serverName = manager.createName(serverPrinc, + // GSSName.NT_HOSTBASED_SERVICE, KERBEROS_OID); + GSSName serverName = manager.createName(serverPrinc, GSSName.NT_HOSTBASED_SERVICE); + + // Get the context for authentication + context = manager.createContext(serverName, KERBEROS_OID, null, GSSContext.DEFAULT_LIFETIME); + // context.requestMutualAuth(true); // Request mutual authentication + // context.requestConf(true); // Request confidentiality + context.requestCredDeleg(true); - @Override - public T run() { - return supplier.get(); - } + byte[] token = new byte[0]; - }); + // token is ignored on the first call + token = context.initSecContext(token, 0, token.length); + + // Send a token to the server if one was generated by + // initSecContext + if (token != null) { + tokenStr = Base64.getEncoder().encodeToString(token); + // complete=true; + } + return tokenStr; + + } catch (GSSException e) { + throw new IllegalStateException("Cannot authenticate to " + serverPrinc, e); + } + }); + } + + public static LoginContext anonymousLogin(RemoteAuthRequest remoteAuthRequest, + RemoteAuthResponse remoteAuthResponse) { + // anonymous + ClassLoader currentContextClassLoader = Thread.currentThread().getContextClassLoader(); + try { + Thread.currentThread().setContextClassLoader(RemoteAuthUtils.class.getClassLoader()); + LoginContext lc = CmsAuth.ANONYMOUS + .newLoginContext(new RemoteAuthCallbackHandler(remoteAuthRequest, remoteAuthResponse)); + lc.login(); + return lc; + } catch (LoginException e1) { + if (log.isDebugEnabled()) + log.error("Cannot log in as anonymous", e1); + return null; } finally { - Thread.currentThread().setContextClassLoader(currentContextCl); + Thread.currentThread().setContextClassLoader(currentContextClassLoader); } } - public final static void configureRequestSecurity(RemoteAuthRequest req) { - if (req.getAttribute(AccessControlContext.class.getName()) != null) - throw new IllegalStateException("Request already authenticated."); - AccessControlContext acc = AccessController.getContext(); - req.setAttribute(REMOTE_USER, CurrentUser.getUsername()); - req.setAttribute(AccessControlContext.class.getName(), acc); - } + public static int askForWwwAuth(RemoteAuthRequest remoteAuthRequest, RemoteAuthResponse remoteAuthResponse, + String realm, boolean forceBasic) { + boolean negotiateFailed = false; + if (remoteAuthRequest.getHeader(HttpHeader.AUTHORIZATION.getHeaderName()) != null) { + // we already tried, so we give up in order not too loop endlessly + if (remoteAuthRequest.getHeader(HttpHeader.AUTHORIZATION.getHeaderName()) + .startsWith(HttpHeader.NEGOTIATE)) { + negotiateFailed = true; + } else { + return HttpStatus.FORBIDDEN.getCode(); + } + } - public final static void clearRequestSecurity(RemoteAuthRequest req) { - if (req.getAttribute(AccessControlContext.class.getName()) == null) - throw new IllegalStateException("Cannot clear non-authenticated request."); - req.setAttribute(REMOTE_USER, null); - req.setAttribute(AccessControlContext.class.getName(), null); + // response.setHeader(HttpUtils.HEADER_WWW_AUTHENTICATE, "basic + // realm=\"" + httpAuthRealm + "\""); + if (hasAcceptorCredentials() && !forceBasic && !negotiateFailed) {// SPNEGO + remoteAuthResponse.addHeader(HttpHeader.WWW_AUTHENTICATE.getHeaderName(), HttpHeader.NEGOTIATE); + // TODO make it configurable ? + remoteAuthResponse.addHeader(HttpHeader.WWW_AUTHENTICATE.getHeaderName(), + HttpHeader.BASIC + " " + HttpHeader.REALM + "=\"" + realm + "\""); + } else { + remoteAuthResponse.setHeader(HttpHeader.WWW_AUTHENTICATE.getHeaderName(), + HttpHeader.BASIC + " " + HttpHeader.REALM + "=\"" + realm + "\""); + } + + // response.setDateHeader("Date", System.currentTimeMillis()); + // response.setDateHeader("Expires", System.currentTimeMillis() + (24 * + // 60 * 60 * 1000)); + // response.setHeader("Accept-Ranges", "bytes"); + // response.setHeader("Connection", "Keep-Alive"); + // response.setHeader("Keep-Alive", "timeout=5, max=97"); + // response.setContentType("text/html; charset=UTF-8"); + + return HttpStatus.UNAUTHORIZED.getCode(); } - public static CmsSession getCmsSession(RemoteAuthRequest req) { - Subject subject = Subject - .getSubject((AccessControlContext) req.getAttribute(AccessControlContext.class.getName())); - CmsSession cmsSession = CmsContextImpl.getCmsContext().getCmsSession(subject); - return cmsSession; + private static boolean hasAcceptorCredentials() { + return CmsContextImpl.getCmsContext().getAcceptorCredentials() != null; } + }