]> git.argeo.org Git - lgpl/argeo-commons.git/blob - auth/UserAdminLoginModule.java
Prepare next development cycle
[lgpl/argeo-commons.git] / auth / UserAdminLoginModule.java
1 package org.argeo.cms.internal.auth;
2
3 import java.security.Principal;
4 import java.util.Arrays;
5 import java.util.Collections;
6 import java.util.List;
7 import java.util.Map;
8 import java.util.Set;
9
10 import javax.naming.InvalidNameException;
11 import javax.naming.ldap.LdapName;
12 import javax.security.auth.Subject;
13 import javax.security.auth.callback.Callback;
14 import javax.security.auth.callback.CallbackHandler;
15 import javax.security.auth.callback.NameCallback;
16 import javax.security.auth.callback.PasswordCallback;
17 import javax.security.auth.login.CredentialNotFoundException;
18 import javax.security.auth.login.LoginException;
19 import javax.security.auth.spi.LoginModule;
20 import javax.security.auth.x500.X500Principal;
21
22 import org.apache.jackrabbit.core.security.AnonymousPrincipal;
23 import org.apache.jackrabbit.core.security.SecurityConstants;
24 import org.apache.jackrabbit.core.security.principal.AdminPrincipal;
25 import org.argeo.cms.CmsException;
26 import org.argeo.cms.KernelHeader;
27 import org.argeo.cms.internal.kernel.Activator;
28 import org.osgi.framework.BundleContext;
29 import org.osgi.service.useradmin.Authorization;
30 import org.osgi.service.useradmin.User;
31 import org.osgi.service.useradmin.UserAdmin;
32
33 public class UserAdminLoginModule implements LoginModule {
34 private Subject subject;
35 private CallbackHandler callbackHandler;
36 private boolean isAnonymous = false;
37
38 private final static LdapName ROLE_KERNEL_NAME, ROLE_ADMIN_NAME,
39 ROLE_ANONYMOUS_NAME, ROLE_USER_NAME;
40 private final static List<LdapName> RESERVED_ROLES;
41 private final static X500Principal ROLE_ANONYMOUS_PRINCIPAL;
42 static {
43 try {
44 ROLE_KERNEL_NAME = new LdapName(KernelHeader.ROLE_KERNEL);
45 ROLE_ADMIN_NAME = new LdapName(KernelHeader.ROLE_ADMIN);
46 ROLE_USER_NAME = new LdapName(KernelHeader.ROLE_USER);
47 ROLE_ANONYMOUS_NAME = new LdapName(KernelHeader.ROLE_ANONYMOUS);
48 RESERVED_ROLES = Collections.unmodifiableList(Arrays
49 .asList(new LdapName[] { ROLE_KERNEL_NAME, ROLE_ADMIN_NAME,
50 ROLE_ANONYMOUS_NAME, ROLE_USER_NAME,
51 new LdapName(KernelHeader.ROLE_GROUP_ADMIN),
52 new LdapName(KernelHeader.ROLE_USER_ADMIN) }));
53 ROLE_ANONYMOUS_PRINCIPAL = new X500Principal(
54 ROLE_ANONYMOUS_NAME.toString());
55 } catch (InvalidNameException e) {
56 throw new Error("Cannot initialize login module class", e);
57 }
58 }
59
60 private Authorization authorization;
61
62 @Override
63 public void initialize(Subject subject, CallbackHandler callbackHandler,
64 Map<String, ?> sharedState, Map<String, ?> options) {
65 try {
66 this.subject = subject;
67 this.callbackHandler = callbackHandler;
68 if (options.containsKey("anonymous"))
69 isAnonymous = Boolean.parseBoolean(options.get("anonymous")
70 .toString());
71 // String ldifFile = options.get("ldifFile").toString();
72 // InputStream in = new URL(ldifFile).openStream();
73 // userAdmin = new LdifUserAdmin(in);
74 } catch (Exception e) {
75 throw new CmsException("Cannot initialize login module", e);
76 }
77 }
78
79 @Override
80 public boolean login() throws LoginException {
81 // TODO use a callback in order to get the bundle context
82 BundleContext bc = Activator.getBundleContext();
83 UserAdmin userAdmin = bc.getService(bc
84 .getServiceReference(UserAdmin.class));
85 final User user;
86
87 if (!isAnonymous) {
88 // ask for username and password
89 NameCallback nameCallback = new NameCallback("User");
90 PasswordCallback passwordCallback = new PasswordCallback(
91 "Password", false);
92 // handle callbacks
93 try {
94 callbackHandler.handle(new Callback[] { nameCallback,
95 passwordCallback });
96 } catch (Exception e) {
97 throw new CmsException("Cannot handle callbacks", e);
98 }
99
100 // create credentials
101 final String username = nameCallback.getName();
102 if (username == null || username.trim().equals(""))
103 throw new CredentialNotFoundException("No credentials provided");
104
105 char[] password = {};
106 if (passwordCallback.getPassword() != null)
107 password = passwordCallback.getPassword();
108 else
109 throw new CredentialNotFoundException("No credentials provided");
110
111 user = userAdmin.getUser(null, username);
112 if (user == null)
113 return false;
114 if (!user.hasCredential(null, password))
115 return false;
116 } else
117 // anonymous
118 user = null;
119 this.authorization = userAdmin.getAuthorization(user);
120 return true;
121 }
122
123 @Override
124 public boolean commit() throws LoginException {
125 if (authorization != null) {
126 Set<Principal> principals = subject.getPrincipals();
127 try {
128 String authName = authorization.getName();
129
130 // determine user's principal
131 final LdapName name;
132 final Principal userPrincipal;
133 if (authName == null) {
134 name = ROLE_ANONYMOUS_NAME;
135 userPrincipal = ROLE_ANONYMOUS_PRINCIPAL;
136 principals.add(userPrincipal);
137 principals.add(new AnonymousPrincipal());
138 } else {
139 name = new LdapName(authName);
140 checkUserName(name);
141 userPrincipal = new X500Principal(name.toString());
142 principals.add(userPrincipal);
143 principals.add(new ImpliedByPrincipal(ROLE_USER_NAME,
144 userPrincipal));
145 }
146
147 // Add roles provided by authorization
148 for (String role : authorization.getRoles()) {
149 LdapName roleName = new LdapName(role);
150 if (roleName.equals(name)) {
151 // skip
152 } else {
153 checkImpliedPrincipalName(roleName);
154 principals.add(new ImpliedByPrincipal(roleName
155 .toString(), userPrincipal));
156 if (roleName.equals(ROLE_ADMIN_NAME))
157 principals.add(new AdminPrincipal(
158 SecurityConstants.ADMIN_ID));
159 }
160 }
161
162 return true;
163 } catch (InvalidNameException e) {
164 throw new CmsException("Cannot commit", e);
165 }
166 } else
167 return false;
168 }
169
170 @Override
171 public boolean abort() throws LoginException {
172 cleanUp();
173 return true;
174 }
175
176 @Override
177 public boolean logout() throws LoginException {
178 // TODO better deal with successive logout
179 if (subject == null)
180 return true;
181 // TODO make it less brutal
182 subject.getPrincipals().removeAll(
183 subject.getPrincipals(X500Principal.class));
184 subject.getPrincipals().removeAll(
185 subject.getPrincipals(ImpliedByPrincipal.class));
186 cleanUp();
187 return true;
188 }
189
190 private void cleanUp() {
191 subject = null;
192 authorization = null;
193 }
194
195 private void checkUserName(LdapName name) {
196 if (RESERVED_ROLES.contains(name))
197 throw new CmsException(name + " is a reserved name");
198 }
199
200 private void checkImpliedPrincipalName(LdapName roleName) {
201 if (ROLE_USER_NAME.equals(roleName)
202 || ROLE_ANONYMOUS_NAME.equals(roleName)
203 || ROLE_KERNEL_NAME.equals(roleName))
204 throw new CmsException(roleName + " cannot be listed as role");
205 }
206 }