I'm developing a SharePoint 2010 Web Part (in C#) which needs to pull user data from Active Directory but I'm having trouble using the current SharePoint user's credentials to authenticate with AD. I am using the DirectoryEntry class to get data from AD and it works fine when I hard-code some credentials but I know that's bad practice so I want to avoid it. I've tried a couple of different things like:
new DirectoryEntry("LDAP://" + dc, null, null, AuthenticationTypes.ServerBind | AuthenticationTypes.FastBind);
and
new DirectoryEntry("LDAP://" + dc, null, null, AuthenticationTypes.Secure);
but they all just throw exceptions. I don't really know what these do but it's what I've managed to find online.
Any help that can point me in the right direction would be greatly appreciated.
You're probably running into the infamous 'double hop' problem. The Sharepoint Server can't automatically authenticate as you to another server, even on the same domain. Google 'double hop' for more info on the problem.
There are several options for doing what you want:
You can use the Secure Store Service in Sharepoint 2010 to store the requisite Username / Password for connecting to AD, either a single username/password shared by all Sharepoint Users (similar to #2 below) or a username/password for each Sharepoint user (similar to #3 below).
You could set up an AD account that has read-only permissions to AD, then create some Web Part properties to store the username and password of the user. Set up the properties so that they are shared by all users and set by the administrator. Use the stored username/password when connecting to AD. Set the password property so that it is write-only from the UI side.
If it's important for each user to see a different set of AD data based on the user's permissions in AD, then you could do something similar to the above, but don't make the username/password properties shared.
Set up Kerberos for your Sharepoint environment, thus allowing the sharepoint web server to authenticate as you to AD without having to store your credentials
Setup AD to allow for anonymous read access (probably not the best, due to security considerations)
I'm using this code to connect to AD with the current user:
using (DirectoryEntry entry = new DirectoryEntry())
{
using (DirectorySearcher searcher = new DirectorySearcher(entry))
{
searcher.Filter = "yourSearchQuery";
searcher.PropertiesToLoad.Add("yourProperty1");
searcher.PropertiesToLoad.Add("yourProperty2");
using (SearchResultCollection results = searcher.FindAll())
{
foreach (SearchResult result in results)
{
using (DirectoryEntry resultEntry = result.GetDirectoryEntry())
{
PropertyValueCollection valuesForProperty1 = resultEntry.Properties["yourProperty1"];
PropertyValueCollection valuesForProperty2 = resultEntry.Properties["yourProperty2"];
// ...
}
}
}
}
}
Related
I'm trying to get an application using EWS to work against O365 with OAuth. I've managed to get it working in the case where I register the app in AAD, and grant it full_access_as_app (Use Exchange Web Services with full access to all mailboxes) in the portal. The code looks like this:
string tokenUri = "https://login.microsoftonline.com/TENANTID/oauth2/v2.0/token";
string resource = "https://outlook.office365.com";
string appId = "APPID_FROM_AAD";
var context = new AuthenticationContext(tokenUri);
X509Certificate2 cert = GetLocalCertificate(THUMBPRINT);
ClientAssertionCertificate c = new ClientAssertionCertificate(appId, cert);
AuthenticationResult authenticationResult = context.AcquireTokenAsync(resource, c).ConfigureAwait(false).GetAwaiter().GetResult();
m_exchangeService.Credentials = new OAuthCredentials(authenticationResult.AccessToken);
//all the autodiscover URIs for O365 are the same
m_exchangeService.Url = new Uri(#"https://outlook.office365.com/EWS/Exchange.asmx");
m_exchangeService.ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress, m_emailAddress);
This seemed a little extreme though. The application is a back end service, and only needs access to one mailbox (the impersonated user id), so I wanted to change it over to full_access_as_user permissions (Access mailboxes as the signed-in user via Exchange Web Services). I added the permission to the app, and then added the application to the user, but I got a 401 anytime I tried to operate on the mailbox after impersonation. I checked the JWT, and while with the original permission I had "full_access_as_app" in the roles, I didn't have any roles assigned this time.
Is there a way I can modify this so that I can have an admin add EWS access to one or more mailboxes in a tenant, or is the only way to get this to work to give an app access to every mailbox on the service?
Yes, OAuth authentication for EWS is only available in Exchange as part of Office 365. EWS applications require the "Full access to user's mailbox" permission.
Reference - Authenticate an EWS application by using OAuth
I have successfully created a user, credentials, and a bucket.
Now I need to grant bucket access to this user.
Is there any way to get this CanonicalUser value from code?
The IAM user object only provides ARN, Path, UserId and UserName values, but none of these are valid for the grant.
using (var s3 = new Amazon.S3.AmazonS3Client("[user_key]", "[secret_user_key]", RegionEndpoint.GetBySystemName("eu-west-1")))
{
var response = s3.GetACL("[bucket_id]");
var acl = response.AccessControlList;
acl.AddGrant(
new S3Grantee() {
CanonicalUser = **???**
},
new S3Permission(S3Permission.FULL_CONTROL)
);
s3.PutACL(
new PutACLRequest() {
AccessControlList = acl,
BucketName = "[bucket_id]"
}
);
}
You can easily get CanonicalUser ID using ListAllMyBuckets API call [1] (s3:ListAllMyBuckets permission is required):
$ aws s3api list-buckets --query Owner
{
"DisplayName": "lord-vader",
"ID": "f420064cb076f772e10584fc40ab777c09f6b7d154342cf358f1bd1e573c9cf7"
}
In AWS SDK for .NET, use code like this [2]:
AmazonS3Client client = new AmazonS3Client();
ListBucketsResponse response = client.ListBuckets();
Console.WriteLine("Canonical user ID - {0}", response.Owner.Id);
In AWSJavaSDK you can use AmazonS3.getS3AccountOwner wrapper method [3].
https://docs.aws.amazon.com/AmazonS3/latest/API/RESTServiceGET.html
https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/S3/MS3ListBuckets.html
https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/services/s3/AmazonS3Client.html#getS3AccountOwner--
No, it is not possible to get the canonical user id from code - you've hit a somewhat odd and likely legacy aspect due to the different way to manage access permissions for S3 resources, see the AWS team's response to How to find out Canonical ID for an IAM user?:
You can not add IAM Users to ACL's as a grantee. I'll have the documentation updated to clarify that IAM Users are not supported in ACL's. There are a few solutions you can use to grant this User access to your Amazon S3 content: [...]
You might indeed want to reconsider using the more versatile S3 bucket policies instead (see below) - however, if you have access to the account's root credentials, you might find the canonical user ID associated with your AWS account as outlined in Specifying a Principal in a Policy (mind you, this doesn't work with IAM user credentials):
Go to http://aws.amazon.com and from the My Account/Console drop-down menu, select Security Credentials.
Sign in using appropriate account credentials.
Click Account Identifiers.
I shall emphasize again that AWS strongly recommends to only use IAM users these days, see e.g. Root Account Credentials vs. IAM User Credentials:
Because you can't control the privileges of the root account
credentials, you should store them in a safe place and instead use AWS
Identity and Access Management (IAM) user credentials for day-to-day
interaction with AWS.
This canonical user id requirement for S3 is a rare exception, and as I said likely to be considered a legacy artifact due to S3's ACL layer predating IAM, thus best avoided, if possible.
I need to find out all AD groups SIDs that current user belongs to inside my Sharepoint (2007) webpart.
I wanted to use System.DirectoryServices.AccountManagement namespace:
using (var context = new PrincipalContext( ContextType.Domain ))
{
using (var user = UserPrincipal.FindByIdentity( context, accountName ))
{
var groups = user.GetAuthorizationGroups();
...
}
}
, but I get the following error:
Event ID: 10016
Through the permission settings (application specific) is the SID (S-1-5-20) for user NT AUTHORITY \ NETWORK SERVICE of address localhost (Using LRPC) is not authorized to activate (Local) for the COM Server application with CLSID
{61738644-F196-11D0-9953-00C04FD919C1}
This might be fixed with this http://support.microsoft.com/kb/899965
but this approach requires changing registry values (the ownership of the application, so you can change apps values at dcomcnfg) and later User Permissions at dcomcnfg's COM security, which isn't an option for me.
Is there another way to access Current user's groups SIDs inside Sharepoint?
I really hoped I can find these values in SPContext.Current.Web.CurrentUser.Groups, but apparently not.
You need to go the SharePoint way here and not use System assemblies, but the SharePoint ones.
The SID of each user is in the SPUser.Sid Property. As you want to look for AD groups only you can check the .IsDomainGroup Property of SPUser.
Now all you need to do is check the current user: ´SPContext.Current.Web.CurrentUser(aSPUser` object).
To answer your question how to get all groups a user belongs to, you actually will need to use System.DirectoryServices. A solution for your problem is shown in the following stackoverflow posts:
In C#, how to access Active Directory to get the list of groups that a certain user belongs to?
Querying AD for finding all groups of a user - Missing one group
So in short: SPUser object as well as querying the Active Directory via DirectoryServices
I have some code in asp.net ( kindly given by someone else ) to query AD to get user name and email etc.
using System.DirectoryServices;
using System.DirectoryServices.ActiveDirectory;
using ActiveDs;
DirectorySearcher search = new DirectorySearcher(new DirectoryEntry(), string.Format("(samaccountname={0})", id));
if (search == null)
return id;
if (search.FindOne() == null)
return id;
DirectoryEntry usr = search.FindOne().GetDirectoryEntry();
IADsUser oUsr = (IADsUser)usr.NativeObject;
return string.Format("{0} {1}", usr.Properties["givenname"].Value, usr.Properties["sn"].Value);
However this requires impersonation with an id that's required to be changed every 2 weeks and then updated in the web.config which is often forgotten
Is there any non impersonation code to achieve the same result ?
UPDATE - it's a config tool and it looks up name, email id etc.
I like the service a/c idea
Q - How is it possible to run ( impersonate ) just the AD code with a "service" a/c ? any samples/code ?
how do you impersona
For your particular purpose, a ServiceAccount shall be added to AD;
If you ASP.NET application is for a LAN in your organization, you could simply forget about providing Username and Password and only provide the root domain. This way, Active directory will search for Windows authenticated user instead of using impersonnation (this assumes that the user accessing your application has the rights to perform the tasks provided by your application).
What exactly does your application need to do?
If your application manages user accounts, groups and OU, then you need to use impersonnation only if the user doing these tasks through the application has no rights of managing the AD with her/his regular user account. This, should not happen. So, event for this, if the user has the proper rights, omitting your credentials will only allow AD to search for the current logged on user.
We usually request IT to give us a domain service account. You still need to impersonate, but with a service account, the password will not have to be changed every 2 weeks, and is granted specific rights for the particular function you need it for, so it would mean very low maintenance for you.
I don't think so, because you need to bind to the domain with valid credentials in order to read from active directory.
Think of the username/password as part of a connection string to a database. I'd request a complex username and password from your domain administrator and request that they give it limited login permissions and set the password to never expire. Then store and use those in your Web.config file.
System Specs:
Infopath 2007 with c# code-behind
Webservices
Active Directory
I need to get the users name (First Name and Last Name) from active directory but the user appears to not have permissions to read the active directory listings.
What permissions do I need to give the user in order for them to search AD
I am using code like this
SearchResult result;
using (DirectoryEntry de = new DirectoryEntry("LDAP://DC=contoso,DC=com,DC=au"))
{
DirectorySearcher search = new DirectorySearcher(de, (string.Format("(&(objectClass=user)(mailNickname={0}))",this.Application.User.UserName)));
result = search.FindOne();
}
I have considered creating a webservice that gets the information required but that seems like overkill but would get around having to make sure every possible user of the form is required to have the correct permissions
EDIT:
The code that I am trying to execute is infopath code behind. The form itself connects to webservices to retrieve some of its data. as such it is under infopath's security model. The form is fully trusted so it should be fine to execute under the current user context.
My fault for not adding the extra detail.
When you create a new DirectoryEntry without specifying a username and password you're connecting to Active Directory using the credentials of the executing user - in your case probably the local IUSR_...-account on the web server which is the default account used when a new web site is set up in IIS. Because that's a local account you won't be able to access Active Directory.
You have two options:
Create a service account in Active Directory and use that account explicitly, ie DirectoryEntry de = new DirectoryEntry("LDAP://DC=contoso,DC=com,DC=au", "sa-username", "sa-password", AuthenticationTypes.Secure). Of course, passwords in clear text in the code is not a good idea so find a way to encrypt the password.
or
Configure the IIS application pool for your web site (IIS 6+) to run under a domain user account - that way that account is used when connecting to Active Directory.
Any user of the AD should have permissions to browse the AD by default.
You probably just need to change your directory entry to point to the user container like so:
new DirectoryEntry("LDAP://CN=users,DC=contoso,DC=com,DC=au")
Your user container could be another name.
If that does not solve the problem it may be that the application is not actually running as the user. For example, an ASP.NET website would need to be using impersonation in order to query the AD.