1 package org
.argeo
.cms
.auth
;
3 import java
.io
.IOException
;
4 import java
.security
.PrivilegedAction
;
5 import java
.security
.cert
.X509Certificate
;
6 import java
.util
.Arrays
;
7 import java
.util
.HashSet
;
9 import java
.util
.Locale
;
13 import javax
.naming
.ldap
.LdapName
;
14 import javax
.security
.auth
.Subject
;
15 import javax
.security
.auth
.callback
.Callback
;
16 import javax
.security
.auth
.callback
.CallbackHandler
;
17 import javax
.security
.auth
.callback
.LanguageCallback
;
18 import javax
.security
.auth
.callback
.NameCallback
;
19 import javax
.security
.auth
.callback
.PasswordCallback
;
20 import javax
.security
.auth
.callback
.UnsupportedCallbackException
;
21 import javax
.security
.auth
.kerberos
.KerberosPrincipal
;
22 import javax
.security
.auth
.login
.CredentialNotFoundException
;
23 import javax
.security
.auth
.login
.LoginException
;
24 import javax
.security
.auth
.spi
.LoginModule
;
25 import javax
.servlet
.http
.HttpServletRequest
;
27 import org
.apache
.commons
.logging
.Log
;
28 import org
.apache
.commons
.logging
.LogFactory
;
29 import org
.argeo
.cms
.CmsException
;
30 import org
.argeo
.cms
.internal
.kernel
.Activator
;
31 import org
.argeo
.naming
.LdapAttrs
;
32 import org
.argeo
.osgi
.useradmin
.AuthenticatingUser
;
33 import org
.argeo
.osgi
.useradmin
.IpaUtils
;
34 import org
.argeo
.osgi
.useradmin
.OsUserUtils
;
35 import org
.osgi
.framework
.BundleContext
;
36 import org
.osgi
.framework
.FrameworkUtil
;
37 import org
.osgi
.service
.useradmin
.Authorization
;
38 import org
.osgi
.service
.useradmin
.User
;
39 import org
.osgi
.service
.useradmin
.UserAdmin
;
41 public class UserAdminLoginModule
implements LoginModule
{
42 private final static Log log
= LogFactory
.getLog(UserAdminLoginModule
.class);
44 private Subject subject
;
45 private CallbackHandler callbackHandler
;
46 private Map
<String
, Object
> sharedState
= null;
48 private List
<String
> indexedUserProperties
= Arrays
49 .asList(new String
[] { LdapAttrs
.mail
.name(), LdapAttrs
.uid
.name(), LdapAttrs
.authPassword
.name() });
52 private BundleContext bc
;
53 private User authenticatedUser
= null;
54 private Locale locale
;
56 private Authorization bindAuthorization
= null;
58 private boolean singleUser
= Activator
.isSingleUser();
60 @SuppressWarnings("unchecked")
62 public void initialize(Subject subject
, CallbackHandler callbackHandler
, Map
<String
, ?
> sharedState
,
63 Map
<String
, ?
> options
) {
64 this.subject
= subject
;
66 bc
= FrameworkUtil
.getBundle(UserAdminLoginModule
.class).getBundleContext();
67 this.callbackHandler
= callbackHandler
;
68 this.sharedState
= (Map
<String
, Object
>) sharedState
;
69 } catch (Exception e
) {
70 throw new CmsException("Cannot initialize login module", e
);
75 public boolean login() throws LoginException
{
76 UserAdmin userAdmin
= bc
.getService(bc
.getServiceReference(UserAdmin
.class));
77 final String username
;
78 final char[] password
;
79 X509Certificate
[] certificateChain
= null;
80 if (sharedState
.containsKey(CmsAuthUtils
.SHARED_STATE_NAME
)
81 && sharedState
.containsKey(CmsAuthUtils
.SHARED_STATE_PWD
)) {
82 // NB: required by Basic http auth
83 username
= (String
) sharedState
.get(CmsAuthUtils
.SHARED_STATE_NAME
);
84 password
= (char[]) sharedState
.get(CmsAuthUtils
.SHARED_STATE_PWD
);
86 } else if (sharedState
.containsKey(CmsAuthUtils
.SHARED_STATE_NAME
)
87 && sharedState
.containsKey(CmsAuthUtils
.SHARED_STATE_CERTIFICATE_CHAIN
)) {
88 // NB: required by Basic http auth
89 username
= (String
) sharedState
.get(CmsAuthUtils
.SHARED_STATE_NAME
);
90 certificateChain
= (X509Certificate
[]) sharedState
.get(CmsAuthUtils
.SHARED_STATE_CERTIFICATE_CHAIN
);
92 } else if (singleUser
) {
93 username
= OsUserUtils
.getOsUsername();
97 // ask for username and password
98 NameCallback nameCallback
= new NameCallback("User");
99 PasswordCallback passwordCallback
= new PasswordCallback("Password", false);
100 LanguageCallback langCallback
= new LanguageCallback();
102 callbackHandler
.handle(new Callback
[] { nameCallback
, passwordCallback
, langCallback
});
103 } catch (IOException e
) {
104 throw new LoginException("Cannot handle callback: " + e
.getMessage());
105 } catch (UnsupportedCallbackException e
) {
110 locale
= langCallback
.getLocale();
112 locale
= Locale
.getDefault();
113 // FIXME add it to Subject
114 // Locale.setDefault(locale);
116 username
= nameCallback
.getName();
117 if (username
== null || username
.trim().equals("")) {
118 // authorization = userAdmin.getAuthorization(null);
119 throw new CredentialNotFoundException("No credentials provided");
121 if (passwordCallback
.getPassword() != null)
122 password
= passwordCallback
.getPassword();
124 throw new CredentialNotFoundException("No credentials provided");
126 User user
= searchForUser(userAdmin
, username
);
128 return true;// expect Kerberos
130 if (password
!= null) {
133 AuthenticatingUser authenticatingUser
= new AuthenticatingUser(user
.getName(), password
);
134 bindAuthorization
= userAdmin
.getAuthorization(authenticatingUser
);
135 // TODO check tokens as well
136 if (bindAuthorization
!= null) {
137 authenticatedUser
= user
;
140 } catch (Exception e
) {
142 if (log
.isTraceEnabled())
143 log
.trace("Bind failed", e
);
146 // works only if a connection password is provided
147 if (!user
.hasCredential(null, password
)) {
150 } else if (certificateChain
!= null) {
151 // TODO check CRLs/OSCP validity?
152 // NB: authorization in commit() will work only if an LDAP connection password
154 } else if (singleUser
) {
155 // TODO verify IP address?
157 throw new CredentialNotFoundException("No credentials provided");
160 authenticatedUser
= user
;
165 public boolean commit() throws LoginException
{
167 OsUserUtils
.loginAsSystemUser(subject
);
169 UserAdmin userAdmin
= bc
.getService(bc
.getServiceReference(UserAdmin
.class));
170 Authorization authorization
;
171 if (callbackHandler
== null) {// anonymous
172 authorization
= userAdmin
.getAuthorization(null);
173 } else if (bindAuthorization
!= null) {// bind
174 authorization
= bindAuthorization
;
176 User authenticatingUser
;
177 Set
<KerberosPrincipal
> kerberosPrincipals
= subject
.getPrincipals(KerberosPrincipal
.class);
178 if (kerberosPrincipals
.isEmpty()) {
179 if (authenticatedUser
== null) {
180 if (log
.isTraceEnabled())
181 log
.trace("Neither kerberos nor user admin login succeeded. Login failed.");
184 authenticatingUser
= authenticatedUser
;
187 KerberosPrincipal kerberosPrincipal
= kerberosPrincipals
.iterator().next();
188 LdapName dn
= IpaUtils
.kerberosToDn(kerberosPrincipal
.getName());
189 authenticatingUser
= new AuthenticatingUser(dn
);
190 if (authenticatedUser
!= null && !authenticatingUser
.getName().equals(authenticatedUser
.getName()))
191 throw new LoginException("Kerberos login " + authenticatingUser
.getName()
192 + " is inconsistent with user admin login " + authenticatedUser
.getName());
194 authorization
= Subject
.doAs(subject
, new PrivilegedAction
<Authorization
>() {
197 public Authorization
run() {
198 Authorization authorization
= userAdmin
.getAuthorization(authenticatingUser
);
199 return authorization
;
203 if (authorization
== null)
204 throw new LoginException(
205 "User admin found no authorization for authenticated user " + authenticatingUser
.getName());
207 // Log and monitor new login
208 CmsAuthUtils
.addAuthorization(subject
, authorization
, locale
,
209 (HttpServletRequest
) sharedState
.get(CmsAuthUtils
.SHARED_STATE_HTTP_REQUEST
));
210 if (log
.isDebugEnabled())
211 log
.debug("Logged in to CMS: " + subject
);
216 public boolean abort() throws LoginException
{
221 public boolean logout() throws LoginException
{
222 if (log
.isTraceEnabled())
223 log
.trace("Logging out from CMS... " + subject
);
224 // boolean httpSessionLogoutOk = CmsAuthUtils.logoutSession(bc,
226 CmsAuthUtils
.cleanUp(subject
);
230 protected User
searchForUser(UserAdmin userAdmin
, String providedUsername
) {
232 // TODO check value null or empty
233 Set
<User
> collectedUsers
= new HashSet
<>();
237 for (String attr
: indexedUserProperties
) {
238 user
= userAdmin
.getUser(attr
, providedUsername
);
240 collectedUsers
.add(user
);
242 if (collectedUsers
.size() == 1) {
243 user
= collectedUsers
.iterator().next();
245 } else if (collectedUsers
.size() > 1) {
246 log
.warn(collectedUsers
.size() + " users for provided username" + providedUsername
);
248 // try DN as a last resort
250 user
= (User
) userAdmin
.getRole(providedUsername
);
253 } catch (Exception e
) {
257 } catch (Exception e
) {
258 if (log
.isTraceEnabled())
259 log
.warn("Cannot search for user " + providedUsername
, e
);