]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.cms/src/org/argeo/cms/auth/RemoteAuthUtils.java
FS utils throws IOException
[lgpl/argeo-commons.git] / org.argeo.cms / src / org / argeo / cms / auth / RemoteAuthUtils.java
1 package org.argeo.cms.auth;
2
3 import java.security.PrivilegedAction;
4 import java.util.Base64;
5 import java.util.function.Supplier;
6
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;
11
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.http.HttpHeader;
16 import org.argeo.cms.http.HttpStatus;
17 import org.argeo.cms.internal.runtime.CmsContextImpl;
18 import org.argeo.cms.util.CurrentSubject;
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;
24
25 /** Remote authentication utilities. */
26 public class RemoteAuthUtils {
27 private final static CmsLog log = CmsLog.getLog(RemoteAuthUtils.class);
28
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;
32 static {
33 try {
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);
39 }
40 }
41
42 /**
43 * Execute this supplier, using the CMS class loader as context classloader.
44 * Useful to log in to JCR.
45 */
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());
51 // try {
52 // return Subject.doAs(
53 // Subject.getSubject((AccessControlContext) req.getAttribute(AccessControlContext.class.getName())),
54 // new PrivilegedAction<T>() {
55 //
56 // @Override
57 // public T run() {
58 // return supplier.get();
59 // }
60 //
61 // });
62 // } finally {
63 // Thread.currentThread().setContextClassLoader(currentContextCl);
64 // }
65 }
66
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);
73 // }
74 //
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);
80 // }
81
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");
86 return cmsSession;
87 }
88
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;
97
98 try {
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);
104
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);
110
111 byte[] token = new byte[0];
112
113 // token is ignored on the first call
114 token = context.initSecContext(token, 0, token.length);
115
116 // Send a token to the server if one was generated by
117 // initSecContext
118 if (token != null) {
119 tokenStr = Base64.getEncoder().encodeToString(token);
120 // complete=true;
121 }
122 return tokenStr;
123
124 } catch (GSSException e) {
125 throw new IllegalStateException("Cannot authenticate to " + serverPrinc, e);
126 }
127 });
128 }
129
130 public static LoginContext anonymousLogin(RemoteAuthRequest remoteAuthRequest,
131 RemoteAuthResponse remoteAuthResponse) {
132 // anonymous
133 ClassLoader currentContextClassLoader = Thread.currentThread().getContextClassLoader();
134 try {
135 Thread.currentThread().setContextClassLoader(RemoteAuthUtils.class.getClassLoader());
136 LoginContext lc = CmsAuth.ANONYMOUS
137 .newLoginContext(new RemoteAuthCallbackHandler(remoteAuthRequest, remoteAuthResponse));
138 lc.login();
139 return lc;
140 } catch (LoginException e1) {
141 if (log.isDebugEnabled())
142 log.error("Cannot log in as anonymous", e1);
143 return null;
144 } finally {
145 Thread.currentThread().setContextClassLoader(currentContextClassLoader);
146 }
147 }
148
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;
157 } else {
158 return HttpStatus.FORBIDDEN.getCode();
159 }
160 }
161
162 // response.setHeader(HttpUtils.HEADER_WWW_AUTHENTICATE, "basic
163 // realm=\"" + httpAuthRealm + "\"");
164 if (hasAcceptorCredentials() && !forceBasic && !negotiateFailed) {// SPNEGO
165 remoteAuthResponse.addHeader(HttpHeader.WWW_AUTHENTICATE.getHeaderName(), HttpHeader.NEGOTIATE);
166 // TODO make it configurable ?
167 remoteAuthResponse.addHeader(HttpHeader.WWW_AUTHENTICATE.getHeaderName(),
168 HttpHeader.BASIC + " " + HttpHeader.REALM + "=\"" + realm + "\"");
169 } else {
170 remoteAuthResponse.setHeader(HttpHeader.WWW_AUTHENTICATE.getHeaderName(),
171 HttpHeader.BASIC + " " + HttpHeader.REALM + "=\"" + realm + "\"");
172 }
173
174 // response.setDateHeader("Date", System.currentTimeMillis());
175 // response.setDateHeader("Expires", System.currentTimeMillis() + (24 *
176 // 60 * 60 * 1000));
177 // response.setHeader("Accept-Ranges", "bytes");
178 // response.setHeader("Connection", "Keep-Alive");
179 // response.setHeader("Keep-Alive", "timeout=5, max=97");
180 // response.setContentType("text/html; charset=UTF-8");
181
182 return HttpStatus.UNAUTHORIZED.getCode();
183 }
184
185 private static boolean hasAcceptorCredentials() {
186 return CmsContextImpl.getCmsContext().getAcceptorCredentials() != null;
187 }
188
189 }