1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.bremersee.security.core.userdetails;
18
19 import java.util.Collections;
20 import lombok.AccessLevel;
21 import lombok.Getter;
22 import lombok.extern.slf4j.Slf4j;
23 import org.bremersee.data.ldaptive.LdaptiveOperations;
24 import org.ldaptive.CompareRequest;
25 import org.ldaptive.FilterTemplate;
26 import org.ldaptive.LdapEntry;
27 import org.ldaptive.SearchRequest;
28 import org.ldaptive.SearchScope;
29 import org.springframework.security.crypto.password.PasswordEncoder;
30 import org.springframework.util.StringUtils;
31
32
33
34
35
36
37 @Slf4j
38 public class LdaptivePasswordMatcher implements PasswordEncoder {
39
40 @Getter(value = AccessLevel.PROTECTED)
41 private final LdaptiveOperations ldaptiveOperations;
42
43 @Getter(value = AccessLevel.PROTECTED)
44 private final String userBaseDn;
45
46 @Getter(value = AccessLevel.PROTECTED)
47 private final String userFindOneFilter;
48
49 @Getter(value = AccessLevel.PROTECTED)
50 private SearchScope userFindOneSearchScope = SearchScope.ONELEVEL;
51
52 @Getter(value = AccessLevel.PROTECTED)
53 private String userPasswordAttributeName = "userPassword";
54
55 @Getter(value = AccessLevel.PROTECTED)
56 private PasswordEncoder delegate = new LdaptivePasswordEncoder();
57
58
59
60
61
62
63
64
65 public LdaptivePasswordMatcher(
66 LdaptiveOperations ldaptiveOperations,
67 String userBaseDn,
68 String userFindOneFilter) {
69
70 this.ldaptiveOperations = ldaptiveOperations;
71 this.userBaseDn = userBaseDn;
72 this.userFindOneFilter = userFindOneFilter;
73 }
74
75
76
77
78
79
80 public void setUserFindOneSearchScope(SearchScope userFindOneSearchScope) {
81 if (userFindOneSearchScope != null) {
82 this.userFindOneSearchScope = userFindOneSearchScope;
83 }
84 }
85
86
87
88
89
90
91 public void setUserPasswordAttributeName(String userPasswordAttributeName) {
92 if (StringUtils.hasText(userPasswordAttributeName)) {
93 this.userPasswordAttributeName = userPasswordAttributeName;
94 }
95 }
96
97
98
99
100
101
102 public void setDelegate(PasswordEncoder delegate) {
103 if (delegate != null) {
104 this.delegate = delegate;
105 }
106 }
107
108 @Override
109 public String encode(CharSequence rawPassword) {
110 return getDelegate().encode(rawPassword);
111 }
112
113
114
115
116
117
118
119
120
121
122
123 @Override
124 public boolean matches(CharSequence rawPassword, String userName) {
125 if (!StringUtils.hasText(userName)) {
126 log.warn("Ldaptive password matcher: password does not match because there is no user name.");
127 return false;
128 }
129 String raw = rawPassword != null ? rawPassword.toString() : "";
130 boolean result = getLdaptiveOperations()
131 .findOne(SearchRequest.builder()
132 .dn(getUserBaseDn())
133 .filter(FilterTemplate.builder()
134 .filter(getUserFindOneFilter())
135 .parameters(userName)
136 .build())
137 .scope(getUserFindOneSearchScope())
138 .returnAttributes(Collections.emptyList())
139 .sizeLimit(1)
140 .build())
141 .map(LdapEntry::getDn)
142 .map(dn -> authenticate(dn, raw))
143 .orElse(false);
144 if (log.isDebugEnabled()) {
145 log.debug("Ldaptive password matcher: password matches for user ({})? {}", userName, result);
146 }
147 return result;
148 }
149
150 private boolean authenticate(String dn, String rawPassword) {
151 return getLdaptiveOperations().compare(CompareRequest.builder()
152 .dn(dn)
153 .name(getUserPasswordAttributeName())
154 .value(encode(rawPassword))
155 .build());
156 }
157
158 }