1#!/usr/bin/env python 2# -*- coding: utf-8 -*- 3"""LDAP methods module.""" 4from hvac import exceptions, utils 5from hvac.api.vault_api_base import VaultApiBase 6 7DEFAULT_MOUNT_POINT = "ldap" 8 9 10class Ldap(VaultApiBase): 11 """LDAP Auth Method (API). 12 13 Reference: https://www.vaultproject.io/api/auth/ldap/index.html 14 """ 15 16 def configure( 17 self, 18 user_dn=None, 19 group_dn=None, 20 url=None, 21 case_sensitive_names=None, 22 starttls=None, 23 tls_min_version=None, 24 tls_max_version=None, 25 insecure_tls=None, 26 certificate=None, 27 bind_dn=None, 28 bind_pass=None, 29 user_attr=None, 30 discover_dn=None, 31 deny_null_bind=True, 32 upn_domain=None, 33 group_filter=None, 34 group_attr=None, 35 use_token_groups=None, 36 token_ttl=None, 37 token_max_ttl=None, 38 mount_point=DEFAULT_MOUNT_POINT, 39 ): 40 """ 41 Configure the LDAP auth method. 42 43 Supported methods: 44 POST: /auth/{mount_point}/config. Produces: 204 (empty body) 45 46 :param user_dn: Base DN under which to perform user search. Example: ou=Users,dc=example,dc=com 47 :type user_dn: str | unicode 48 :param group_dn: LDAP search base to use for group membership search. This can be the root containing either 49 groups or users. Example: ou=Groups,dc=example,dc=com 50 :type group_dn: str | unicode 51 :param url: The LDAP server to connect to. Examples: ldap://ldap.myorg.com, ldaps://ldap.myorg.com:636. 52 Multiple URLs can be specified with commas, e.g. ldap://ldap.myorg.com,ldap://ldap2.myorg.com; these will be 53 tried in-order. 54 :type url: str | unicode 55 :param case_sensitive_names: If set, user and group names assigned to policies within the backend will be case 56 sensitive. Otherwise, names will be normalized to lower case. Case will still be preserved when sending the 57 username to the LDAP server at login time; this is only for matching local user/group definitions. 58 :type case_sensitive_names: bool 59 :param starttls: If true, issues a StartTLS command after establishing an unencrypted connection. 60 :type starttls: bool 61 :param tls_min_version: Minimum TLS version to use. Accepted values are tls10, tls11 or tls12. 62 :type tls_min_version: str | unicode 63 :param tls_max_version: Maximum TLS version to use. Accepted values are tls10, tls11 or tls12. 64 :type tls_max_version: str | unicode 65 :param insecure_tls: If true, skips LDAP server SSL certificate verification - insecure, use with caution! 66 :type insecure_tls: bool 67 :param certificate: CA certificate to use when verifying LDAP server certificate, must be x509 PEM encoded. 68 :type certificate: str | unicode 69 :param bind_dn: Distinguished name of object to bind when performing user search. Example: 70 cn=vault,ou=Users,dc=example,dc=com 71 :type bind_dn: str | unicode 72 :param bind_pass: Password to use along with binddn when performing user search. 73 :type bind_pass: str | unicode 74 :param user_attr: Attribute on user attribute object matching the username passed when authenticating. Examples: 75 sAMAccountName, cn, uid 76 :type user_attr: str | unicode 77 :param discover_dn: Use anonymous bind to discover the bind DN of a user. 78 :type discover_dn: bool 79 :param deny_null_bind: This option prevents users from bypassing authentication when providing an empty password. 80 :type deny_null_bind: bool 81 :param upn_domain: The userPrincipalDomain used to construct the UPN string for the authenticating user. The 82 constructed UPN will appear as [username]@UPNDomain. Example: example.com, which will cause vault to bind as 83 username@example.com. 84 :type upn_domain: str | unicode 85 :param group_filter: Go template used when constructing the group membership query. The template can access the 86 following context variables: [UserDN, Username]. The default is 87 `(|(memberUid={{.Username}})(member={{.UserDN}})(uniqueMember={{.UserDN}}))`, which is compatible with several 88 common directory schemas. To support nested group resolution for Active Directory, instead use the following 89 query: (&(objectClass=group)(member:1.2.840.113556.1.4.1941:={{.UserDN}})). 90 :type group_filter: str | unicode 91 :param group_attr: LDAP attribute to follow on objects returned by groupfilter in order to enumerate user group 92 membership. Examples: for groupfilter queries returning group objects, use: cn. For queries returning user 93 objects, use: memberOf. The default is cn. 94 :type group_attr: str | unicode 95 :param use_token_groups: If true, groups are resolved through Active Directory tokens. This may speed up nested 96 group membership resolution in large directories. 97 :type use_token_groups: bool 98 :param token_ttl: The incremental lifetime for generated tokens. 99 :type token_ttl: str | unicode 100 :param token_max_ttl: The maximum lifetime for generated tokens. 101 :type token_max_ttl: str | unicode 102 :param mount_point: The "path" the method/backend was mounted on. 103 :type mount_point: str | unicode 104 :return: The response of the configure request. 105 :rtype: requests.Response 106 """ 107 params = utils.remove_nones( 108 { 109 "url": url, 110 "userdn": user_dn, 111 "groupdn": group_dn, 112 "case_sensitive_names": case_sensitive_names, 113 "starttls": starttls, 114 "tls_min_version": tls_min_version, 115 "tls_max_version": tls_max_version, 116 "insecure_tls": insecure_tls, 117 "certificate": certificate, 118 "userattr": user_attr, 119 "discoverdn": discover_dn, 120 "deny_null_bind": deny_null_bind, 121 "groupfilter": group_filter, 122 "groupattr": group_attr, 123 "upndomain": upn_domain, 124 "binddn": bind_dn, 125 "bindpass": bind_pass, 126 "certificate": certificate, 127 "use_token_groups": use_token_groups, 128 "token_ttl": token_ttl, 129 "token_max_ttl": token_max_ttl, 130 } 131 ) 132 133 api_path = utils.format_url( 134 "/v1/auth/{mount_point}/config", mount_point=mount_point 135 ) 136 return self._adapter.post( 137 url=api_path, 138 json=params, 139 ) 140 141 def read_configuration(self, mount_point=DEFAULT_MOUNT_POINT): 142 """ 143 Retrieve the LDAP configuration for the auth method. 144 145 Supported methods: 146 GET: /auth/{mount_point}/config. Produces: 200 application/json 147 148 :param mount_point: The "path" the method/backend was mounted on. 149 :type mount_point: str | unicode 150 :return: The JSON response of the read_configuration request. 151 :rtype: dict 152 """ 153 api_path = utils.format_url( 154 "/v1/auth/{mount_point}/config", mount_point=mount_point 155 ) 156 return self._adapter.get( 157 url=api_path, 158 ) 159 160 def create_or_update_group( 161 self, name, policies=None, mount_point=DEFAULT_MOUNT_POINT 162 ): 163 """ 164 Create or update LDAP group policies. 165 166 Supported methods: 167 POST: /auth/{mount_point}/groups/{name}. Produces: 204 (empty body) 168 169 170 :param name: The name of the LDAP group 171 :type name: str | unicode 172 :param policies: List of policies associated with the group. This parameter is transformed to a comma-delimited 173 string before being passed to Vault. 174 :type policies: list 175 :param mount_point: The "path" the method/backend was mounted on. 176 :type mount_point: str | unicode 177 :return: The response of the create_or_update_group request. 178 :rtype: requests.Response 179 """ 180 if policies is not None and not isinstance(policies, list): 181 error_msg = '"policies" argument must be an instance of list or None, "{policies_type}" provided.'.format( 182 policies_type=type(policies), 183 ) 184 raise exceptions.ParamValidationError(error_msg) 185 186 params = {} 187 if policies is not None: 188 params["policies"] = ",".join(policies) 189 api_path = utils.format_url( 190 "/v1/auth/{mount_point}/groups/{name}", 191 mount_point=mount_point, 192 name=name, 193 ) 194 return self._adapter.post( 195 url=api_path, 196 json=params, 197 ) 198 199 def list_groups(self, mount_point=DEFAULT_MOUNT_POINT): 200 """ 201 List existing LDAP existing groups that have been created in this auth method. 202 203 Supported methods: 204 LIST: /auth/{mount_point}/groups. Produces: 200 application/json 205 206 207 :param mount_point: The "path" the method/backend was mounted on. 208 :type mount_point: str | unicode 209 :return: The JSON response of the list_groups request. 210 :rtype: dict 211 """ 212 api_path = utils.format_url( 213 "/v1/auth/{mount_point}/groups", mount_point=mount_point 214 ) 215 return self._adapter.list( 216 url=api_path, 217 ) 218 219 def read_group(self, name, mount_point=DEFAULT_MOUNT_POINT): 220 """ 221 Read policies associated with a LDAP group. 222 223 Supported methods: 224 GET: /auth/{mount_point}/groups/{name}. Produces: 200 application/json 225 226 227 :param name: The name of the LDAP group 228 :type name: str | unicode 229 :param mount_point: The "path" the method/backend was mounted on. 230 :type mount_point: str | unicode 231 :return: The JSON response of the read_group request. 232 :rtype: dict 233 """ 234 params = { 235 "name": name, 236 } 237 api_path = utils.format_url( 238 "/v1/auth/{mount_point}/groups/{name}", 239 mount_point=mount_point, 240 name=name, 241 ) 242 return self._adapter.get( 243 url=api_path, 244 json=params, 245 ) 246 247 def delete_group(self, name, mount_point=DEFAULT_MOUNT_POINT): 248 """ 249 Delete a LDAP group and policy association. 250 251 Supported methods: 252 DELETE: /auth/{mount_point}/groups/{name}. Produces: 204 (empty body) 253 254 255 :param name: The name of the LDAP group 256 :type name: str | unicode 257 :param mount_point: The "path" the method/backend was mounted on. 258 :type mount_point: str | unicode 259 :return: The response of the delete_group request. 260 :rtype: requests.Response 261 """ 262 api_path = utils.format_url( 263 "/v1/auth/{mount_point}/groups/{name}", 264 mount_point=mount_point, 265 name=name, 266 ) 267 return self._adapter.delete( 268 url=api_path, 269 ) 270 271 def create_or_update_user( 272 self, username, policies=None, groups=None, mount_point=DEFAULT_MOUNT_POINT 273 ): 274 """ 275 Create or update LDAP users policies and group associations. 276 277 Supported methods: 278 POST: /auth/{mount_point}/users/{username}. Produces: 204 (empty body) 279 280 281 :param username: The username of the LDAP user 282 :type username: str | unicode 283 :param policies: List of policies associated with the user. This parameter is transformed to a comma-delimited 284 string before being passed to Vault. 285 :type policies: str | unicode 286 :param groups: List of groups associated with the user. This parameter is transformed to a comma-delimited 287 string before being passed to Vault. 288 :type groups: str | unicode 289 :param mount_point: The "path" the method/backend was mounted on. 290 :type mount_point: str | unicode 291 :return: The response of the create_or_update_user request. 292 :rtype: requests.Response 293 """ 294 list_required_params = { 295 "policies": policies, 296 "groups": groups, 297 } 298 for param_name, param_arg in list_required_params.items(): 299 if param_arg is not None and not isinstance(param_arg, list): 300 error_msg = '"{param_name}" argument must be an instance of list or None, "{param_type}" provided.'.format( 301 param_name=param_name, 302 param_type=type(param_arg), 303 ) 304 raise exceptions.ParamValidationError(error_msg) 305 306 params = {} 307 if policies is not None: 308 params["policies"] = ",".join(policies) 309 if groups is not None: 310 params["groups"] = ",".join(groups) 311 api_path = utils.format_url( 312 "/v1/auth/{mount_point}/users/{username}", 313 mount_point=mount_point, 314 username=username, 315 ) 316 return self._adapter.post( 317 url=api_path, 318 json=params, 319 ) 320 321 def list_users(self, mount_point=DEFAULT_MOUNT_POINT): 322 """ 323 List existing users in the method. 324 325 Supported methods: 326 LIST: /auth/{mount_point}/users. Produces: 200 application/json 327 328 329 :param mount_point: The "path" the method/backend was mounted on. 330 :type mount_point: str | unicode 331 :return: The JSON response of the list_users request. 332 :rtype: dict 333 """ 334 api_path = utils.format_url( 335 "/v1/auth/{mount_point}/users", mount_point=mount_point 336 ) 337 return self._adapter.list( 338 url=api_path, 339 ) 340 341 def read_user(self, username, mount_point=DEFAULT_MOUNT_POINT): 342 """ 343 Read policies associated with a LDAP user. 344 345 Supported methods: 346 GET: /auth/{mount_point}/users/{username}. Produces: 200 application/json 347 348 349 :param username: The username of the LDAP user 350 :type username: str | unicode 351 :param mount_point: The "path" the method/backend was mounted on. 352 :type mount_point: str | unicode 353 :return: The JSON response of the read_user request. 354 :rtype: dict 355 """ 356 api_path = utils.format_url( 357 "/v1/auth/{mount_point}/users/{username}", 358 mount_point=mount_point, 359 username=username, 360 ) 361 return self._adapter.get( 362 url=api_path, 363 ) 364 365 def delete_user(self, username, mount_point=DEFAULT_MOUNT_POINT): 366 """ 367 Delete a LDAP user and policy association. 368 369 Supported methods: 370 DELETE: /auth/{mount_point}/users/{username}. Produces: 204 (empty body) 371 372 373 :param username: The username of the LDAP user 374 :type username: str | unicode 375 :param mount_point: The "path" the method/backend was mounted on. 376 :type mount_point: str | unicode 377 :return: The response of the delete_user request. 378 :rtype: requests.Response 379 """ 380 api_path = utils.format_url( 381 "/v1/auth/{mount_point}/users/{username}", 382 mount_point=mount_point, 383 username=username, 384 ) 385 return self._adapter.delete( 386 url=api_path, 387 ) 388 389 def login( 390 self, username, password, use_token=True, mount_point=DEFAULT_MOUNT_POINT 391 ): 392 """ 393 Log in with LDAP credentials. 394 395 Supported methods: 396 POST: /auth/{mount_point}/login/{username}. Produces: 200 application/json 397 398 399 :param username: The username of the LDAP user 400 :type username: str | unicode 401 :param password: The password for the LDAP user 402 :type password: str | unicode 403 :param use_token: if True, uses the token in the response received from the auth request to set the "token" 404 attribute on the the :py:meth:`hvac.adapters.Adapter` instance under the _adapater Client attribute. 405 :type use_token: bool 406 :param mount_point: The "path" the method/backend was mounted on. 407 :type mount_point: str | unicode 408 :return: The response of the login_with_user request. 409 :rtype: requests.Response 410 """ 411 params = { 412 "password": password, 413 } 414 api_path = utils.format_url( 415 "/v1/auth/{mount_point}/login/{username}", 416 mount_point=mount_point, 417 username=username, 418 ) 419 return self._adapter.login( 420 url=api_path, 421 use_token=use_token, 422 json=params, 423 ) 424