How to get the IADs interface of an active directory user? - winapi

How can i get the IADs interface of an Active Directory user - by username?
Note: Native code
i am trying to write the function that can get the IADs interface of a user in Active Directory.
i have the following "pseudocode" so far:
public IADs GetUserObject(string samAccountName)
{
IADs ads;
//Get the current domain's distinguished name ("dc=stackoverflow,dc=com")
AdsGetObject("LDAP://rootDSE", IADs, ref ads);
String dn = ads.Get("defaultNamingContext"); //"dc=stackoverflow,dc=com"
String path;
//Attempt #1 to bind to a user by username
path = "LDAP://sSAMAccountName="+samAccountName+",dc=stackoverflow,dc=com"
AdsGetObject(path, IADs, ref ads); //invalid syntax
return ads;
}
The trick, that i cannot figure out, is how to bind to the user by their account name. The following variantions don't work:
LDAP://sSAMAccountName=ian,dc=stackoverflow,dc=com
LDAP://dc=stackoverflow,dc=com;(&(objectCategory=user)(sAMAccountName=ian))
<LDAP://dc=stackoverflow,dc=com>;(&(objectCategory=user)(sAMAccountName=ian))
Edit:
A version that does work, but doesn't answer my question, is:
LDAP://cn=Ian Boyd,ou=Avatar Users,dc=stackoverflow,dc=com
It doesn't answer my question for two reasons:
i don't know the user's CN (Common-Name) (e.g. Ian Boyd), only their sAMAccountName (e.g. ian)
doesn't work for users not in the Avatar Users organizational unit; and i don't know a user's OU
Which comes from the notes i had before:
Note:
i don't know the name of the domain (but that's okay, i can get it at runtime)
i don't know the name of any active directory servers
i don't know the folder that the user is in
tl;dr: How would you write the utility function:
public IADs GetUserObject(string samAccountName)
{
//TODO: ask stackoverflow
}
Update 2:
Note:
i already know how to query for information about a user using .NET's DirectorySearcher
i already know how to query for information about a user using the Active Directory Services OLEDB provider with ADO (using the SQL syntax, but not the native syntax)
i'm now looking for code to query for information about a user using Active Directory Services COM objects (hence the question about getting an IADs for a user)
Update 3:
It certainly might require me to apply "filters", except i don't know where. The only ActiveDs interface that mentions Filter is IADSContainer, but i don't know where to get one.
i tried randomly to get the IADsContainer interface from the root IADs interface, but "rootDSE" doesn't support IADsContainer:
IADs ads = AdsGetObject("LDAP://rootDSE");
IADsContainer container = (IADsContainer)ads; //interface not supported exception
i could
ask a question on how to get the IADsContainer of the AD root
so i can ask how to recursively search active diretory
so i can ask how to filter using IADsContainer
so i can figure out how to get the IADs object of a user
so i an figure out how to query AD for user properties
But keeping track of all these questions is difficult.

If you know the value of sAMAccountName and need to get IADs of the user you need first find the user in Active Directory by the sAMAccountName and get distinguishedName attribute of the user. You know already how to get IADs by distinguishedName.
So you should just follow the code from MSDN for example. First you get IDirectorySearch interface of the AD container of defaultNamingContext of "LDAP://rootDSE".
IADs domain;
ADsGetObject("LDAP://rootDSE", IADs, domain);
Then you use IDirectorySearch::ExecuteSearch to apply search using the filter string:
(&(objectClass=user)(objectCategory=person)(sAMAccountName=theName))
Note: The search filter syntax is described here.
IDirectorySearch directorySearch = domain as IDirectorySearch;
ADS_SEARCH_HANDLE searchHandle;
directorySearch.ExecuteSearch(
"(&(objectClass=user)(objectCategory=person)(sAMAccountName=ian))",
attributeNames, numberOfAttributes,
out searchHandle);
you use the known value of sAMAccountName instead of theName.
for pAttributeNames you can use LPOLESTR array which consist from L"distinguishedName" only (see pszNonVerboseList from the code example and look the code of FindUsers in case of bIsVerbose as FALSE).
You should get distinguishedName attribute of first (and the only if any exist) found item. Having distinguishedName attribute you can use AdsGetObject to get the IADs of the user.
Alternatively you can get objectGUID attribute of the user instead of distinguishedName attribute and use binding by GUID syntax, but the usage of distinguishedName I personally find more clear and understandable.
public IADs GetUserObject(string samAccountName)
{
IADs ads;
//Get the current domain's distinguished name (e.g. "dc=stackoverflow,dc=com")
AdsGetObject("LDAP://rootDSE", IADs, ref ads);
String dn = ads.Get("defaultNamingContext"); //"dc=stackoverflow,dc=com"
//Get the the object of the current domain (e.g. LDAP://dc=stackoverflow,dc=com)
AdsGetObject("LDAP://"+dn, IADs, ref ads);
//Now we're going to search for the "distinguishedName" of this user
//setup the search filter for the user we want
String filter = "(&(objectClass=user)(objectCategory=person)(sAMAccountName="+samAccountName+"))";
//specify that we only need to return one attribute, distinguishedNamem,
//otherwise it returns all attributes and is a waste of resources
String[] searchAttributes = { "distinguishedName" };
//run the search
IDirectorySearch ds = ads as IDirectorySearch;
ADS_SEARCH_HANDLE searchHandle;
ds.ExecuteSearch(filter, searchAttributes, 1, out searchHandle);
ds.GetFirstRow(searchHandle);
//Now get the details of the "distinguishedName" column
ADS_SEARCH_COLUMN column;
ds.GetColumn(searchHandle, "distinguishedName", ref column);
//Get the user's distinguishedName
String dn = column.pADsValues.DNString;
//Now that we have the user's distinguishedName, we can do what we really wanted:
AdsGetObject("LDAP://"+dn, IADs, ads);
return ads;
}
This means that conceptually it can be broken down into two steps:
getting a user's distinguishedName from their samAccountName
fetching the IADs for a distinguishedName
And splitting the code:
public IADs GetUserObject(string samAccountName)
{
String userDistinguishedName = GetUserDistinguishedName(samAccountName);
return GetObject("LDAP://"+userDistingishedName);
}
public String GetUserDistinguishedName(string samAccountName)
{
//Get the current domain's distinguished name (e.g. "dc=stackoverflow,dc=com")
IADs ads = GetObject("LDAP://rootDSE");
String dn = ads.Get("defaultNamingContext"); //"dc=stackoverflow,dc=com"
//Get the the object of the current domain (e.g. LDAP://dc=stackoverflow,dc=com)
ads := GetObject("LDAP://"+dn);
//Now we're going to search for the "distinguishedName" of this user
//setup the search filter for the user we want
String filter = '(&(objectClass=user)(objectCategory=person)(sAMAccountName='+samAccountName+'))';
//specify that we only need to return one attribute, distinguishedNamem,
//otherwise it returns all attributes and is a waste of resources
String[] searchAttributes = { "distinguishedName" };
//run the search
IDirectorySearch ds = ads as IDirectorySearch;
ADS_SEARCH_HANDLE searchHandle;
ds.ExecuteSearch(filter, searchAttributes, 1, out searchHandle);
ds.GetFirstRow(searchHandle);
//Now get the details of the "distinguishedName" column
ADS_SEARCH_COLUMN column;
ds.GetColumn(searchHandle, "distinguishedName", ref column);
//Get the user's distinguishedName
return column.pADsValues.DNString;
}

Here is a sample in C++
IADs *pObject;
HRESULT hr;
// Initialize COM.
CoInitialize(NULL);
hr = ADsGetObject(L"LDAP://CN=JPB,OU=MonOU,DC=societe,DC=fr",
IID_IADs,
(void**) &pObject);
if(SUCCEEDED(hr))
{
// Use the object.
// Release the object.
pObject->Release()
}
// Uninitialize COM.
CoUninitialize();
You can find more information in Binding to Active Directory Domain Services.

Related

Sharing database access with many users on Parse- ACLs or CLPs?

I am currently handling my database user groups with ACLs. This works okay as per below and as per my logic and models. I'm mainly doing this because ACLs seem easier to implement. Parse has a somewhat confusing example using both CLPs and ACLs.
I would like to know if there is advantage to using CLPs over ACLs as I'm doing below or is my solution satisfactory, I.E. would what I'm doing below encounter any issues of any kind?
I'm using Parse & Back4App.
My demo project:
/*
Info: each user will have list of all authorized users ACL (user.objectID) and these
will all be written to any object crud actions.
*/
Future<void> saveUser001GroupData() async {
final acls = ParseACL();
var box = await Hive.openBox<String>(user001GroupIDsBox);
user001GroupIDs = box.values.toList();
for (final id in user001GroupIDs) {
acls.setReadAccess(userId: id, allowed: true);
acls.setWriteAccess(userId: id, allowed: true);
}
for (final color in user001GroupData) {
color.setACL(acls);
await color.save();
}
}

Can anyone tell me the Java utility to download documents to your local PC from Content Engine in filenet?

Hello Guys I am trying to write the java utility to download the documents to local PC from content engine in filenet can anyone help me out?
You should read about FileNet P8 CE API, you can start here:
You have to know that the FileNet Content Engine has two types of interface that can be used to connect to it: RMI and SOAP. A cmd line app you are planning to write, can connect only by SOAP (I am not sure that this is true for the newest versions, but what is definitely true, that it is much easier to setup the SOAP connection than EJB), so you have to read that part of the documentation, how to establish a connection in this way to your Content Engine.
On the link above, you can see that first of all you have to collect the required jars for SOAP connection: please check the "Required for a Content Engine Java API CEWS transport client" section for the file names.
After you collect them, you will need a SOAP WSDL URL and a proper user and password, the user has to have read properties and read content right to the documents you would like to download. You also need to know the ObjectStore name and the identifier or the location of your documents.
Now we have to continue using this Setting Up a Thick Client Development Environment link (I opened it from the page above.)
Here you have to scroll down to the "CEWS transport protocol (non-application-server dependent)" section.
Here you can see, that you have to create a jaas.conf file with the following content:
FileNetP8WSI {
com.filenet.api.util.WSILoginModule required;
};
This file must be added as the following JVM argument when you run the class we will create:
java -cp %CREATE_PROPER_CLASSPATH% -Djava.security.auth.login.config=jaas.conf DownloadClient
Now, on the top-right corner of the page, you can see links that describes what to do in order to get a connection, like "Getting Connection", "Retrieving an EntireNetwork Object" etc. I used that snipplet to create the class below for you.
public class DownloadClient {
public static void main(String[] args) throws Exception{
String uri = "http://filenetcehost:9080/wsi/FNCEWS40MTOM";
String userId = "ceadmin";
String password = "password";
String osName = "Test";
UserContext uc = UserContext.get();
try {
//Get the connection and default domain
Connection conn = Factory.Connection.getConnection(uri);
Domain domain = Factory.Domain.getInstance(conn, null);
ObjectStore os = Factory.ObjectStore.fetchInstance(domain, osName, null);
// the last value (jaas samza name) must match with the name of the login module in jaas.conf
Subject subject =UserContext.createSubject(connection, userId, password, "FileNetP8WSI");
// set the subject to the local thread via threadlocal
uc.pushSubject(subject);
// from now, we are connected to FileNet CE, and objectStore "Test"
//https://www.ibm.com/support/knowledgecenter/en/SSNW2F_5.2.0/com.ibm.p8.ce.dev.ce.doc/document_procedures.htm
Document doc = Factory.Document.getInstance(os, ClassNames.DOCUMENT, new Id("{F4DD983C-B845-4255-AC7A-257202B557EC}") );
// because in FileNet a document can have more that one associated content element
// (e.g. stores single page tifs and handle it as a multipaged document), we have to
// get the content elements and iterate list.
ContentElementList docContentList = doc.get_ContentElements();
Iterator iter = docContentList.iterator();
while (iter.hasNext() )
{
ContentTransfer ct = (ContentTransfer) iter.next();
// Print element sequence number and content type of the element.
// Get and print the content of the element.
InputStream stream = ct.accessContentStream();
// now you have an inputstream to the document content, you can save it local file,
// or you can do what you want with it, just do not forget to close the stream at the end.
stream.close();
}
} finally {
uc.popSubject();
}
}
}
This code is just shows how can you implement such a thick client, I have created it now using the documentation, not production code. But after specifying the packages to import, and may handle the exceptions it will probably work.
You have to specify the right URL, user, password and docId of course, and you have to implement the copy from the TransferInputStream to a FileOutputStream, e.g. by using commons.io or java NIO, etc.

EWS - Access Shared Calendars

I would like to list the permission from every exchange shared calendar.
I have two exchange online account :
vikramsingh#gmail.com
vikramsinghmrt#outlook.com
Now, i have shared a calendar to vikramsinghmrt#outlook.com from vikramsingh#gmail.com.
Now when i fetch the calendar list using Exchange EWS API, i got all calendar (include shared calendar), But there is no way to know that which one shared calendar.
I have use below code :
PropertySet propSet = new PropertySet(BasePropertySet.FirstClassProperties, FolderSchema.Permissions);
Folder folder = Folder.Bind(service, folderid, propSet);
if (folder.Permissions.Count != 0)
{
for (int t = 0; t < folder.Permissions.Count; t++)
{
if (folder.Permissions[t].UserId.DisplayName != null || folder.Permissions[t].UserId.PrimarySmtpAddress != null)
{
}
}
}
But this return userId.DisplayName and UserId.PrimarySmtpAddress to null every time.
How could i know that which calendar is SHARED ?
You can use the properties RequiredAttendees and OptionalAttendees and check for the E-Mail address of the shared calendar. If one of the properties containts the mail address of the shared calendar, it is from the shared calendar.
If you check the Folder Associated Items collection of each folder using an Associated Traversal for Shared folders there should be an Item of Type IPM.Sharing.SyncState within that collection.
ItemView iv = new ItemView(1000);
iv.Traversal = ItemTraversal.Associated;
SearchFilter sf = new SearchFilter.IsEqualTo(ItemSchema.ItemClass, "IPM.Sharing.SyncState");
FindItemsResults<Item> fiItems = Folder.FindItems(sf, iv);
I got the answer. We have calendar call as below. service.FindFolders(new FolderId(WellKnownFolderName.Root, "email")
In this call we pass my email id. (My credential email).
If i need to access other user calendar(Shared calendar). I need to pass other user email address here. like service.FindFolders(new FolderId(WellKnownFolderName.Root, "Other user email").
Service object is created by my credential and just pass email of other user. Now you can get all calendar.

RavenDB Service Session.Query not returning results

I have a RavenDB Service instance set up on localhost:8080 and am using the OAuth plugin to store a simple user document for authentication. I am using a guid for the id and the users email as the name. The following code is functioning properly to store the user
public AccountUserDocument CreateUser(RegisterModel model)
{
using (IDocumentSession session = DataDocumentStore.Instance.OpenSession())
{
Guid userId = Guid.NewGuid();
session.Store(new AccountUserDocument
{
Name = model.Email,
Id = String.Format("Raven/Users/{0}", userId),
AllowedDatabases = new[] { "*" },
Email = model.Email,
FirstName = model.FirstName,
LastName = model.LastName,
FacebookId = 0,
Expires = DateTime.Now.AddMonths(1),
AccessToken = string.Empty
}.SetPassword(model.Password));
session.SaveChanges();
return session.Load<AccountUserDocument>
(String.Format("Raven/Users/{0}", userId));
}
}
and returns a valid user object. However, when i call
return session.Query<AccountUserDocument>()
.Customize(x => x.WaitForNonStaleResults())
.Where(x => x.Name == email)
.SingleOrDefault();
I get nothing. It had been working a week ago but now it just doesn't. If I open up RavenDB studio, I can see the user and the name is exactly how I am entering it (i have even copy and pasted it into the text field).
I have tried stopping and restarting the service hoping that would solve the problem but it did not.
I was wondering if someone could point me in the direction of how I might debug what is going on here. The full code repository can be found here
https://github.com/jamesamuir/MVC_Facebook_Auth
if anyone is inclined to download it.
Thanks in advance.
Well, I see a lot of things wrong with the code sample you provided - such as mismatched versions of client and server. Also, the Authentication Bundle was a 1.0 feature that has been deprecated in 2.0 which is almost final, so you're writing code you will eventually have to replace.
But to answer your question, the specific reason that you can't get results from your query is that you chose to use a document key starting with "Raven/", which is the convention for RavenDB system documents - which don't get indexed.
If you remove "Raven/" from your id, it will work.
Also, you should not be using .WaitForNonStaleResults() - that's only for unit tests, and is dangerous in production. If you really feel like you need to wait, use .WaitForNonStaleResultsAsOfNow() - which is safer because it provides a cutoff.

How do I get the displayname of the logged in user in EWS?

Exchange Autodiscovery will give me the user's Display Name via the UserSettingName.UserDisplayName property.
However, in cases where autodiscovery fails and connection needs to be done manually I can't figure out how to get the DisplayName.
I tried this, but I just get the users' email address:
_service = new ExchangeService();
_service.Credentials = new System.Net.NetworkCredential(exchangeSettings.EmailAddress, exchangeSettings.Password);
_service.Url = new Uri(exchangeSettings.ExternalEwsUrl);
NameResolutionCollection resolvedNames = _service.ResolveName(exchangeSettings.EmailAddress);
exchangeSettings.UserDisplayName = resolvedNames.First().Mailbox.Name;
Thanks
If you are going to use ResolveName and you want the displayName then you should use the overload to specify that the operation should return the AD contact information. Then you can just use the DisplayName property.
NameResolutionCollection ncCol =
service.ResolveName("user#domain.com",ResolveNameSearchLocation.DirectoryOnly,true);
Console.WriteLine(ncCol[0].Contact.DisplayName);

Resources