@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Support multiple LDAP filters in the Phabricator UI

Summary: Ref T3208. Not ready for prime time yet.

Test Plan: ldap T.T

Reviewers: btrahan

Reviewed By: btrahan

Subscribers: epriestley, frgtn, aran

Maniphest Tasks: T3208

Differential Revision: https://secure.phabricator.com/D8160

+94 -21
+94 -21
src/applications/auth/provider/PhabricatorAuthProviderLDAP.php
··· 30 30 $realname_attributes = array(); 31 31 } 32 32 33 + $search_attributes = $conf->getProperty(self::KEY_SEARCH_ATTRIBUTES); 34 + $search_attributes = phutil_split_lines($search_attributes, false); 35 + $search_attributes = array_filter($search_attributes); 36 + 33 37 $adapter = id(new PhutilAuthAdapterLDAP()) 34 38 ->setHostname( 35 39 $conf->getProperty(self::KEY_HOSTNAME)) ··· 37 41 $conf->getProperty(self::KEY_PORT)) 38 42 ->setBaseDistinguishedName( 39 43 $conf->getProperty(self::KEY_DISTINGUISHED_NAME)) 40 - ->setSearchAttribute( 41 - $conf->getProperty(self::KEY_SEARCH_ATTRIBUTE)) 44 + ->setSearchAttributes($search_attributes) 42 45 ->setUsernameAttribute( 43 46 $conf->getProperty(self::KEY_USERNAME_ATTRIBUTE)) 44 47 ->setRealNameAttributes($realname_attributes) ··· 53 56 ->setAnonymousPassword( 54 57 new PhutilOpaqueEnvelope( 55 58 $conf->getProperty(self::KEY_ANONYMOUS_PASSWORD))) 56 - ->setSearchFirst( 57 - $conf->getProperty(self::KEY_SEARCH_FIRST)) 58 59 ->setActiveDirectoryDomain( 59 60 $conf->getProperty(self::KEY_ACTIVEDIRECTORY_DOMAIN)); 60 61 $this->adapter = $adapter; ··· 167 168 } else { 168 169 throw new Exception("Username and password are required!"); 169 170 } 171 + } catch (PhutilAuthCredentialException $ex) { 172 + $response = $controller->buildProviderPageResponse( 173 + $this, 174 + $this->renderLoginForm($request, 'login')); 175 + return array($account, $response); 170 176 } catch (Exception $ex) { 171 177 // TODO: Make this cleaner. 172 178 throw $ex; ··· 180 186 const KEY_HOSTNAME = 'ldap:host'; 181 187 const KEY_PORT = 'ldap:port'; 182 188 const KEY_DISTINGUISHED_NAME = 'ldap:dn'; 183 - const KEY_SEARCH_ATTRIBUTE = 'ldap:search-attribute'; 189 + const KEY_SEARCH_ATTRIBUTES = 'ldap:search-attribute'; 184 190 const KEY_USERNAME_ATTRIBUTE = 'ldap:username-attribute'; 185 191 const KEY_REALNAME_ATTRIBUTES = 'ldap:realname-attributes'; 186 192 const KEY_VERSION = 'ldap:version'; ··· 188 194 const KEY_START_TLS = 'ldap:start-tls'; 189 195 const KEY_ANONYMOUS_USERNAME = 'ldap:anoynmous-username'; 190 196 const KEY_ANONYMOUS_PASSWORD = 'ldap:anonymous-password'; 191 - const KEY_SEARCH_FIRST = 'ldap:search-first'; 192 197 const KEY_ACTIVEDIRECTORY_DOMAIN = 'ldap:activedirectory-domain'; 193 198 194 199 private function getPropertyKeys() { ··· 200 205 self::KEY_HOSTNAME => pht('LDAP Hostname'), 201 206 self::KEY_PORT => pht('LDAP Port'), 202 207 self::KEY_DISTINGUISHED_NAME => pht('Base Distinguished Name'), 203 - self::KEY_SEARCH_ATTRIBUTE => pht('Search Attribute'), 208 + self::KEY_SEARCH_ATTRIBUTES => pht('Search Attributes'), 209 + self::KEY_ANONYMOUS_USERNAME => pht('Anonymous Username'), 210 + self::KEY_ANONYMOUS_PASSWORD => pht('Anonymous Password'), 204 211 self::KEY_USERNAME_ATTRIBUTE => pht('Username Attribute'), 205 212 self::KEY_REALNAME_ATTRIBUTES => pht('Realname Attributes'), 206 213 self::KEY_VERSION => pht('LDAP Version'), 207 214 self::KEY_REFERRALS => pht('Enable Referrals'), 208 215 self::KEY_START_TLS => pht('Use TLS'), 209 - self::KEY_SEARCH_FIRST => pht('Search First'), 210 - self::KEY_ANONYMOUS_USERNAME => pht('Anonymous Username'), 211 - self::KEY_ANONYMOUS_PASSWORD => pht('Anonymous Password'), 212 216 self::KEY_ACTIVEDIRECTORY_DOMAIN => pht('ActiveDirectory Domain'), 213 217 ); 214 218 } ··· 262 266 self::KEY_DISTINGUISHED_NAME => 263 267 pht('Example: %s', 264 268 phutil_tag('tt', array(), pht('ou=People, dc=example, dc=com'))), 265 - self::KEY_SEARCH_ATTRIBUTE => 269 + self::KEY_USERNAME_ATTRIBUTE => 266 270 pht('Example: %s', 267 271 phutil_tag('tt', array(), pht('sn'))), 268 - self::KEY_USERNAME_ATTRIBUTE => 269 - pht('Optional, if different from search attribute.'), 270 272 self::KEY_REALNAME_ATTRIBUTES => 271 - pht('Optional. Example: %s', 273 + pht('Example: %s', 272 274 phutil_tag('tt', array(), pht('firstname, lastname'))), 273 275 self::KEY_REFERRALS => 274 276 pht('Follow referrals. Disable this for Windows AD 2003.'), 275 277 self::KEY_START_TLS => 276 278 pht('Start TLS after binding to the LDAP server.'), 277 - self::KEY_SEARCH_FIRST => 278 - pht( 279 - 'When the user enters their username, search for a matching '. 280 - 'record using the "Search Attribute", then try to bind using '. 281 - 'the DN for the record. This is useful if usernames are not '. 282 - 'part of the record DN.'), 283 279 self::KEY_ANONYMOUS_USERNAME => 284 280 pht('Username to bind with before searching.'), 285 281 self::KEY_ANONYMOUS_PASSWORD => ··· 289 285 $types = array( 290 286 self::KEY_REFERRALS => 'checkbox', 291 287 self::KEY_START_TLS => 'checkbox', 292 - self::KEY_SEARCH_FIRST => 'checkbox', 288 + self::KEY_SEARCH_ATTRIBUTES => 'textarea', 293 289 self::KEY_REALNAME_ATTRIBUTES => 'list', 294 290 self::KEY_ANONYMOUS_PASSWORD => 'password', 295 291 ); 296 292 293 + $instructions = array( 294 + self::KEY_SEARCH_ATTRIBUTES => pht( 295 + "When a user types their LDAP username and password into Phabricator, ". 296 + "Phabricator can either bind to LDAP with those credentials directly ". 297 + "(which is simpler, but not as powerful) or bind to LDAP with ". 298 + "anonymous credentials, then search for record matching the supplied ". 299 + "credentials (which is more complicated, but more powerful).\n\n". 300 + "For many installs, direct binding is sufficient. However, you may ". 301 + "want to search first if:\n\n". 302 + " - You want users to be able to login with either their username ". 303 + " or their email address.\n". 304 + " - The login/username is not part of the distinguished name in ". 305 + " your LDAP records.\n". 306 + " - You want to restrict logins to a subset of users (like only ". 307 + " those in certain departments).\n". 308 + " - Your LDAP server is configured in some other way that prevents ". 309 + " direct binding from working correctly.\n\n". 310 + "**To bind directly**, enter the LDAP attribute corresponding to the ". 311 + "login name into this box. Often, this is something like `sn` or ". 312 + "`uid`. This is the simplest configuration, but will only work if the ". 313 + "username is part of the distinguished name, and won't let you apply ". 314 + "complex restrictions to logins.\n\n". 315 + " lang=text,name=Simple Direct Binding\n". 316 + " sn\n\n". 317 + "**To search first**, provide an anonymous username and password ". 318 + "below, then enter one or more search queries into this field, one ". 319 + "per line. After binding, these queries will be used to identify the ". 320 + "record associated with the login name the user typed.\n\n". 321 + "Searches will be tried in order until a matching record is found. ". 322 + "Each query can be a simple attribute name (like `sn` or `mail`), ". 323 + "which will search for a matching record, or it can be a complex ". 324 + "query that uses the string `\${login}` to represent the login ". 325 + "name.\n\n". 326 + "A common simple configuration is just an attribute name, like ". 327 + "`sn`, which will work the same way direct binding works:\n\n". 328 + " lang=text,name=Simple Example\n". 329 + " sn\n\n". 330 + "A slightly more complex configuration might let the user login with ". 331 + "either their login name or email address:\n\n". 332 + " lang=text,name=Match Several Attributes\n". 333 + " mail\n". 334 + " sn\n\n". 335 + "If your LDAP directory is more complex, or you want to perform ". 336 + "sophisticated filtering, you can use one or more queries. Depending ". 337 + "on your directory structure, this example might allow users to login ". 338 + "with either their email address or username, but only if they're in ". 339 + "specific departments:\n\n". 340 + " lang=text,name=Complex Example\n". 341 + " (&(mail=\${login})(|(departmentNumber=1)(departmentNumber=2)))\n". 342 + " (&(sn=\${login})(|(departmentNumber=1)(departmentNumber=2)))\n\n". 343 + "All of the attribute names used here are just examples: your LDAP ". 344 + "server may use different attribute names."), 345 + self::KEY_USERNAME_ATTRIBUTE => pht( 346 + "Optionally, specify a username attribute to use to prefill usernames ". 347 + "when registering a new account. This is purely cosmetic and does not ". 348 + "affect the login process, but you can configure it to make sure ". 349 + "users get the same default username as their LDAP username, so ". 350 + "usernames remain consistent across systems."), 351 + self::KEY_REALNAME_ATTRIBUTES => pht( 352 + "Optionally, specify one or more comma-separated attributes to use to ". 353 + "prefill the \"Real Name\" field when registering a new account. This ". 354 + "is purely cosmetic and does not affect the login process, but can ". 355 + "make registration a little easier."), 356 + ); 357 + 297 358 foreach ($labels as $key => $label) { 298 359 $caption = idx($captions, $key); 299 360 $type = idx($types, $key); ··· 323 384 ->setCaption($caption) 324 385 ->setValue($value); 325 386 break; 387 + case 'textarea': 388 + $control = id(new AphrontFormTextAreaControl()) 389 + ->setName($key) 390 + ->setLabel($label) 391 + ->setCaption($caption) 392 + ->setValue($value); 393 + break; 326 394 default: 327 395 $control = id(new AphrontFormTextControl()) 328 396 ->setName($key) ··· 330 398 ->setCaption($caption) 331 399 ->setValue($value); 332 400 break; 401 + } 402 + 403 + $instruction_text = idx($instructions, $key); 404 + if (strlen($instruction_text)) { 405 + $form->appendRemarkupInstructions($instruction_text); 333 406 } 334 407 335 408 $form->appendChild($control);