I've been working with Directory Services since the last few days.
Using the UserPrincipal Object, I've tried to get the email and company field from current AD user. Email is not a problem since it's exposed by default. Company is an other story though.
I found in here a post that explains how to do so: Get job title using System.DirectoryServices.AccountManagement
Although, I am having an unfortunate issue with this method. The post shows how to create the FindByIdentity method in the extended class, but in order for that to work you have to set search type to your extended class type, which has for result to find no entries for my specific user. If I set the search type to UserPrincipal in the FindByIdentityWithType, it does find my AD user, but as you can expect, I'm getting an conversion error.
So My question is very simple, Is there any known way to Find by Identity in an extended class?
For Reference here is my extended class:
[DirectoryObjectClass("group")]
[DirectoryRdnPrefix("CN")]
public class UserPrincipalClassExtensions : System.DirectoryServices.AccountManagement.UserPrincipal
{
PrincipalContext context;
public UserPrincipalClassExtensions(PrincipalContext context)
: base(context)
{ this.context = context; }
public UserPrincipalClassExtensions(PrincipalContext context, string samAccountName, string Password,bool enabled)
: base(context, samAccountName, Password, enabled)
{
}
[DirectoryProperty("company")]
public string Company
{
get
{
if (ExtensionGet("company").Length != 1)
return null;
return (string)ExtensionGet("company")[0];
}
set { this.ExtensionSet("company", value); }
}
// Implement the overloaded search method FindByIdentity.
public static new UserPrincipalClassExtensions FindByIdentity(PrincipalContext context, string identityValue)
{
return (UserPrincipalClassExtensions)FindByIdentityWithType(context, typeof(UserPrincipalClassExtensions), identityValue);
}
// Implement the overloaded search method FindByIdentity.
public static new UserPrincipalClassExtensions FindByIdentity(PrincipalContext context, IdentityType identityType, string identityValue)
{
return (UserPrincipalClassExtensions)FindByIdentityWithType(context, typeof(UserPrincipalClassExtensions), identityType, identityValue);
}
}
This is a Call that Returns null:
SPUser user = SPContext.Current.Web.CurrentUser;
using (PrincipalContext principalContext = new PrincipalContext(ContextType.Domain, getUserDomain(user)))
{
UserPrincipalClassExtensions UserInfos = UserPrincipalClassExtensions.FindByIdentity(principalContext,IdentityType.SamAccountName,user.Name);
}
This is a Call that Returns a UserPrincipal but without the company field value:
SPUser user = SPContext.Current.Web.CurrentUser;
using (PrincipalContext principalContext = new PrincipalContext(ContextType.Domain, getUserDomain(user)))
{
UserPrincipal UserInfos = UserPrincipal.FindByIdentity(principalContext,IdentityType.SamAccountName,user.Name);
}
If you need any further info, let me know!
Thanks for in advance!
EDIT
After searching a little more and decompiling de DLL in IL Spy, I've noticed that my code had an issue:
[DirectoryObjectClass("Person")]
[DirectoryRdnPrefix("CN")]
DirectoryObjectClass had to be "Person"
Hope this is going to help some body else!
Related
In my MVC 5 web app I have this (in AccountController.cs):
// Used for XSRF protection when adding external sign ins
private const string XsrfKey = "XsrfId";
and
public string SocialAccountProvider { get; set; }
public string RedirectUri { get; set; }
public string UserId { get; set; }
public override void ExecuteResult(ControllerContext context)
{
var properties = new AuthenticationProperties { RedirectUri = RedirectUri };
if (UserId != null)
{
properties.Dictionary[XsrfKey] = UserId;
}
context.HttpContext.GetOwinContext().Authentication.Challenge(properties, SocialAccountProvider);
}
How exactly is it being used for protection?
Should I set the value of XsrfKey to something more random?
Take a look at ManageController methods LinkLogin and LinkLoginCallback:
//
// POST: /Manage/LinkLogin
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LinkLogin(string provider)
{
// Request a redirect to the external login provider to link a login for the current user
return new AccountController.ChallengeResult(provider, Url.Action("LinkLoginCallback", "Manage"), User.Identity.GetUserId());
}
//
// GET: /Manage/LinkLoginCallback
public async Task<ActionResult> LinkLoginCallback()
{
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync(XsrfKey, User.Identity.GetUserId());
if (loginInfo == null)
{
return RedirectToAction("ManageLogins", new { Message = ManageMessageId.Error });
}
var result = await UserManager.AddLoginAsync(User.Identity.GetUserId(), loginInfo.Login);
return result.Succeeded ? RedirectToAction("ManageLogins") : RedirectToAction("ManageLogins", new { Message = ManageMessageId.Error });
}
These are the methods that handle linking of external accounts (i.e. Google, Facebook, etc.). The flow goes like this:
User clicks "Link Account" button, which calls a POST to LinkLogin method.
LinkLogin returns ChallengeResult object, with callback url set to LinkLoginCallback method.
ChallengeResult.ExecuteResult is called by MVC framework, calls IAuthenticationManager.Challenge, which causes a redirect to the specific external login provider (let's say: google).
User authenticates with google, then google redirects to callback url.
The callback is handled with LinkLoginCallback. Here, we want to prevent XSRF and verify that the call was initiated by a user, from a page served by our server (and not by some malicious site).
Normally, if it was a simple GET-POST sequence, you would add a hidden <input> field with an anti-forgery token and compare it with a corresponding cookie value (that's how Asp.Net Anti-Forgery Tokens work).
Here, the request comes from external auth provider (google in our example). So we need to give the anti-forgery token to google and google should include it in the callback request. That's exactly what state parameter in OAuth2 was designed for.
Back to our XsrfKey: everything you put in AuthenticationProperties.Dictionary will be serialized and included in the state parameter of OAuth2 request - and consequentially, OAuth2 callback. Now, GetExternalLoginInfoAsync(this IAuthenticationManager manager, string xsrfKey, string expectedValue) will look for the XsrfKey in the received state Dictionary and compare it to the expectedValue. It will return an ExternalLoginInfo only if the values are equal.
So, answering your original question: you can set XsrfKey to anything you want, as long as the same key is used when setting and reading it. It doesn't make much sense to set it to anything random - the state parameter is encrypted, so no one expect you will be able to read it anyway.
Just leave it as is:
As the name of the member states it is a key:
private const string XsrfKey = "XsrfId";
It is defined in this manner to avoid "magic numbers" and then is used a little down in the scaffold code:
public override void ExecuteResult(ControllerContext context)
{
var properties = new AuthenticationProperties { RedirectUri = RedirectUri };
if (UserId != null)
{
properties.Dictionary[XsrfKey] = UserId;
}
context.HttpContext.GetOwinContext().Authentication.Challenge(properties, LoginProvider);
}
The value of the dictionary item is then set to the UserId property in the above code by using the XsrfKey member as the key.
IOW the code is already setting the XSRF dictionary item to the value of the user ID in the snippet. If you change the XsrfKey members value to anything else you will cause problems down the line, since the expected key "XsrfId" will have no value set.
If by changing it to something more random you are implying to change the value and not they key of the dictionary, or in other words, not set it to the user id then please see the following for an explanation of the anti forgery token inner workings.
http://www.asp.net/mvc/overview/security/xsrfcsrf-prevention-in-aspnet-mvc-and-web-pages
I have an extremely odd error and wondered if anyone knew the reason for this.
When I create a new DataObject and TableController called Content and ContentController respectively, it doesn't register the tablecontroller and the help documentation it automatically generates has lost its styling.
I can't connect to the controller at all but all other controllers work as expected.
If I just rename it to DataController and that's just the name of the controller, not the dataobject everything works perfectly.
Is ContentController a reserved word of some kind or is this just specifically happening on my machine?
public class DataController : TableController<Content>
{
protected override void Initialize(HttpControllerContext controllerContext)
{
base.Initialize(controllerContext);
MobileContext context = new MobileContext();
DomainManager = new EntityDomainManager<Content>(context, Request, Services);
}
// GET tables/Content
public IQueryable<Content> GetAllContent()
{
return Query();
}
// GET tables/Content/48D68C86-6EA6-4C25-AA33-223FC9A27959
public SingleResult<Content> GetContent(string id)
{
return Lookup(id);
}
// PATCH tables/Content/48D68C86-6EA6-4C25-AA33-223FC9A27959
public Task<Content> PatchContent(string id, Delta<Content> patch)
{
return UpdateAsync(id, patch);
}
// POST tables/Content/48D68C86-6EA6-4C25-AA33-223FC9A27959
public async Task<IHttpActionResult> PostContent(Content item)
{
Content current = await InsertAsync(item);
return CreatedAtRoute("Tables", new { id = current.Id }, current);
}
// DELETE tables/Content/48D68C86-6EA6-4C25-AA33-223FC9A27959
public Task DeleteContent(string id)
{
return DeleteAsync(id);
}
}
An MVC project will create an application directory called Content. This will override your route mapping to the ContentController.
You can get around this if desired through changing RouteMaps and other trickery although probably the simpliest answer is to change the name of the controller...
I have a class in my MVC5 application that deals with some user related functionality and has a dependency on HttpContext.Current.User as shown below
public interface IUser
{
// return roles of currently logged in user
string[] GetRoles;
}
public Class User : IUser
{
private HttpContext context;
// constructor
public User(HttpContext user)
{
this.context = user
}
// get roles
public string[] GetRoles()
{
string username = this.context.User.Identity.Name;
// get roles through some DB calls
string[] roles = someDbCalls();
return roles;
}
}
I have it setup for dependency injection using Ninject in NinjectWebCommon.cs as
kernel.Bind<IUser>().To<User>().WithConstructorArgument("user", x => HttpContext.Current);
This works fine if called from anywhere in my code except in my custom RolesProvider which is setup as shown below
public class CustomRoleProvider : RoleProvider
{
[Inject]
public IUser user {get; set;}
public override string[] GetRolesForUser(string username)
{
return this.user.GetRoles();
}
}
The call to GetRoles() from my custom role provider fails because HttpContext.Current.User injected by Ninject under this case is null. Any idea on what I may be doing wrong?
Edit:
On further testing, it appears that the problem is with the way I am using Ninject in my custom Roles provider. Using the attribute injection as shown below
[Inject]
public IUser user {get; set;}
works only the first time and subsequent calls fail with HttpContext.Current.User is null error. I have fixed it in a hacky way by forcing the injection to happen each time I call the GetRoles method as shown below
public class CustomRoleProvider : RoleProvider
{
private IUser user;
public override string[] GetRolesForUser(string username)
{
// force ninject to inject a new instance of my interface
var user = DependencyResolver.Current.GetService<IUser>();
return user.GetRoles();
}
}
Not sure why this works and so I am leaving this question open if someone can provide an explanation.
It appears that by the time the role provider is called, the HTTPContext.Current is not yet set. This leads to other issues with custom RolesProvider (like the Null Reference Exception due to EtwTracing bug see: SqlRoleProvider on IIS8 Express
).
If you really need the HTTPContext.Current instead of using the Thread's PrincipalIdentity, you can setup your app to use compatibility mode. This appears to resolve the problem by setting up the HttpContext.Current sooner:
https://social.msdn.microsoft.com/Forums/en-US/8ee88c92-5e8a-4c66-ace7-887eb500e1cb/httpcontextcurrent-always-been-null
I have the following code inside MyDataService.svc.cs (This is an example from DevExpress):
namespace MyDataService {
[System.ServiceModel.ServiceBehavior(IncludeExceptionDetailInFaults = true)]
[JSONPSupportBehavior]
public class DataService : DataService<TestDataEntities>, IServiceProvider {
public static void InitializeService(DataServiceConfiguration config) {
config.SetEntitySetAccessRule("*", EntitySetRights.AllRead);
config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V3;
}
public object GetService(Type serviceType) {
if (serviceType == typeof(IDataServiceStreamProvider)) {
return new ImageStreamProvider();
}
return null;
}
protected override void OnStartProcessingRequest(ProcessRequestArgs args) {
CustomBasicAuth.Authenticate(HttpContext.Current);
if (HttpContext.Current.User == null)
throw new DataServiceException(401, "Invalid login or password");
base.OnStartProcessingRequest(args);
}
}
}
So while this is will check the Entity for a username and password, how safe is it that config.SetEntitySetAccessRule is set to AllRead. Wouldn't someone just be able to see this information on a url such as www.website.com/MyDataService.svc/Customer (where Customer is the table). If this is not so can someone please fill in the conceptual gap I am facing. Thanks!
You are correct that all entities will be returned when queried - AllRead just disallows insert updates and deletes.
You will need to use Query Interceptor to add your logic to restrict users to the set of data they have permission to view, for example adding a check user id to the query.
I have the following question that's been nagging at me for quite some time.
I'd like to model the following domain entity "Contact":
public class Contact:IEntity<Contact>
{
private readonly ContactId _Id;
public ContactId Id
{
get { return this._Id; }
}
private CoreAddress _CoreAddress;
public CoreAddress CoreAddress
{
get { return this._CoreAddress; }
set
{
if (value == null)
throw new ArgumentNullException("CoreAddress");
this._CoreAddress = value;
}
}
private ExtendedAddress _ExtendedAddress;
public ExtendedAddress ExtendedAddress
{
get { return this._ExtendedAddress; }
set
{
if (value == null)
throw new ArgumentNullException("ExtendedAddress");
this._ExtendedAddress = value;
}
}
private readonly IList<ContactExchangeSubscription> _Subscriptions
= new List<ContactExchangeSubscription>();
public IEnumerable<ContactExchangeSubscription> Subscriptions
{
get { return this._Subscriptions; }
}
public Contact(ContactId Id, CoreAddress CoreAddress, ExtendedAddress ExtendedAddress)
{
Validations.Validate.NotNull(Id);
this._Id = Id;
this._CoreAddress = CoreAddress;
this._ExtendedAddress = ExtendedAddress;
}
}
As you can see it has a collection of subscriptions. A subscription is modeled like this:
public class ContactExchangeSubscription
{
private ContactId _AssignedContact;
public ContactId AssignedContact
{
get { return this._AssignedContact; }
set
{
if (value == null)
throw new ArgumentNullException("AssignedContact");
this._AssignedContact = value;
}
}
private User _User;
public User User
{
get { return this._User; }
set
{
Validations.Validate.NotNull(value, "User");
this._User = value;
}
}
private ExchangeEntryId _EntryId;
public ExchangeEntryId EntryId
{
get { return this._EntryId; }
set
{
if (value == null)
throw new ArgumentNullException("EntryId");
this._EntryId = value;
}
}
public ContactExchangeSubscription(ContactId AssignedContact, User User, ExchangeEntryId EntryId)
{
this._AssignedContact = AssignedContact;
this._User = User;
this._EntryId = EntryId;
}
}
Now I've been thinking that I shouldnt model a storage technology (Exchange) in my domain, after all, we might want to switch our application to other subscription providers. The property "EntryId" is specific to Exchange. A subscription would always need a User and a ContactId, though.
Is there a better way to model the Subscription? Should I use a factory or abstract factory for the Subscription type to cover other types of subscriptions, should the need arise?
EDIT: So let's toss an abstract factory in the ring and introduce some interfaces:
public interface IContactSubscriptionFactory
{
IContactSubscription Create();
}
public interface IContactSubscription
{
ContactId AssignedContact { get;}
User User { get; }
}
How would a concrete factory for a ContactExchangeSubscription be coded? Remember that this type will need the EntryID field, so it has to get an additional ctr parameter. How to handle different constructor paremeters on different sub-types in factories in general?
I think the answer is staring you in the face in that you need to work against an interface making it easier to introduce new subscription providers (if that's the right term) in the future. I think this is more of an OO design question that DDD.
public interface ISubscriptionProvider
{
ContactId AssignedContact { get; }
User User { get; }
}
And the code in your contract becomes
private readonly IList<ISubscriptionProvider> _subscriptions
= new List<ISubscriptionProvider>();
public IEnumerable<ISubscriptionProvider> Subscriptions
{
get { return _subscriptions; }
}
With regards to using a factory; the purpose of a factory is to construct your domain objects when a creation strategy is required. For example a SubscriptionProviderFactory could be used within your repository when you rehydrate your aggregate and would make the decision to return the ContactExchangeSubscription (as an ISubscriptionProvider) or something else based on the data passed into it.
One final point but perhaps this is just because of the way you have shown your example. But I would say your not really following DDD, the lack of behaviour and with all your propeties having public getters and setters, suggestions your falling into the trap of building an Aemic Domain Model.
After some research I came up with this. Code first, explanation below:
public interface IContactFactory<TSubscription> where TSubscription : IContactSubscription
{
Contact Create(ContactId Id, CoreAddress CoreAddress, ExtendedAddress ExtendedAddress, TSubscription Subscription);
}
public class ContactFromExchangeFactory : IContactFactory<ContactExchangeSubscription>
{
public Contact Create(ContactId Id, CoreAddress CoreAddress, ExtendedAddress ExtendedAddress, ContactExchangeSubscription ExchangeSubscription)
{
Contact c = new Contact(Id, CoreAddress, ExtendedAddress);
c.AddSubscription(ExchangeSubscription);
return c;
}
}
I realized that I dont need a factory for the Contactsubscription but rather for the contact itself.
I learned some things about factories along the way:
They are only to be used when creating (really) new entities, not when rebuilding them from a SQL DB for example
They live in the domain layer (see above!)
Factories are more suitable for similar objects that differ in behaviour rather than data
I welcome comments and better answers.