1 package org
.argeo
.cms
.auth
;
3 import java
.security
.PrivilegedAction
;
4 import java
.util
.Base64
;
5 import java
.util
.function
.Supplier
;
7 import javax
.security
.auth
.Subject
;
8 import javax
.security
.auth
.kerberos
.KerberosTicket
;
9 import javax
.security
.auth
.login
.LoginContext
;
10 import javax
.security
.auth
.login
.LoginException
;
12 import org
.argeo
.api
.cms
.CmsAuth
;
13 import org
.argeo
.api
.cms
.CmsLog
;
14 import org
.argeo
.api
.cms
.CmsSession
;
15 import org
.argeo
.cms
.internal
.runtime
.CmsContextImpl
;
16 import org
.argeo
.util
.CurrentSubject
;
17 import org
.argeo
.util
.http
.HttpHeader
;
18 import org
.argeo
.util
.http
.HttpStatus
;
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 CmsSession cmsSession
= getCmsSession(req
);
48 return CurrentSubject
.callAs(cmsSession
.getSubject(), () -> supplier
.get());
49 // ClassLoader currentContextCl = Thread.currentThread().getContextClassLoader();
50 // Thread.currentThread().setContextClassLoader(RemoteAuthUtils.class.getClassLoader());
52 // return Subject.doAs(
53 // Subject.getSubject((AccessControlContext) req.getAttribute(AccessControlContext.class.getName())),
54 // new PrivilegedAction<T>() {
58 // return supplier.get();
63 // Thread.currentThread().setContextClassLoader(currentContextCl);
67 // public final static void configureRequestSecurity(RemoteAuthRequest req) {
68 // if (req.getAttribute(AccessControlContext.class.getName()) != null)
69 // throw new IllegalStateException("Request already authenticated.");
70 // AccessControlContext acc = AccessController.getContext();
71 // req.setAttribute(REMOTE_USER, CurrentUser.getUsername());
72 // req.setAttribute(AccessControlContext.class.getName(), acc);
75 // public final static void clearRequestSecurity(RemoteAuthRequest req) {
76 // if (req.getAttribute(AccessControlContext.class.getName()) == null)
77 // throw new IllegalStateException("Cannot clear non-authenticated request.");
78 // req.setAttribute(REMOTE_USER, null);
79 // req.setAttribute(AccessControlContext.class.getName(), null);
82 public static CmsSession
getCmsSession(RemoteAuthRequest req
) {
83 CmsSession cmsSession
= (CmsSession
) req
.getAttribute(CmsSession
.class.getName());
84 if (cmsSession
== null)
85 throw new IllegalStateException("Request must have a CMS session attribute");
89 public static String
createGssToken(Subject subject
, String service
, String server
) {
90 if (subject
.getPrivateCredentials(KerberosTicket
.class).isEmpty())
91 throw new IllegalArgumentException("Subject " + subject
+ " is not GSS authenticated.");
92 return Subject
.doAs(subject
, (PrivilegedAction
<String
>) () -> {
93 // !! different format than Kerberos
94 String serverPrinc
= service
+ "@" + server
;
95 GSSContext context
= null;
96 String tokenStr
= null;
99 // Get service's principal name
100 GSSManager manager
= GSSManager
.getInstance();
101 // GSSName serverName = manager.createName(serverPrinc,
102 // GSSName.NT_HOSTBASED_SERVICE, KERBEROS_OID);
103 GSSName serverName
= manager
.createName(serverPrinc
, GSSName
.NT_HOSTBASED_SERVICE
);
105 // Get the context for authentication
106 context
= manager
.createContext(serverName
, KERBEROS_OID
, null, GSSContext
.DEFAULT_LIFETIME
);
107 // context.requestMutualAuth(true); // Request mutual authentication
108 // context.requestConf(true); // Request confidentiality
109 context
.requestCredDeleg(true);
111 byte[] token
= new byte[0];
113 // token is ignored on the first call
114 token
= context
.initSecContext(token
, 0, token
.length
);
116 // Send a token to the server if one was generated by
119 tokenStr
= Base64
.getEncoder().encodeToString(token
);
124 } catch (GSSException e
) {
125 throw new IllegalStateException("Cannot authenticate to " + serverPrinc
, e
);
130 public static LoginContext
anonymousLogin(RemoteAuthRequest remoteAuthRequest
,
131 RemoteAuthResponse remoteAuthResponse
) {
133 ClassLoader currentContextClassLoader
= Thread
.currentThread().getContextClassLoader();
135 Thread
.currentThread().setContextClassLoader(RemoteAuthUtils
.class.getClassLoader());
136 LoginContext lc
= CmsAuth
.ANONYMOUS
137 .newLoginContext(new RemoteAuthCallbackHandler(remoteAuthRequest
, remoteAuthResponse
));
140 } catch (LoginException e1
) {
141 if (log
.isDebugEnabled())
142 log
.error("Cannot log in as anonymous", e1
);
145 Thread
.currentThread().setContextClassLoader(currentContextClassLoader
);
149 public static int askForWwwAuth(RemoteAuthRequest remoteAuthRequest
, RemoteAuthResponse remoteAuthResponse
,
150 String realm
, boolean forceBasic
) {
151 boolean negotiateFailed
= false;
152 if (remoteAuthRequest
.getHeader(HttpHeader
.AUTHORIZATION
.getHeaderName()) != null) {
153 // we already tried, so we give up in order not too loop endlessly
154 if (remoteAuthRequest
.getHeader(HttpHeader
.AUTHORIZATION
.getHeaderName())
155 .startsWith(HttpHeader
.NEGOTIATE
)) {
156 negotiateFailed
= true;
158 return HttpStatus
.FORBIDDEN
.getCode();
162 // response.setHeader(HttpUtils.HEADER_WWW_AUTHENTICATE, "basic
163 // realm=\"" + httpAuthRealm + "\"");
164 if (hasAcceptorCredentials() && !forceBasic
&& !negotiateFailed
)// SPNEGO
165 remoteAuthResponse
.setHeader(HttpHeader
.WWW_AUTHENTICATE
.getHeaderName(), HttpHeader
.NEGOTIATE
);
167 remoteAuthResponse
.setHeader(HttpHeader
.WWW_AUTHENTICATE
.getHeaderName(),
168 HttpHeader
.BASIC
+ " " + HttpHeader
.REALM
+ "=\"" + realm
+ "\"");
170 // response.setDateHeader("Date", System.currentTimeMillis());
171 // response.setDateHeader("Expires", System.currentTimeMillis() + (24 *
173 // response.setHeader("Accept-Ranges", "bytes");
174 // response.setHeader("Connection", "Keep-Alive");
175 // response.setHeader("Keep-Alive", "timeout=5, max=97");
176 // response.setContentType("text/html; charset=UTF-8");
178 return HttpStatus
.UNAUTHORIZED
.getCode();
181 private static boolean hasAcceptorCredentials() {
182 return CmsContextImpl
.getCmsContext().getAcceptorCredentials() != null;