/
LdapPasswordProvider.java
197 lines (177 loc) · 7.04 KB
/
LdapPasswordProvider.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
/*
* $Id$
*
* Copyright 2009-2011 Glencoe Software, Inc. All rights reserved.
* Use is subject to license terms supplied in LICENSE.txt
*/
package ome.security.auth;
import java.security.Permissions;
import ome.conditions.ApiUsageException;
import ome.conditions.ValidationException;
import ome.logic.LdapImpl;
import ome.model.meta.Experimenter;
import ome.security.SecuritySystem;
import org.springframework.util.Assert;
/**
* LDAP {@link PasswordProvider} which can create users on
* {@link #checkPassword(String, String, boolean) request} to synchronize with an LDAP
* directory. Assuming that a user exists in the configured LDAP store but not
* in the database, then a new user will be created. Authentication, however,
* always takes place against LDAP, and changing passwords is not allowed.
*
* Note: deleted LDAP users will not be removed from OMERO, but will not be able
* to login.
*
* @author Josh Moore, josh at glencoesoftware.com
* @see SecuritySystem
* @see Permissions
* @since 4.0
*/
public class LdapPasswordProvider extends ConfigurablePasswordProvider {
final protected LdapImpl ldapUtil;
public LdapPasswordProvider(PasswordUtil util, LdapImpl ldap) {
super(util);
Assert.notNull(ldap);
this.ldapUtil = ldap;
}
public LdapPasswordProvider(PasswordUtil util,
LdapImpl ldap,
boolean ignoreUnknown) {
super(util, ignoreUnknown);
Assert.notNull(ldap);
this.ldapUtil = ldap;
}
/**
* Only returns if the user is already in the database and has a DN value in
* the password table. Note: after a call to
* {@link #checkPassword(String, String, boolean)} with this same user value, this
* method might begin to return {@code true} due to a call to
* {@link LdapImpl#createUser(String, String)}.
*/
@Override
public boolean hasPassword(String user) {
if (ldapUtil.getSetting()) {
Long id = util.userId(user);
if (id != null) {
String dn = ldapUtil.lookupLdapAuthExperimenter(id);
if (dn != null) {
return true;
}
}
}
return false;
}
@Override
public Boolean checkPassword(String user, String password, boolean readOnly) {
if (!ldapUtil.getSetting()) {
return null; // EARLY EXIT!
}
// Note: LDAP simple authentication defaults to anonymous
// binding if the password is blank:
//
// 5.1.2. Unauthenticated Authentication Mechanism of Simple Bind
//
// An LDAP client may use the unauthenticated authentication mechanism
// of the simple Bind method to establish an anonymous authorization
// state by sending a Bind request with a name value (a distinguished
// name in LDAP string form [RFC4514] of non-zero length) and specifying
// the simple authentication choice containing a password value of zero
// length.
//
// Since an anonymous bind proves nothing about the validity of this
// user, we disable all attempts to login with an empty password.
//
// The same check takes place in LdapImpl.isAuthContext method.
//
if (password == null || password.equals("")) {
log.warn("Empty password for user: " + user);
loginAttempt(user, false);
return false;
}
Long id = util.userId(user);
// Unknown user. First try to create.
if (null == id) {
try {
if (readOnly == true) {
throw new IllegalStateException("Cannot create user!");
}
Experimenter experimenter = ldapUtil.createUser(user, password);
// Use default logic if the user creation did not exist,
// because there may be another non-database login mechanism
// which should also be given a chance.
if (experimenter != null) {
loginAttempt(user, true);
return true;
}
} catch (ApiUsageException e) {
log.info(String.format(
"Default choice on create user: %s (%s)", user, e));
}
}
// Known user, preventing special users by checking for a null dn
// in which case we ignore any information from LDAP.
// See ticket:6702
final String dn1 = (id == null) ? null : getOmeroDN(id);
if (dn1 != null) {
// If LDAP doesn't return a DN for a user that expects one
// then assume that they've been locked out. ticket:6248
final String dn2 = getLdapDN(user);
if (dn2 == null) {
log.info(String.format(
"User not found in LDAP: {username=%s, dn=%s}",
user, dn1));
return loginAttempt(user, false);
} else if (!dn1.equals(dn2)) {
String msg = String.format("DNs don't match: '%s' and '%s'",
dn1, dn2);
log.warn(msg);
loginAttempt(user, false);
// Throwing an exception so that the permissions verifier
// will state an "InternalException: Please contact your admin"
// We will need to find another way to handle this.
// Perhaps a hard-coded value in "password"."dn"
throw new ValidationException(msg);
} else {
if (readOnly == true) {
throw new IllegalStateException(
"Cannot synchronize user!");
}
ldapUtil.synchronizeLdapUser(user);
return loginAttempt(user,
ldapUtil.validatePassword(dn1, password));
}
}
// If anything goes wrong or no LDAP is found in OMERO,
// then use the default (configurable) logic, which will
// probably return null in order to check JDBC for the password.
return super.checkPassword(user, password, readOnly);
}
private String getOmeroDN(long id) {
try {
String dn = ldapUtil.lookupLdapAuthExperimenter(id);
if (log.isDebugEnabled()) {
log.debug(String.format("lookupLdap(%s)=%s", id, dn));
}
return dn;
} catch (ApiUsageException e) {
if (log.isDebugEnabled()) {
log.debug(String.format("lookupLdap(%s) is empty", id));
}
return null;
}
}
private String getLdapDN(String user) {
try {
String dn = ldapUtil.findDN(user);
if (log.isDebugEnabled()) {
log.debug(String.format("findDN(%s)=%s", user, dn));
}
return dn;
} catch (ApiUsageException e) {
if (log.isDebugEnabled()) {
log.debug(String.format("findDN(%s) is empty", user));
}
return null;
}
}
}