7
7
*/
8
8
package svnserver .auth ;
9
9
10
+ import com .unboundid .ldap .sdk .DIGESTMD5BindRequest ;
11
+ import com .unboundid .ldap .sdk .LDAPConnection ;
12
+ import com .unboundid .ldap .sdk .LDAPException ;
13
+ import com .unboundid .ldap .sdk .ResultCode ;
14
+ import com .unboundid .util .ssl .SSLUtil ;
15
+ import com .unboundid .util .ssl .TrustAllTrustManager ;
10
16
import org .jetbrains .annotations .NotNull ;
11
17
import org .jetbrains .annotations .Nullable ;
12
18
import org .slf4j .Logger ;
16
22
import org .tmatesoft .svn .core .SVNException ;
17
23
import svnserver .config .LDAPUserDBConfig ;
18
24
19
- import javax .naming .AuthenticationException ;
20
- import javax .naming .Context ;
21
- import javax .naming .NamingEnumeration ;
22
25
import javax .naming .NamingException ;
23
- import javax .naming .directory .*;
26
+ import javax .net .SocketFactory ;
27
+ import java .net .URI ;
28
+ import java .security .GeneralSecurityException ;
24
29
import java .text .MessageFormat ;
25
30
import java .util .Collection ;
26
31
import java .util .Collections ;
27
- import java .util .Hashtable ;
28
32
29
33
/**
30
34
* Authenticates a user by binding to the directory with the DN of the entry for that user and the password
@@ -42,64 +46,86 @@ public final class LDAPUserDB implements UserDB, PasswordChecker {
42
46
43
47
@ NotNull
44
48
private final LDAPUserDBConfig config ;
49
+ @ NotNull
50
+ private final String baseDn ;
51
+ @ NotNull
52
+ private final String ldapHost ;
53
+ private final int ldapPort ;
54
+ @ Nullable
55
+ private final SocketFactory socketFactory ;
45
56
46
57
public LDAPUserDB (@ NotNull LDAPUserDBConfig config ) {
58
+ URI ldapUri = URI .create (config .getConnectionUrl ());
59
+ SocketFactory factory ;
60
+ int defaultPort ;
61
+ switch (ldapUri .getScheme ().toLowerCase ()) {
62
+ case "ldap" :
63
+ factory = null ;
64
+ defaultPort = 389 ;
65
+ break ;
66
+ case "ldaps" :
67
+ factory = createSslFactory (config );
68
+ defaultPort = 636 ;
69
+ break ;
70
+ default :
71
+ throw new IllegalStateException ("Unknown ldap scheme: " + ldapUri .getScheme ());
72
+ }
73
+ this .socketFactory = factory ;
74
+ this .baseDn = ldapUri .getPath ().isEmpty () ? "" : ldapUri .getPath ().substring (1 );
47
75
this .config = config ;
76
+ this .ldapPort = ldapUri .getPort () > 0 ? ldapUri .getPort () : defaultPort ;
77
+ this .ldapHost = ldapUri .getHost ();
78
+ }
79
+
80
+ private static SocketFactory createSslFactory (@ NotNull LDAPUserDBConfig config ) {
81
+ try {
82
+ return new SSLUtil (null , new TrustAllTrustManager ()).createSSLSocketFactory ();
83
+ } catch (GeneralSecurityException e ) {
84
+ throw new IllegalStateException ("Can't create SSL Socket Factory" , e );
85
+ }
48
86
}
49
87
50
88
@ Nullable
51
89
@ Override
52
90
public User check (@ NotNull String username , @ NotNull String password ) throws SVNException {
53
- final Hashtable <String , Object > env = new Hashtable <>();
54
- env .put (Context .INITIAL_CONTEXT_FACTORY , config .getContextFactory ());
55
- env .put (Context .PROVIDER_URL , config .getConnectionUrl ());
56
- env .put (Context .SECURITY_AUTHENTICATION , config .getAuthentication ());
57
- env .put (Context .SECURITY_PRINCIPAL , username );
58
- env .put (Context .SECURITY_CREDENTIALS , password );
59
-
60
- InitialDirContext context = null ;
61
91
try {
62
- context = new InitialDirContext (env );
63
-
64
- final SearchControls searchControls = new SearchControls ();
65
- searchControls .setSearchScope (config .isUserSubtree () ? SearchControls .SUBTREE_SCOPE : SearchControls .ONELEVEL_SCOPE );
66
- searchControls .setReturningAttributes (new String []{config .getNameAttribute (), config .getEmailAttribute ()});
67
- searchControls .setCountLimit (2 );
68
-
69
- final NamingEnumeration <SearchResult > search = context .search ("" , MessageFormat .format (config .getUserSearch (), username ), searchControls );
70
- if (!search .hasMore ()) {
71
- log .debug ("Failed to find LDAP entry for {}" , username );
72
- return null ;
92
+ LDAPConnection ldap = new LDAPConnection (socketFactory , ldapHost , ldapPort );
93
+ try {
94
+ ldap .bind (new DIGESTMD5BindRequest (username , password ));
95
+ com .unboundid .ldap .sdk .SearchResult search = ldap .search (
96
+ baseDn ,
97
+ config .isUserSubtree () ? com .unboundid .ldap .sdk .SearchScope .SUB : com .unboundid .ldap .sdk .SearchScope .ONE ,
98
+ MessageFormat .format (config .getUserSearch (), username ),
99
+ config .getNameAttribute (), config .getEmailAttribute ()
100
+ );
101
+ if (search .getEntryCount () == 0 ) {
102
+ log .debug ("Failed to find LDAP entry for {}" , username );
103
+ return null ;
104
+ } else if (search .getEntryCount () > 1 ) {
105
+ log .error ("Multiple LDAP entries found for {}" , username );
106
+ return null ;
107
+ }
108
+ final com .unboundid .ldap .sdk .SearchResultEntry entry = search .getSearchEntries ().get (0 );
109
+ final String realName = getAttribute (entry , config .getNameAttribute ());
110
+ final String email = getAttribute (entry , config .getEmailAttribute ());
111
+ return new User (username , realName != null ? realName : username , email );
112
+ } finally {
113
+ ldap .close ();
73
114
}
74
-
75
- final Attributes attributes = search .next ().getAttributes ();
76
-
77
- if (search .hasMore ()) {
78
- log .error ("Multiple LDAP entries found for {}" , username );
115
+ } catch (LDAPException e ) {
116
+ if (e .getResultCode () == ResultCode .INVALID_CREDENTIALS ) {
79
117
return null ;
80
118
}
81
-
82
- final String realName = getAttribute (attributes , config .getNameAttribute ());
83
- final String email = getAttribute (attributes , config .getEmailAttribute ());
84
- return new User (username , realName != null ? realName : username , email );
85
- } catch (AuthenticationException e ) {
86
- return null ;
119
+ throw new SVNException (SVNErrorMessage .create (SVNErrorCode .AUTHN_NO_PROVIDER , e .getMessage ()), e );
87
120
} catch (NamingException e ) {
88
121
throw new SVNException (SVNErrorMessage .create (SVNErrorCode .AUTHN_NO_PROVIDER , e .getMessage ()), e );
89
- } finally {
90
- if (context != null )
91
- try {
92
- context .close ();
93
- } catch (NamingException e ) {
94
- log .error (e .getMessage (), e );
95
- }
96
122
}
97
123
}
98
124
99
125
@ Nullable
100
- private String getAttribute (@ NotNull Attributes attributes , @ NotNull String name ) throws NamingException {
101
- Attribute attribute = attributes . get (name );
102
- return attribute == null ? null : String . valueOf ( attribute .get () );
126
+ private String getAttribute (@ NotNull com . unboundid . ldap . sdk . SearchResultEntry entry , @ NotNull String name ) throws NamingException {
127
+ com . unboundid . ldap . sdk . Attribute attribute = entry . getAttribute (name );
128
+ return attribute == null ? null : attribute .getValue ( );
103
129
}
104
130
105
131
@ NotNull
0 commit comments