LDAP Ambiguous Name Resolution (ANR) equivalent on Microsoft Graph - azure-active-directory

I'm building a tool that can target both on-prem Active Directory and Azure AD that, amongst other things, searches user objects.
For on-prem, I can do an LDAP search using ambiguous name resolution with a query like this:
(anr=searchstring*)
Is there an equivalent filter string for the Microsoft Graph?

The equivalent to {string}* is the OData startsWith({property},'{string}') Filter parmeter:
/v1.0/users?$filter=startsWith(displayName,'Adele')
Note that the Microsoft Graph currently only supports a subset of OData query parameters, particularly when it comes to AAD filters:
The following $filter operators are not supported for Azure AD resources: ne, gt, ge, lt, le, and not.
Also, Microsoft Graph does not support contains or endswith string functions in any workload.
You can try test your queries this using Graph Explorer.

Okay, so after some trial & error, I managed to replicate Ambiguous Name Resolution using the filter syntax. It's not an exact match because not all attributes are searchable, but I guess it will have to do.
Here's my helper class I created for the job.
public static class QueryHelper
{
public static string GenerateAnrFilter(string query)
{
if (string.IsNullOrEmpty(query))
return query;
var tokens = query.Split(' ');
var filterClauses = new List<string>();
if (tokens.Count() > 1)
{
var otherTokens = string.Join(" ", tokens.Skip(1));
string nameFilter1 = $"({generateFilterClause(tokens[0], "givenName")} and {generateFilterClause(otherTokens, "surname")})";
filterClauses.Add(nameFilter1);
string nameFilter2 = $"({generateFilterClause(otherTokens, "givenName")} and {generateFilterClause(tokens[0], "surname")})";
filterClauses.Add(nameFilter2);
filterClauses.Add(generateFilterClause(query, "displayName"));
//filterClauses.Add(generateFilterClause(query, "officeLocation")); // not supported for filter
filterClauses.Add(generateMultiValueFilterClause(query, "proxyAddresses"));
filterClauses.Add(generateFilterClause(query, "userPrincipalName"));
//filterClauses.Add(generateFilterClause(query, "onPremisesSamAccountName")); // not supported for filter
filterClauses.Add(generateFilterClause(query, "mail"));
filterClauses.Add(generateFilterClause(query, "mailNickName"));
}
else
{
filterClauses.Add(generateFilterClause(query, "displayName"));
filterClauses.Add(generateFilterClause(query, "givenName"));
filterClauses.Add(generateFilterClause(query, "surname"));
//filterClauses.Add(generateFilterClause(query, "officeLocation")); // not supported for filter
filterClauses.Add(generateMultiValueFilterClause(query, "proxyAddresses"));
filterClauses.Add(generateFilterClause(query, "userPrincipalName"));
//filterClauses.Add(generateFilterClause(query, "onPremisesSamAccountName")); // not suported for filter
filterClauses.Add(generateFilterClause(query, "mail"));
filterClauses.Add(generateFilterClause(query, "mailNickName"));
}
var fullQuery = string.Join(" or ", filterClauses);
return fullQuery;
}
private static string generateMultiValueFilterClause(string query, string attributeName)
{
return $"{attributeName}/any(a:startswith(a,'{query}'))";
}
private static string generateFilterClause(string query, string attributeName)
{
return $"startsWith({attributeName},'{query}')";
}
}

Related

Find all IRIs used in ontology which are not entities?

Is there any straight-forward way in OWLAPI to find all IRIs used in an ontology which have not been identified as Entities i.e. have not been declared and are not used in a context which would allowed them to be identified as a specific entity type? Hoping for something analogous to OWLOntology.signature(), didn't see anything.
An example of that situation appears in BFO 2.0 (http://purl.obolibrary.org/obo/bfo.owl) :
<rdf:Description rdf:about="http://example.com/bfo-spec-label">
<obo:IAO_0000119>Person:Alan Ruttenberg</obo:IAO_0000119>
</rdf:Description>
Here http://example.com/bfo-spec-label is just a "bare" IRI of unknown entity type and thus does not appear in the ontology signature.
Couldn't find any elegant way to find all of these bare IRI's but these can be found by looking in all places they could possibly occur. A simple method would look like:
private List findBareIRIs(OWLOntology onto) {
List bares = new ArrayList();
// bare IRIs can occur as AnnotationSubjects, AnnotationObjects or the domain/range of AnnotationProperties
List<OWLAnnotationAssertionAxiom> asserts = OWLAPIStreamUtils.asList(onto.axioms(AxiomType.ANNOTATION_ASSERTION));
List<OWLAnnotationPropertyDomainAxiom> domains = OWLAPIStreamUtils.asList(onto.axioms(AxiomType.ANNOTATION_PROPERTY_DOMAIN));
List<OWLAnnotationPropertyRangeAxiom> ranges = OWLAPIStreamUtils.asList(onto.axioms(AxiomType.ANNOTATION_PROPERTY_RANGE));
//check the subject and values of each AnnotationAsertion
for (OWLAnnotationAssertionAxiom ax : asserts) {
OWLAnnotationSubject subj = ax.getSubject();
OWLAnnotationValue value = ax.getValue();
if (subj.isIRI()) {
bares.add((IRI) subj);
}
if (value.isIRI()) {
bares.add((IRI) value);
}
}
// check the domain and ranges of each AnnotationProperty
for (OWLAnnotationPropertyDomainAxiom ax : domains) {
bares.add(ax.getDomain());
}
for (OWLAnnotationPropertyRangeAxiom ax : ranges) {
bares.add(ax.getRange());
}
return bares;
}

Spring LDAP search with organisational units returns zero results

I'm working on a plain java command line software which performs a recursive LDAP search with Spring LDAP, starting from a specified group and searching all the users from the specified groups and subgroups.
The search fails to find anything if the group distinguished name contains organisational units (=ou), but works in other cases.
Here is the short version of implementation, recursion omitted:
private void searchLdapGroup(List<UserDTO> users, LdapTemplate ldapTemplate, String groupName) {
// recursion guard omitted
String base = groupName.substring(groupName.indexOf(',') + 1);
AndFilter filter = new AndFilter().and(new EqualsFilter("objectclass", "group")).and(new EqualsFilter("memberof", groupName));
List<String> subgroups = ldapTemplate.search(base, filter.encode(), new GroupNameMapper());
// recursive calls for subgroups omitted
getAllUsers(users, ldapTemplate, groupName, base);
}
private void getAllUsers(List<UserDTO> users, LdapTemplate ldapTemplate, String groupName, String base) {
AndFilter filter = new AndFilter().and(new EqualsFilter("objectclass", "person")).and(new EqualsFilter("memberof", groupName));
// Paged search omitted.
List<UserDTO> result = ldapTemplate.search(base,filter.encode(),new UserAttributesMapper());
users.addAll(result);
}
The GroupNameMapper returns distinguishedName as Strings and UserAttributesMapper returns user objects from different attributes such as sAMAccountName and givenName.
The code (with recursion) finds all the 36 users in the first test group, where the specified group is like:
CN=import_users,CN=Users,DC=example,DC=test,DC=org
in the same exact test environment it returns zero persons and subgroups when the group distinguished name contains one or more organisational units, such as
CN=import_users,OU=testou,DC=example,DC=test,DC=org
This can't be due to wrong group distinguished name, "memberof" not working or group containing no users, since i tested lookup:
String[] test = (String[])ldapTemplate.lookup("CN=import_users,OU=testou,DC=example,DC=test,DC=org", new ContextMapper() {
public Object mapFromContext(Object ctx) {
DirContextAdapter adapter = (DirContextAdapter) ctx;
return adapter.getStringAttributes("Member");
}
});
which finds
CN=John Doe,CN=Users,DC=example,DC=test,DC=org
and lookup for the user John Doe
String[] test = (String[])ldapTemplate.lookup("CN=John Doe,CN=Users,DC=example,DC=test,DC=org", new ContextMapper() {
public Object mapFromContext(Object ctx) {
DirContextAdapter adapter = (DirContextAdapter) ctx;
return adapter.getStringAttributes("memberof");
}
});
gives results:
CN=import_users,OU=testou,DC=example,DC=test,DC=org
CN=import_users,CN=Users,DC=example,DC=test,DC=org
How come the search does not find anything when organisational units are involved?
Library user:
spring-ldap-core - 2.0.4.RELEASE
The devil is in the details:
The member of the group CN=import_users,OU=testou,DC=example,DC=test,DC=org is
CN=John Doe,CN=Users,DC=example,DC=test,DC=org
But you appear to be searching for users under
OU=testou,DC=example,DC=test,DC=org
That is, it appears all users are under CN=Users,DC=example,DC=test,DC=org, but when you are actually searching for users you assume they are placed relative to the group.

DateTime Issue with Laravel 5.2 & MS SQL Server

Found this, but it didn't help: https://laracasts.com/discuss/channels/eloquent/timestampdatetime-issue-with-51-sqlserver
When I set my date field on the model like so protected $dates = ['OrderDate','TimeStamp']; then call $order->OrderDate I get the following error:
InvalidArgumentException with message 'Unexpected data found.
Unexpected data found.
The separation symbol could not be found
The format separator does not match
Trailing data'
But, if I manually create a Carbon date object using the same formatting and copying\pasting the date directly from SQL Server, it does so successfully:
>>> Carbon\Carbon::createFromFormat('Y-m-d H:i:s.000', '2015-12-29 00:00:00.000');
=> Carbon\Carbon {#835
+"date": "2015-12-29 00:00:00.000000",
+"timezone_type": 3,
+"timezone": "UTC",
}
What am I missing?
For some reason Laravel was seeing it as M j Y h:i:s:000A -- probably the driver. I added protected $dateFormat = 'M j Y h:i:s:000A'; to all the MS SQL Server models I am using.
The workaround would be to setup accessors and mutators and parse date manually, instead of putting it in $dates array.
Something like that:
public function getOrderDateAttribute($value)
{
return Carbon::createFromFormat('Y-m-d H:i:s.000', $value);
}
public function getTimeStampAttribute($value)
{
return Carbon::createFromFormat('Y-m-d H:i:s.000', $value);
}
public function setOrderDateAttribute($value)
{
$this->attributes['OrderDate'] = Carbon::createFromFormat('Y-m-d H:i:s', $value)->format('Y-m-d H:i:s.000');
}
public function setTimeStampAttribute($value)
{
$this->attributes['TimeStamp'] = Carbon::createFromFormat('Y-m-d H:i:s.000', $value)->format('Y-m-d H:i:s.000');
}

Passing a full-text search parameter using Dapper.net

Consider this simple query which use full text searching on the Keywords field:
DECLARE #searchTerm VARCHAR(500) = 'painted'
SELECT * FROM StockCatalogueItems
WHERE (CONTAINS(KeyWords, #searchTerm))
This works as expected, but I need to do the same using a Dapper.net parameterised query. When using stored procedures, I create the full text parameter like this: "\"painted*\""
But using the same approach this doesn't work using dapper. No results are returned. This is the line in the query where I use the parameter:
AND (CONTAINS(KeyWords, #search))
and it's passed to the query like so:
return _context.Database.Connection.Query<StockProfileMatrix>(basequery, new
{
search = searchTerm
}
I can only assume that dapper is sanitising the string somehow, removing quotes perhaps?
Any ideas?
This works for me. However the tech stack am working on is .net core RTM and "Dapper": "1.50.0-rc3",
_dbConnection.QueryAsync<Guid>(#"select br.Id from Brand br where CONTAINS(br.Text,#Name)",new {Name = $"\"*{name}*\""}))
For completeness, I'll answer the question. The query syntax is correct, but the way in which the full-text parameter is created was obviously not. I created an extension method that formats the parameter:
public static string ToFullText(this string str)
{
string searchTerm = null;
if (!string.IsNullOrEmpty(str))
{
string[] keywords = str.Trim().Split(null);
foreach (var keyword in keywords)
{
searchTerm += string.Format("\"{0}*\" AND ", keyword);
}
if (searchTerm != null)
searchTerm = searchTerm.Substring(0, searchTerm.LastIndexOf(" AND "));
}
return searchTerm;
}
Then I call the method when I pass the parameter in to the dapper query:
_context.Database.Connection.Query<dynamic>(query, new
{
search = filter.SearchTerm.ToFullText()
});

RIA-Services - how to WhereOr or use an IN style construct

I am using SL 4, WCF RIA Services against Entity Framework 4.0. I have an Entity, Visit, that has a string Status field. I have a search screen where I need to display results that have StatusA or StatusB. I am struggling to find a way to specify a client-side query that specifies a collection of statuses that should be matched. If I was to write what I want in SQL it would look something like:
select * from Visit where Status in ('StatusA', 'StatusB');
Client side, it appears to be straightforward to chain Where methods for a WhereAnd effect:
var query = this.PqContext.GetVisitsQuery();
if (!string.IsNullOrEmpty(this.PracticeName))
{
query = query.Where(v => v.PracticeName.ToUpper().Contains(this.PracticeName.ToUpper()));
}
if (this.VisitDateAfter.HasValue)
{
query = query.Where(v => v.VisitDate > this.VisitDateAfter);
}
if (this.VisitDateBefore.HasValue)
{
query = query.Where(v => v.VisitDate < this.VisitDateBefore);
}
However, I can't seem to find a straightforward way to do a WhereOr style operation. I have tried this:
var statuses = new List<string>();
if (this.ShowStatusA)
{
statuses.Add("StatusA");
}
if (this.ShowStatusB)
{
statuses.Add("StatusB");
}
if (statuses.Any())
{
query = query.Where(BuildContainsExpression<Visit, string>(e => e.Status, statuses));
}
Where BuildContainsExpression looks like:
private static Expression<Func<TElement, bool>> BuildContainsExpression<TElement, TValue>(Expression<Func<TElement, TValue>> valueSelector, IEnumerable<TValue> values)
{
if (null == valueSelector)
{
throw new ArgumentNullException("valueSelector");
}
if (null == values)
{
throw new ArgumentNullException("values");
}
ParameterExpression p = valueSelector.Parameters.Single();
if (!values.Any())
{
return e => false;
}
var equals =
values.Select(
value =>
(Expression)Expression.Equal(valueSelector.Body, Expression.Constant(value, typeof(TValue))));
var body = equals.Aggregate<Expression>(Expression.Or);
return Expression.Lambda<Func<TElement, bool>>(body, p);
}
But this throws a "Bitwise operators are not supported in queries." exception. Any clues? Is there an alternative way to build an expression tree that works here or do I need to pass all the parameters over to the server and use the BuildContainsExpression there?
Your time and your guidance are much appreciated.
You can create a query method such as the following in your domain service:
GetVisitsByStatus(string[] statusList) {
// create the LINQ where clause here
}
And then from the client, call context.GetVistsByStatusQuery(string[]).
Not all of LINQ is (or even can) be exposed over the URL, so there are always cases where you need to use simple parameters, and have the middle tier construct the LINQ expressions that eventually define the query that goes to the back-end data store.
Hope that helps.

Resources