1 package org
.argeo
.cms
.auth
;
3 import java
.security
.AccessControlContext
;
4 import java
.security
.AccessController
;
5 import java
.security
.PrivilegedAction
;
6 import java
.util
.Base64
;
7 import java
.util
.function
.Supplier
;
9 import javax
.security
.auth
.Subject
;
10 import javax
.security
.auth
.kerberos
.KerberosTicket
;
11 import javax
.security
.auth
.login
.LoginContext
;
12 import javax
.security
.auth
.login
.LoginException
;
14 import org
.argeo
.api
.cms
.CmsAuth
;
15 import org
.argeo
.api
.cms
.CmsLog
;
16 import org
.argeo
.api
.cms
.CmsSession
;
17 import org
.argeo
.cms
.internal
.runtime
.CmsContextImpl
;
18 import org
.argeo
.util
.http
.HttpHeader
;
19 import org
.ietf
.jgss
.GSSContext
;
20 import org
.ietf
.jgss
.GSSException
;
21 import org
.ietf
.jgss
.GSSManager
;
22 import org
.ietf
.jgss
.GSSName
;
23 import org
.ietf
.jgss
.Oid
;
25 /** Remote authentication utilities. */
26 public class RemoteAuthUtils
{
27 private final static CmsLog log
= CmsLog
.getLog(RemoteAuthUtils
.class);
29 static final String REMOTE_USER
= "org.osgi.service.http.authentication.remote.user";
30 private final static Oid KERBEROS_OID
;
31 // private final static Oid KERB_V5_OID, KRB5_PRINCIPAL_NAME_OID;
34 KERBEROS_OID
= new Oid("1.3.6.1.5.5.2");
35 // KERB_V5_OID = new Oid("1.2.840.113554.1.2.2");
36 // KRB5_PRINCIPAL_NAME_OID = new Oid("1.2.840.113554.1.2.2.1");
37 } catch (GSSException e
) {
38 throw new IllegalStateException("Cannot create Kerberos OID", e
);
43 * Execute this supplier, using the CMS class loader as context classloader.
44 * Useful to log in to JCR.
46 public final static <T
> T
doAs(Supplier
<T
> supplier
, RemoteAuthRequest req
) {
47 ClassLoader currentContextCl
= Thread
.currentThread().getContextClassLoader();
48 Thread
.currentThread().setContextClassLoader(RemoteAuthUtils
.class.getClassLoader());
51 Subject
.getSubject((AccessControlContext
) req
.getAttribute(AccessControlContext
.class.getName())),
52 new PrivilegedAction
<T
>() {
56 return supplier
.get();
61 Thread
.currentThread().setContextClassLoader(currentContextCl
);
65 public final static void configureRequestSecurity(RemoteAuthRequest req
) {
66 if (req
.getAttribute(AccessControlContext
.class.getName()) != null)
67 throw new IllegalStateException("Request already authenticated.");
68 AccessControlContext acc
= AccessController
.getContext();
69 req
.setAttribute(REMOTE_USER
, CurrentUser
.getUsername());
70 req
.setAttribute(AccessControlContext
.class.getName(), acc
);
73 public final static void clearRequestSecurity(RemoteAuthRequest req
) {
74 if (req
.getAttribute(AccessControlContext
.class.getName()) == null)
75 throw new IllegalStateException("Cannot clear non-authenticated request.");
76 req
.setAttribute(REMOTE_USER
, null);
77 req
.setAttribute(AccessControlContext
.class.getName(), null);
80 public static CmsSession
getCmsSession(RemoteAuthRequest req
) {
81 Subject subject
= Subject
82 .getSubject((AccessControlContext
) req
.getAttribute(AccessControlContext
.class.getName()));
83 CmsSession cmsSession
= CmsContextImpl
.getCmsContext().getCmsSession(subject
);
87 public static String
getGssToken(Subject subject
, String service
, String server
) {
88 if (subject
.getPrivateCredentials(KerberosTicket
.class).isEmpty())
89 throw new IllegalArgumentException("Subject " + subject
+ " is not GSS authenticated.");
90 return Subject
.doAs(subject
, (PrivilegedAction
<String
>) () -> {
91 // !! different format than Kerberos
92 String serverPrinc
= service
+ "@" + server
;
93 GSSContext context
= null;
94 String tokenStr
= null;
97 // Get service's principal name
98 GSSManager manager
= GSSManager
.getInstance();
99 // GSSName serverName = manager.createName(serverPrinc,
100 // GSSName.NT_HOSTBASED_SERVICE, KERBEROS_OID);
101 GSSName serverName
= manager
.createName(serverPrinc
, GSSName
.NT_HOSTBASED_SERVICE
);
103 // Get the context for authentication
104 context
= manager
.createContext(serverName
, KERBEROS_OID
, null, GSSContext
.DEFAULT_LIFETIME
);
105 // context.requestMutualAuth(true); // Request mutual authentication
106 // context.requestConf(true); // Request confidentiality
107 context
.requestCredDeleg(true);
109 byte[] token
= new byte[0];
111 // token is ignored on the first call
112 token
= context
.initSecContext(token
, 0, token
.length
);
114 // Send a token to the server if one was generated by
117 tokenStr
= Base64
.getEncoder().encodeToString(token
);
122 } catch (GSSException e
) {
123 throw new IllegalStateException("Cannot authenticate to " + serverPrinc
, e
);
128 public static LoginContext
anonymousLogin(RemoteAuthRequest remoteAuthRequest
,
129 RemoteAuthResponse remoteAuthResponse
) {
131 ClassLoader currentContextClassLoader
= Thread
.currentThread().getContextClassLoader();
133 Thread
.currentThread().setContextClassLoader(RemoteAuthUtils
.class.getClassLoader());
134 LoginContext lc
= CmsAuth
.ANONYMOUS
135 .newLoginContext(new RemoteAuthCallbackHandler(remoteAuthRequest
, remoteAuthResponse
));
138 } catch (LoginException e1
) {
139 if (log
.isDebugEnabled())
140 log
.error("Cannot log in as anonymous", e1
);
143 Thread
.currentThread().setContextClassLoader(currentContextClassLoader
);
147 public static int askForWwwAuth(RemoteAuthResponse remoteAuthResponse
, String realm
, boolean forceBasic
) {
148 // response.setHeader(HttpUtils.HEADER_WWW_AUTHENTICATE, "basic
149 // realm=\"" + httpAuthRealm + "\"");
150 if (hasAcceptorCredentials() && !forceBasic
)// SPNEGO
151 remoteAuthResponse
.setHeader(HttpHeader
.WWW_AUTHENTICATE
.getName(), HttpHeader
.NEGOTIATE
);
153 remoteAuthResponse
.setHeader(HttpHeader
.WWW_AUTHENTICATE
.getName(),
154 HttpHeader
.BASIC
+ " " + HttpHeader
.REALM
+ "=\"" + realm
+ "\"");
156 // response.setDateHeader("Date", System.currentTimeMillis());
157 // response.setDateHeader("Expires", System.currentTimeMillis() + (24 *
159 // response.setHeader("Accept-Ranges", "bytes");
160 // response.setHeader("Connection", "Keep-Alive");
161 // response.setHeader("Keep-Alive", "timeout=5, max=97");
162 // response.setContentType("text/html; charset=UTF-8");
167 private static boolean hasAcceptorCredentials() {
168 return CmsContextImpl
.getCmsContext().getAcceptorCredentials() != null;