]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.cms/src/org/argeo/cms/auth/UserAdminLoginModule.java
Improve node deployment
[lgpl/argeo-commons.git] / org.argeo.cms / src / org / argeo / cms / auth / UserAdminLoginModule.java
1 package org.argeo.cms.auth;
2
3 import java.io.IOException;
4 import java.security.PrivilegedAction;
5 import java.util.Arrays;
6 import java.util.HashSet;
7 import java.util.List;
8 import java.util.Locale;
9 import java.util.Map;
10 import java.util.Set;
11
12 import javax.naming.ldap.LdapName;
13 import javax.security.auth.Subject;
14 import javax.security.auth.callback.Callback;
15 import javax.security.auth.callback.CallbackHandler;
16 import javax.security.auth.callback.LanguageCallback;
17 import javax.security.auth.callback.NameCallback;
18 import javax.security.auth.callback.PasswordCallback;
19 import javax.security.auth.callback.UnsupportedCallbackException;
20 import javax.security.auth.kerberos.KerberosPrincipal;
21 import javax.security.auth.login.CredentialNotFoundException;
22 import javax.security.auth.login.LoginException;
23 import javax.security.auth.spi.LoginModule;
24 import javax.servlet.http.HttpServletRequest;
25
26 import org.apache.commons.logging.Log;
27 import org.apache.commons.logging.LogFactory;
28 import org.argeo.cms.CmsException;
29 import org.argeo.naming.LdapAttrs;
30 import org.argeo.osgi.useradmin.IpaUtils;
31 import org.osgi.framework.BundleContext;
32 import org.osgi.framework.FrameworkUtil;
33 import org.osgi.service.useradmin.Authorization;
34 import org.osgi.service.useradmin.User;
35 import org.osgi.service.useradmin.UserAdmin;
36
37 public class UserAdminLoginModule implements LoginModule {
38 private final static Log log = LogFactory.getLog(UserAdminLoginModule.class);
39
40 private Subject subject;
41 private CallbackHandler callbackHandler;
42 private Map<String, Object> sharedState = null;
43
44 private List<String> indexedUserProperties = Arrays
45 .asList(new String[] { LdapAttrs.uid.name(), LdapAttrs.mail.name(), LdapAttrs.cn.name() });
46
47 // private state
48 private BundleContext bc;
49 private User authenticatedUser = null;
50 private Locale locale;
51
52 @SuppressWarnings("unchecked")
53 @Override
54 public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState,
55 Map<String, ?> options) {
56 this.subject = subject;
57 try {
58 bc = FrameworkUtil.getBundle(UserAdminLoginModule.class).getBundleContext();
59 this.callbackHandler = callbackHandler;
60 this.sharedState = (Map<String, Object>) sharedState;
61 } catch (Exception e) {
62 throw new CmsException("Cannot initialize login module", e);
63 }
64 }
65
66 @Override
67 public boolean login() throws LoginException {
68 UserAdmin userAdmin = bc.getService(bc.getServiceReference(UserAdmin.class));
69 final String username;
70 final char[] password;
71 if (sharedState.containsKey(CmsAuthUtils.SHARED_STATE_NAME)
72 && sharedState.containsKey(CmsAuthUtils.SHARED_STATE_PWD)) {
73 // NB: required by Basic http auth
74 username = (String) sharedState.get(CmsAuthUtils.SHARED_STATE_NAME);
75 password = (char[]) sharedState.get(CmsAuthUtils.SHARED_STATE_PWD);
76 // // TODO locale?
77 } else {
78 // ask for username and password
79 NameCallback nameCallback = new NameCallback("User");
80 PasswordCallback passwordCallback = new PasswordCallback("Password", false);
81 LanguageCallback langCallback = new LanguageCallback();
82 try {
83 callbackHandler.handle(new Callback[] { nameCallback, passwordCallback, langCallback });
84 } catch (IOException e) {
85 throw new LoginException("Cannot handle callback: " + e.getMessage());
86 } catch (UnsupportedCallbackException e) {
87 return false;
88 }
89
90 // i18n
91 locale = langCallback.getLocale();
92 if (locale == null)
93 locale = Locale.getDefault();
94 // FIXME add it to Subject
95 // UiContext.setLocale(locale);
96
97 username = nameCallback.getName();
98 if (username == null || username.trim().equals("")) {
99 // authorization = userAdmin.getAuthorization(null);
100 throw new CredentialNotFoundException("No credentials provided");
101 }
102 if (passwordCallback.getPassword() != null)
103 password = passwordCallback.getPassword();
104 else
105 throw new CredentialNotFoundException("No credentials provided");
106 }
107
108 // User user = userAdmin.getUser(null, username);
109 User user = searchForUser(userAdmin, username);
110 if (user == null)
111 return true;// expect Kerberos
112 // throw new FailedLoginException("Invalid credentials");
113 if (!user.hasCredential(null, password))
114 return false;
115 // throw new FailedLoginException("Invalid credentials");
116 authenticatedUser = user;
117 return true;
118 }
119
120 @Override
121 public boolean commit() throws LoginException {
122 UserAdmin userAdmin = bc.getService(bc.getServiceReference(UserAdmin.class));
123 Authorization authorization;
124 if (callbackHandler == null) {// anonymous
125 authorization = userAdmin.getAuthorization(null);
126 } else {
127 User authenticatingUser;
128 Set<KerberosPrincipal> kerberosPrincipals = subject.getPrincipals(KerberosPrincipal.class);
129 if (kerberosPrincipals.isEmpty()) {
130 if (authenticatedUser == null) {
131 if (log.isTraceEnabled())
132 log.trace("Neither kerberos nor user admin login succeeded. Login failed.");
133 return false;
134 } else {
135 authenticatingUser = authenticatedUser;
136 }
137 } else {
138 KerberosPrincipal kerberosPrincipal = kerberosPrincipals.iterator().next();
139 LdapName dn = IpaUtils.kerberosToDn(kerberosPrincipal.getName());
140 authenticatingUser = new AuthenticatingUser(dn);
141 if (authenticatedUser != null && !authenticatingUser.getName().equals(authenticatedUser.getName()))
142 throw new LoginException("Kerberos login " + authenticatingUser.getName()
143 + " is inconsistent with user admin login " + authenticatedUser.getName());
144 }
145 authorization = Subject.doAs(subject, new PrivilegedAction<Authorization>() {
146
147 @Override
148 public Authorization run() {
149 Authorization authorization = userAdmin.getAuthorization(authenticatingUser);
150 return authorization;
151 }
152
153 });
154 if (authorization == null)
155 throw new LoginException(
156 "User admin found no authorization for authenticated user " + authenticatingUser.getName());
157 }
158 // Log and monitor new login
159 CmsAuthUtils.addAuthorization(subject, authorization, locale,
160 (HttpServletRequest) sharedState.get(CmsAuthUtils.SHARED_STATE_HTTP_REQUEST));
161 if (log.isDebugEnabled())
162 log.debug("Logged in to CMS: " + subject);
163 return true;
164 }
165
166 @Override
167 public boolean abort() throws LoginException {
168 return true;
169 }
170
171 @Override
172 public boolean logout() throws LoginException {
173 if (log.isTraceEnabled())
174 log.trace("Logging out from CMS... " + subject);
175 // boolean httpSessionLogoutOk = CmsAuthUtils.logoutSession(bc,
176 // subject);
177 CmsAuthUtils.cleanUp(subject);
178 return true;
179 }
180
181 protected User searchForUser(UserAdmin userAdmin, String providedUsername) {
182 try {
183 // TODO check value null or empty
184 Set<User> collectedUsers = new HashSet<>();
185 // try dn
186 User user = null;
187 try {
188 user = (User) userAdmin.getRole(providedUsername);
189 if (user != null)
190 collectedUsers.add(user);
191 } catch (Exception e) {
192 // silent
193 }
194 // try all indexes
195 for (String attr : indexedUserProperties) {
196 user = userAdmin.getUser(attr, providedUsername);
197 if (user != null)
198 collectedUsers.add(user);
199 }
200 if (collectedUsers.size() == 1)
201 return collectedUsers.iterator().next();
202 else if (collectedUsers.size() > 1)
203 log.warn(collectedUsers.size() + " users for provided username" + providedUsername);
204 return null;
205 } catch (Exception e) {
206 if (log.isTraceEnabled())
207 log.warn("Cannot search for user " + providedUsername, e);
208 return null;
209 }
210
211 }
212 }