Skip to content

Commit

Permalink
[PLAT-13651]LDAP sync should have an option to extract users from oth…
Browse files Browse the repository at this point in the history
…er attributes as well

Summary:
Currently during our YBA ↔︎ YBDB LDAP sync, we assume that the user name we want to sync will be present on the DN. While that will be true for most of the scenarios, but we can have customers wanting to sync the user present on a different attribute for example - `sAMAccountName`

This diff performs the sync based on the attribute the user specified in the payload. If the user has specified `ldapUserfieldAttribute`, the user name will be always retrieved from this attribute on the LDAP server. If this is not specified, `ldapUserfield` should be specified [this is to get the user name from the dn] else the sync fails with the message: `Either of the ldapUserfield or ldapUserfieldAttribute is necessary to perform the sync`

Test Plan:
  - Triggered the sync with the `ldapUserfieldAttribute` and synced only the users that have this attribute set on the LDAP server
  - Triggered the sync with the `ldapUserfield` and observed the sync where the user name is retrieved from the dn
  - Triggered the sync with both the `ldapUserfieldAttribute` and the `ldapUserfield` configured, and the pref is given to `ldapUserfieldAttribute`
  - Triggered the sync with specifying neither of the `ldapUserfieldAttribute` and the `ldapUserfield` and observed the exception thrown.

Reviewers: #yba-api-review!, svarshney

Reviewed By: svarshney

Subscribers: yugaware

Differential Revision: https://phorge.dev.yugabyte.com/D34412
  • Loading branch information
rohita committed Apr 24, 2024
1 parent d217164 commit 55278fe
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,42 @@ private void queryLdap(LdapNetworkConnection connection, boolean enabledDetailed
ldapUnivSyncFormData.getLdapBasedn(),
ldapUnivSyncFormData.getLdapSearchFilter(),
SearchScope.SUBTREE,
ldapUnivSyncFormData.getLdapGroupMemberOfAttribute());
"*");

while (cursor.next()) {
Entry entry = cursor.get();
if (enabledDetailedLogs) {
log.debug("LDAP user entry retrieved: {}", entry.toString());
}
String dn = entry.getDn().toString();
String userKey = retrieveValueFromDN(dn, ldapUnivSyncFormData.getLdapUserfield());

String userKey = "";
// user's name is retrieved from the ldapUserfieldAttribute if specified
if (StringUtils.isNotEmpty(ldapUnivSyncFormData.getLdapUserfieldAttribute())) {
Attribute userAttribute = entry.get(ldapUnivSyncFormData.getLdapUserfieldAttribute());
if (enabledDetailedLogs) {
log.debug("User's name retrieved from attribute: '{}'", userAttribute);
}
if (userAttribute != null) {
userKey = userAttribute.get().getString();
} else {
if (enabledDetailedLogs) {
log.warn(
"{} is not set for this user on the LDAP Server, hence excluded from the sync",
ldapUnivSyncFormData.getLdapUserfieldAttribute());
}
}
} else {
// user's name is retrieved from the dn if ldapUserfieldAttribute is not specified
String dn = entry.getDn().toString();
if (enabledDetailedLogs) {
log.debug(
"User's name retrieved from {} in {}", ldapUnivSyncFormData.getLdapUserfield(), dn);
}
userKey = retrieveValueFromDN(dn, ldapUnivSyncFormData.getLdapUserfield());
}
if (enabledDetailedLogs) {
log.debug("user name retrieved: {}", userKey);
}

if (!StringUtils.isEmpty(userKey)) {
Attribute groups = entry.get(ldapUnivSyncFormData.getLdapGroupMemberOfAttribute());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.Authorization;
import java.util.UUID;
import org.apache.commons.lang3.StringUtils;
import play.mvc.Http;
import play.mvc.Result;

Expand Down Expand Up @@ -92,6 +93,13 @@ public Result syncUniverse(UUID customerUUID, UUID universeUUID, Http.Request re
LdapUnivSyncFormData ldapUnivSyncFormData =
formFactory.getFormDataOrBadRequest(request.body().asJson(), LdapUnivSyncFormData.class);

if (StringUtils.isEmpty(ldapUnivSyncFormData.getLdapUserfield())
&& StringUtils.isEmpty(ldapUnivSyncFormData.getLdapUserfieldAttribute())) {
errorMsg =
"Either of the ldapUserfield or ldapUserfieldAttribute is necessary to perform the sync";
throw new PlatformServiceException(BAD_REQUEST, errorMsg);
}

if (ldapUnivSyncFormData.getTargetApi().equals(LdapUnivSyncFormData.TargetApi.ycql)) {
if (!(ldapUnivSyncFormData.getDbUser().equals("cassandra"))) {
errorMsg = "Sync can be performed only by the dbUser(YCQL): cassandra";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,15 @@ public enum TargetApi {
private String ldapGroupMemberOfAttribute = "memberOf";

@ApiModelProperty(
value = "LDAP field to get the user information",
required = true,
example = "cn")
@Constraints.Required()
value = "LDAP attribute to get the user's name from",
example = "sAMAccountName")
private String ldapUserfieldAttribute;

@ApiModelProperty(value = "User dn field to get the user's name from", example = "cn")
private String ldapUserfield;

@ApiModelProperty(
value = "LDAP field to get the group information",
value = "Group dn field to get the group's name from",
required = true,
example = "cn")
@Constraints.Required()
Expand Down
11 changes: 8 additions & 3 deletions managed/src/main/resources/swagger-strict.json
Original file line number Diff line number Diff line change
Expand Up @@ -6569,7 +6569,7 @@
"type" : "string"
},
"ldapGroupfield" : {
"description" : "LDAP field to get the group information",
"description" : "Group dn field to get the group's name from",
"example" : "cn",
"type" : "string"
},
Expand All @@ -6593,10 +6593,15 @@
"type" : "string"
},
"ldapUserfield" : {
"description" : "LDAP field to get the user information",
"description" : "User dn field to get the user's name from",
"example" : "cn",
"type" : "string"
},
"ldapUserfieldAttribute" : {
"description" : "LDAP attribute to get the user's name from",
"example" : "sAMAccountName",
"type" : "string"
},
"targetApi" : {
"enum" : [ "ysql", "ycql" ],
"type" : "string"
Expand All @@ -6606,7 +6611,7 @@
"type" : "boolean"
}
},
"required" : [ "dbuserPassword", "ldapGroupfield", "ldapUserfield", "targetApi" ],
"required" : [ "dbuserPassword", "ldapGroupfield", "targetApi" ],
"type" : "object"
},
"LocalCloudInfo" : {
Expand Down
11 changes: 8 additions & 3 deletions managed/src/main/resources/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -6616,7 +6616,7 @@
"type" : "string"
},
"ldapGroupfield" : {
"description" : "LDAP field to get the group information",
"description" : "Group dn field to get the group's name from",
"example" : "cn",
"type" : "string"
},
Expand All @@ -6640,10 +6640,15 @@
"type" : "string"
},
"ldapUserfield" : {
"description" : "LDAP field to get the user information",
"description" : "User dn field to get the user's name from",
"example" : "cn",
"type" : "string"
},
"ldapUserfieldAttribute" : {
"description" : "LDAP attribute to get the user's name from",
"example" : "sAMAccountName",
"type" : "string"
},
"targetApi" : {
"enum" : [ "ysql", "ycql" ],
"type" : "string"
Expand All @@ -6653,7 +6658,7 @@
"type" : "boolean"
}
},
"required" : [ "dbuserPassword", "ldapGroupfield", "ldapUserfield", "targetApi" ],
"required" : [ "dbuserPassword", "ldapGroupfield", "targetApi" ],
"type" : "object"
},
"LocalCloudInfo" : {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ public void testLdapUnivSyncWithInvalidYcqlParams() {
}

@Test
public void testLdapUnivSyncWithoutUserfield() {
public void testLdapUnivSyncWithoutUserfieldAndUserfieldAttr() {
setUniverseRuntimeConfig("true");
ldapUnivSyncFormData.setTargetApi(LdapUnivSyncFormData.TargetApi.ysql);
ldapUnivSyncFormData.setDbUser("");
Expand All @@ -179,7 +179,10 @@ public void testLdapUnivSyncWithoutUserfield() {
() -> syncLdapWithUniv(defaultUniverse.getUniverseUUID(), bodyJson));
assertEquals(BAD_REQUEST, result.status());
JsonNode resultJson = Json.parse(contentAsString(result));
assertErrorNodeValue(resultJson, "ldapUserfield", "error.required");
assertValue(
resultJson,
"error",
"Either of the ldapUserfield or ldapUserfieldAttribute is necessary to perform the sync");
assertAuditEntry(0, defaultCustomer.getUuid());
}

Expand Down

0 comments on commit 55278fe

Please sign in to comment.