How do ldap search for email address in contacts' 2nd/3rd alternate email addresses? - ldap

I have an LDAP server with my email contacts so that I can lookup contacts by name/email, etc. However, it only seems to search and find the first email address for any contact.
For example, if I have a person:
LastName: Doe
FirstName: John
Email: jdoe#work.com
Email2: johndoe#home.com
Email3: johndoe#fun.com
It only searches through or returns the first email. For example, if I search for "John", it will return only the "jdoe#work.com" even though the other two email addresses have "john" in them. The search filters I've tried are:
//This one will both look through and match the first email but ignores the 2nd/3rd
(|(displayName=*%v*)(mail=*%v*)(uid=*%v*)(givenname=*%v*)(sn=*%v*)(cn=*%v*))
//This one throws an error saying "mail2" and "mail3" are invalid filters.
(|(displayName=*%v*)(mail=*%v*)(mail2=*%v*)(mail3=*%v*)(uid=*%v*)(givenname=*%v*)(sn=*%v*)(cn=*%v*))
What should I be using?
Also, does anyone have a link to some page that lists all the possible filters I can put in an ldap person search?

Exchange does not store additional mailaddresses in fields like mail2 or mail3. All addresses are stored in the multi-valued field "proxyAddresses". This field contains one line for each address in the form of
address-type:address
Example:
smtp:test#contoso.local
SMTP:user#contoso.local
The second entry in the example would be main address for that account, because the SMTP prefix is all uppercase.
So you would search for (proxyAddress=%v) or something like that. I don't know the LDAP search syntax out of my head.
Edit: Another option is to use the ResolveNames operation of the EWS webservices (see http://www.infinitec.de/post/2009/04/13/Resolving-the-primary-email-address-with-Exchange-WebServices-ResolveNames-operation.aspx and http://msdn.microsoft.com/en-us/library/aa563518(v=exchg.140).aspx).

The filter:
(|(displayName=*%v*)(mail=*%v*)(uid=*%v*)(givenname=*%v*)(sn=*%v*)(cn=*%v*))
will not match the entry:
LastName: Doe
FirstName: John
Email: jdoe#work.com
Email2: johndoe#home.com
Email3: johndoe#fun.com
because none of the filter assertions match any of the attribute names in the given entry.
(|(Email=jd*)(Email2=john*)(Email3=john*)(lastName=Do*))
would match. Have you considered using the standard names for the example entry you give?

Active Directory was released with some schema choices that are questionable. Now it is hard to fix them.
One of those is that mail, was flagged as single valued. This should have been a multivalued attribute. Thus the use of proxyAddresses, where it tries to overload a string syntax attribute with more information by using smtp: or x500: or SIP: to indicate a protocol for the address. Then upper case (SMTP) means primary and lower case (smtp) means secondary.
This also occured for telephoneNumber being single valued, and extra values now overflow into the attribute otherPhone.
Same for:
facsimileTelephoneNumber and otherFacsimileTelephoneNumber
labelledUri and url
homePhone and otherHomePhone
pager and otherPager
mobile and otherMobile

Related

How to guarantee preferred_username unicity on amazon-cognito?

I am using a single table to store all my data in dynamodb as such:
Partion Key (PK)
Sorting Key (SK)
Attributes
USER#gijoe
PROFILE#gijoe
{ name: "G.I", lastName: "Joe" }
USER#gijoe
CARD#first-card
{ name: "King", picture: "./king.png" }
I am using the preferred_username as a part of the Partition Key, and thus need it to be unique to avoid colliding user data.
How can I garantee that two users in my User Pool cannot have matching preferred_username ?
Edit:
The answer from #Lukas did it. Note that it required me to drop and recreate my cloudformation stack, which is why it failed on my first tries. Now when I try to edit the preferred_username I get the error I was looking for:
{
"message": "Already found an entry for the provided username.",
"code": "AliasExistsException",
"time": "2021-01-19T09:36:47.874Z",
"requestId": "7b52dbc2-58c5-4354-aa51-66d4dc7472a0",
"statusCode": 400,
"retryable": false,
"retryDelay": 85.84051584672932
}
username is unique within single pool. Same with alias. preferred_username may be configured as username alias.
https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-settings-attributes.html
Key take aways:
Developers can use the preferred_username attribute to give users a username that they can change. For more information, see Overview of Aliases.
The username must be unique within a user pool. A username can be reused, but only after it has been deleted and is no longer in use.
You can allow your end users to sign in with multiple identifiers by using aliases.
The preferred_username attribute provides users the experience of changing their username, when in fact the actual username value for a user is not changeable.
If you want to enable this user experience, submit the new username value as a preferred_username and choose preferred_username as an alias. Then users can sign in with the new value they entered.
If preferred_username is selected as an alias, the value can be provided only when an account is confirmed. The value cannot be provided during registration.
....
Alias values must be unique in a user pool. If an alias is configured for an email address or phone number, the value provided can be in a verified state in only one account. During sign-up, if an email address or phone number is supplied as an alias from a different account that has already been used, registration succeeds. Nevertheless, when a user tries to confirm the account with this email (or phone number) and enters the valid code, an AliasExistsException error is thrown. The error indicates to the user that an account with this email (or phone number) already exists. At this point, the user can abandon the new account creation and can try to reset the password for the old account. If the user continues creating the new account, your app should call the ConfirmSignUp API with the forceAliasCreation option. This moves the alias from the previous account to the newly created account, and it also marks the attribute unverified in the previous account.

How to get user from User field LookupId

I have a list in sharepoint online.
And in this list, i have a person field.
When i call the API endpoint to get all the items in the list, i get an LookupId value for the person field.
I tried to get the user by using the value of the lookupid, but it don't work because the id is not recognized.
The lookupid is a int (eg: 21) instead of a guid.
Is there something missing in the configuration of the person field or in my calls to Microsoft Graph API ?
When a user signs into a SharePoint site collection for the first time, a ListItem is created in a hidden User Information List. The LookupId in a PersonOrGroup field refers to the ListItem in this list. The URL for the User Information List for SharePoint Online should be:
https://{yourTenant}.sharepoint.com/{yourSiteCollection}/_catalogs/users/detail.aspx
Since the User Information List is a generic SharePoint list, you can query the list via Graph. First, get the list id for the User Information List. An easy way to get the list id is to view the source for the User Information Site via Chrome and search for 'listId'. You should find a result like this:
"listId":"{yourListIdIsHere}"
Copy the id. By using the copied id, the id of your root site and the LookupId, you can get the ListItem in the User Information List:
https://graph.microsoft.com/v1.0/sites/{siteId}/lists/{pasteCopiedListId}/items/{lookUpId}?$expand=Fields
The ListItem contains information about the user, such as the email, which can be used to identify the Azure user:
https://graph.microsoft.com/v1.0/users/{eMail}
Question: How could i get the hidden User Information List from Microsoft Graph?
If you do not want to use the 'trick' with Google Chrome to get the id, there is another way to get the site. Typically, if you want to get the id for any site, you would call:
https://graph.microsoft.com/v1.0/sites/{siteId}/lists
However, you will not find the id of the User Information List, even if you include hidden sites. I do not know why. An additional problem seems to be, that you cannot filter lists by their name:
https://graph.microsoft.com/v1.0/sites/{siteId}/lists?$filter=name eq 'users'
The query returns an error, that the provided filter statement is not supported. The only way to get the list without knowing the id seems to by using the property displayName of the list. However, the displayName is based on your localization. So, since I am from Germany, I can get the site by using the query:
https://graph.microsoft.com/v1.0/sites/{siteId}/lists?$filter=displayName eq 'Benutzerinformationsliste'
You will need to replace Benutzerinformationsliste with your localized name. For EN replace it with 'User Information List'.
This returns the expected result:
{
"#odata.context": "https://graph.microsoft.com/v1.0/$metadata#sites('xxx')/lists(id,name,displayName)",
"value": [
{
"#odata.etag": "\"xxx\"",
"id": "xxx",
"name": "users",
"displayName": "Benutzerinformationsliste"
}
]
}
As you can see, the name of the list is 'users', so why the first filter statement does not work is a little mystery to me. Maybe someone here knows and can help out.
Some of the queries above don't work at the moment.
What I finally found as a good solution - after trying many many queries - is that you can do this by following the few steps below:
1- Get the GUID of the user information list.
Using the title of the list "User Information List" or the name "users" in the parameter "$filter" does not work.
Don't forget 'system' among the properties you select if you want to retrieve the hidden system-lists.
GET https://graph.microsoft.com/v1.0/sites('{site_id}')/lists?select=id,name,system
2- Filter the previous result in order to pick up the ID of the targeted list named 'users'.
By the way, applying this restriction "$filter=name eq 'users'" does not work.
You will get an exception. So you must do the filtering part by writing a few lines of code.
3- Once you've got the list identifier, then select all the items you want. And voilĂ ! The word 'Fields' must be in pascal case (uppercase the first letter ).
GET https://graph.microsoft.com/v1.0/sites('{site_id}')/lists('users_list_id')/items?$select=Fields&$expand=Fields
As #QuestionsPS1991 mentioned, the people field in fact refers to the hidden user list. With the lookupid, we can get the user via below methods:
Get user by id
Get user property by expanding lookup field
//////////// updated
By default, MS Graph does not return this user list. You may hard code the list id or follow ##QuestionsPS1991 suggestion. Below is my test:

Finding duplicate users in a user list (Asked in an Interview)

I was recently asked this in an interview for a SDE role.
Suppose you have a list of User objects
class User {
String userId;
String email;
String ip_addr;
}
where userId field is unique among all users, while ip_addr and email are not necessarily so.
and you know some users have more than one user account (if any two User objects share a common email OR an ip_addr, you classify them as belonging to the same user).
you are required to write a function, whose signature is given as:
List<List<User>> findDups(User[] userList) {
// code here
}
[EDIT] so for example, if there are 7 users, only 2 of which are unique, the function can return something like the following (not necessarily in this specific order):
{
{user1, ip1, email1},
{user5, ip5, email1},
{user24, ip5, email2}
},
{
{user2, ip2, email2},
{user7, ip2, email7},
{user8, ip2, email0},
{user19, ip19, email7}
}
here, in this first group, the first user (user1) is the same user as the second one (user5) as they share the same email address. We also know that the third user (user24) is also the same user as it shares the same ip address (ip5) as the second user in the list.
[/END EDIT]
what data structure would you use and what would be the time complexity of the proposed solution?
I tried to use disjoint set union (quick union), which would give me linear complexity, however the interviewer constantly tried to steer me away from that and said just the Collection API would be enough (Using Lists and Sets and maps).
What would be your approach and solution and the corresponding time complexity?

Private and shared address book with LDAP?

For a set of users belonging to some organisation, I want to provide the following:
each user should have a private address book
each user should have access to a company address book
I wonder how to model this scenario in LDAP so that:
user connects to LDAP server with some client
user performs a search for some string
all matching entries from the global address book are returned
all matching entries from the user's private address book are returned
Is searching the global and private address book possible with a single query? I guess the user would provide his path in LDAP as DN, but the global address book would be located at a different DN. I imagine something like that:
/
/OU=private-address-books
/CN=user1
/CN=entry1
...
/OU=global-address-book
/CN=entryABC
So is it possible to somehow automatically reference the global address book under the user's private address book?
You would probably be best off to arrange your DIT Structure to be more like:
ou=private,ou=Addressbooks...
ou=public,ou=Addressbooks...
You then could search using the the ou=addressbooks as the baseDN.
Or better, assign an Attribute boolean type private=TRUE to each entry.
So for all addressbooks,
(objectClass=Entry)
And for private
(&(objectClass=Entry)(private=TRUE))
And then finally, you may be able to use an ExtensibleMatch search filter to search both containers.
-jim

Is this a valid REST API?

I am designing an API.
There's the user profile, accessible at
http://example.org/api/v1/users (resp. http://example.org/api/v1/users/:id)
Now, the user's profile will be dynamic.
So we will allow an API function to add a new profile attribute.
Is the following a valid REST API URL for this?
POST http://example.org/api/v1/users/attributes
Indeed, to retrieve a specific user, the user's id would be appended to the .../users/ URL.
Now if I use the "attributes" element after /users/, would that somehow break the user id pattern for the URL?
I'd like to keep the base URL to be api/v1/users though, because logically I am modifying the users profile still...
EDIT: The attributes would be added valid for all profiles, it's independent of a user. Say the profile has "name", "surname", "email", and I want to add "address" to all profiles (Of course I know that users with a missing "address" field would not get the new attribute)
What is a good practice to address such an issue?
I think the id should be kept in the URL because you are adding the attributes to a specific user, right?
It is an acceptable solution to use the /api/v1/users/attributes as long as the :id cannot be the text: "attributes". However I recommend to create your own media type, microformat, or microdata for the attributes, because it is rather a type than a resource.
I think you should check these links:
http://alps.io/spec/index.html
http://www.markus-lanthaler.com/hydra/spec/latest/core/
http://schema.org/
http://microformats.org/wiki/microformats2
http://amundsen.com/media-types/maze/
If the user can set what attributes she can have, only then should you use a resource for attributes. But then each user should have one. But I don't think using resources will be necessary, microdata and microformats both contain more than enough person description attributes...
Some update after 5 months:
Now if I use the "attributes" element after /users/, would that
somehow break the user id pattern for the URL?
From the perspective of the client that "id pattern" does not exist. The client follows links by checking the semantics annotated to them. So REST clients are completely decoupled from the URI structure of the actual REST API (aka. uniform interface constraint). If your pattern breaks, then it is completely a server side, link generation and routing issue, which is not a client side concern.
Say the profile has "name", "surname", "email", and I want to add
"address" to all profiles. What is a good practice to address such an
issue?
Address is an optional field in this case and probably a sub-resource, because it can have further fields, like city, postal code, street, etc... You can add address separately, for example with PUT /users/123/address {city: "", street: "", ...} or you can add those fields to your user form, and add a partial update to the user, like PATCH /users/123 {address: {city: "", street: "", ...}} if only the address changes.
In case you want to update every resource in the entire collection I would send a PATCH request to /users.
While it is a valid URI, I would suggest avoiding POST http://example.org/api/v1/users/attributes. In my opinion, it violates the principle of least surprise when a collection endpoint has a child node which is not a member of the collection. If you want to track user attributes as shared by all users, then that's a separate collection, perhaps /user-attributes.
POST /user-attributes
{
"name": "Email Address",
"type": "String",
...
}
GET /user-attributes would return all the possible attributes, and GET /user-attributes/{id} would return all the metadata around an attribute.
If there's no metadata, then #inf3rno's suggestion to just PUT the attribute up and let the server deal with it is definitely worth considering.
This all presupposes you need to manage attributes through the API. If not, I agree with #inf3rno that media types are the way to go. Of course, in that case you may want a media type for the user-attributes resource ..