I am trying to add to the authentication system provided in the Silverlight 4 business template as my model does not completely fit that provided in the template. I have an existing web service that performs my authentication and provides roles and also permitted operations for each role. This is the model provided by AzMan/Authentication Manager.
However, rather than just get a single role, following authentication I provide the user with a list of available roles and allow the user to select one of these roles and then get a list of operations/actions for that selected role.
The problem that I have is that I can't work out how to add new methods to the authenticationservice to allow me to get the operations for the current user, and currently selected role in order to complete the login process e.g.
public SessionInfo GetOperations(string username, string selectedRole)
{
SessionInfo sessionInfo;
using (AzManServiceClient azClient = new AzManServiceClient("AnonymousAuthentication"))
{
sessionInfo = azClient.LoginUserByUsername("msldap://CN=LiveApps,CN=Program Data,DC=HLSUK,DC=local", "AIRS", selectedRole, null, username);
}
return sessionInfo;
}
The above method is not accessible from the LoginForm.xaml.cs using WebContextBase.Current.Authentication... Only methods such as Login are visible which is even more baffling because I can't see these methods in authenticationbase. I'm completely confused. How do I add new methods to the authentication service, or should I create a new domainservice, or should I access the azman service to get the operations directly from the silverlight client.
Have you tried to Override the methods in AuthenticationBase?
Then you can expand your authenticationservice with whatever logic you want.
<EnableClientAccess()>
Public Class AuthenticationRiaService
Inherits AuthenticationBase(Of UserAccount)
Protected Overrides Function ValidateUser(ByVal userName As String, ByVal password As String) As Boolean
End Function
End Class
Also set
WebContext.Current.Authentication To your authenticationservice as found in namespace System.ServiceModel.DomainServices.Client.ApplicationServices
Sorry for stupid VB code. :D
Related
I am learning ASP.NET MVC, and confused as to how can I ensure unique values for columns (username & email) for a table.
Can anybody help me with a sample code or a link to the tutorial which shows & explains this?
EDIT:
I know that I can apply an unique key constraint on my table columns and achieve it. However, I want to know how can I achieve it via ASP.NET MVC code?
UPDATE:
I wish to do a check in my application such that no duplicated values are passed to DAL, i.e. achieve a check before inserting a new row.
Mliya, this is something you are controlling at the database level, not at the application level.
If you are designing a database with a users table in which you would like to constraint the username and email columns to be UNIQUE you should create a UNIQUE INDEX on such columns.
without knowing your backend database (mySQL, SQL Server, MS Access, Oracle...) it's not the case to show you pictures or tell much more, just create the table with the designer and add these unique constraints to those columns and by design you will be sure no duplicated values will ever be inserted for username and email.
I also suggest you to create an ID column which would be set as PK (primary key, which means it will be automatically set as NON NULL and UNIQUE).
From your ASP.NET MVC application you should of course make sure that no duplicated values are then passed to the DAL for username and email. You could do this in different ways, the easiest is probably to check before inserting a new row if any user already exists with that username and/or email and if so you can show a notification message telling the user to please select another pair of values.
In an ASP.NET MVC architecture, you should try to do most of your validation in the Model, but with low-level validation rules like these, it's sometimes impossible. What you should look to for answers then is Domain-driven Design (DDD) where Application Services can solve such low-level needs.
Application Services will have access to the database (either directly, or better yet; indirectly through repositories) and can perform low-level validation and throw ValidationException or something similar (with detailed information the Controller can act upon and respond to the user) when a prerequisite or business rule isn't met.
S#arp Architecture implementes all of this in a best-practice framework that you can use as a basis for your ASP.NET MVC applications. It is highly opinionated towards DDD principles and NHibernate, and it will sometimes force your hand on how you do stuff, which is kind of the point. The most important part about it is that it learns you how to deal with these kinds of problems.
To answer your question more concretely and in the spirit of DDD, this is how I would solve it:
public class UserController
{
private readonly IUserService userService;
public UserController(IUserService userService)
{
// The IUserService will be injected into the controller with
// an "Inversion of Control" container like NInject, Castle Windsor
// or StructureMap:
this.userService = userService;
}
public ActionResult Save(UserFormModel userFormModel)
{
if (userFormModel.IsValid)
{
try
{
// Mapping can be performed by AutoMapper or some similar library
UserDto userDto = Mapper.Map<UserDto>(userFormModel);
this.userService.Save(userDto);
}
catch (ValidationException ve)
{
ViewBag.Error = ve.Detail;
}
}
// Show validation errors or redirect to a "user saved" page.
}
}
public class UserService : IUserService
{
private readonly IUserRepository userRepository;
public UserService(IUserRepository userRepository)
{
// The IUserRepository will be injected into the service with
// an "Inversion of Control" container like NInject, Castle Windsor
// or StructureMap:
this.userRepository = userReposityr;
}
public UserDto Save(UserDto userDto)
{
using (this.userRepository.BeginTransaction())
{
if (!this.userRepository.IsUnique(userDto.UserName))
{
// The UserNameNotUniqueValidationException will inherit from ValidationException
// and build a Detail object that contains information that can be presented to
// a user.
throw new UserNameNotUniqueValidationException(userDto.UserName);
}
userDto = this.userRepository.Save(userDto);
this.userRepository.CommitTransaction();
return userDto;
}
}
}
I've been able to find lots of stuff about using the ASP .NET Membership provider in WPF. I've even got the ASP .NET Role provider working in my WPF application. These are very easy to use in WPF. What I can't find any information about is how to use the ASP .NET PROFILE provider in a WPF application.
My application will be running on a laptop located in a moving vehicle; it cannot use Web Services to connect to a server and authenticate the user. My code needs to be able to retrieve the user's profile information from the local database.
I've tried several times to build a class that derives from ProfileBase but nothing I've tried has worked. To elaborate, the code below is an excerpt from my latest failed attempt:
public class UserProfile : ProfileBase {
[CustomProviderData( "AlarmsToDisplay;int" )]
public int AlarmsToDisplay {
get { return (int) GetPropertyValue( "AlarmsToDisplay" ); }
set { SetPropertyValue( "AlarmsToDisplay", value ); }
}
// Other properties following the same pattern follow.
private UserProfile() { }
public static UserProfile GetUserProfile( string username ) {
return Create( username ) as UserProfile;
}
public static UserProfile GetUserProfile( string username, bool isAuthenticated ) {
return Create( username, isAuthenticated ) as UserProfile;
}
}
In this code, the method GetUserProfile( string username, bool isAuthenticated ) returns null. I believe this is because the compiler can't cast a ProfileBase to a UserProfile, even though UserProfile descends from ProfileBase. Please correct me if I'm wrong.
Can someone point me to an existing article or describe the proper way to build a user profile class that extends from ProfileBase?
I've managed to resolve this issue on my own. Here's what works:
Remove the attributes from all of the CLR properties. These do not work with the default aspnet_Profiles table.
In app.Config, you need to add an "inherits" attribute to the tag. This has to be set to the fully qualified name of your class, separated from the assembly file name (no extension) by a comma.
Once these things were done, the GetUserProfile method and the ProfileBase class's Create methods return UserProfile objects.
My IS manager provided me with parameters in this format and I am trying use C# to validate a user against Active directory.
Here is a code sample (of course not the real credentials). How do I use these parameters to against a DirectoryEntry object so I can search for users etc.
provider-url=ldap://email.acmetech.com:1111/
base-dn= DC=acmetecg,DC=com
security-authentication= simple
security-principal= CN=ldap,cn=users,DC=acmetech,DC=com
security-credentials= Ldap000
I know this should be simple but its been years since I've programmed active directory.
Edit: How do I pass my params to a directory entry object so I can query objects in AD?
Using .NET 3.5 it's pretty easy.
using(PrincipalContext pc = new PrincipalContext(ContextType.Domain, "acmetecg"))
{
// check the creds (assuming ldap is the user name, and ldap000 is the password)
bool isValid = pc.ValidateCredentials("ldap", "ldap000")
}
My question is similar to this question. I hope I can provide some more detail and context to get it answered.
So here's some context: I have a simple in-house silverlight (ver 4) app with WCF Ria services that I'm building for our small support team. It uses authentication against a third-party vended database, but all other user information, e.g. FriendlyName and Roles (only 1 role per user) comes from our own database. I'm trying to keep this simple and don't want to implement custom membership and role providers.
I have few domain service operations that I want to restrict to certain roles, so I tried using the RequiresRole attribute like so:
[RequiresRole("Admin", "HelpDesk", "Billing" )]
public RisStudyInfo GetStudyInfo(string accession) {
return ris.GetStudyInfo(accession);
}
On the client side WebContext.Current.User.IsInRole("Admin") returns true, but I always get access denied when calling the service. The RequiresAuthentication attribute works as expected.
Below is the implementation of my AuthenticationService. The User class simply inherits from UserBase and adds the FriendlyName property. Any ideas what I'm doing wrong?
[EnableClientAccess]
public class AuthenticationService : AuthenticationBase<User> {
UserDataService userData = new UserDataService();
protected override bool ValidateUser(string userName, string password) {
var auth = new DatabaseAuthenticator();
return auth.Authenticate(userName, password);
}
protected override User GetAuthenticatedUser(IPrincipal principal) {
User user = null;
if (principal.Identity.IsAuthenticated) {
user = new User();
user.FriendlyName = userData.GetFriendlyName(principal.Identity.Name);
user.Name = principal.Identity.Name;
user.Roles = GetRolesFor(user.Name);
}
return user;
}
private IEnumerable<string> GetRolesFor(string username) {
IList<string> roles = new List<string>();
string role = userData.GetRolesFor(username);
if (role != null)
roles.Add(role);
return roles;
}
Figured it out. At least 2 things wrong. First clue found here. The second clue here
1.Turns out I really do need to write a custom role provider. Only need to implement GetRolesForUser though.
public override string[] GetRolesForUser(string username) {
return new string[] { _userService.GetRolesFor(username) };
}
2.Configure the custom role provider correctly in the web.config
<roleManager cacheRolesInCookie="true" enabled="true" defaultProvider="MyRoleProvider">
<providers>
<add name="MyRoleProvider" type="MyProject.Web.Providers.MyRoleProvider, MyProject.Web"/>
</providers>
</roleManager>
I solved this one by using the local credential store to cache credentials. Whenever a local cred check fails a foreign check occurs and the cache is populated/updated. This was a trivial override of the ValidateUser method. It does mean that stale passwords continue to work until the updated password is used (it will fail locally, pass remotely and trigger an update).
This approach meant that internally everything worked as per an out of the box configuration with no need for any other mods (apart from removing the local create-a-user links).
I'm currently trying out Silverlight with RIA Services. I'm implementing a simple login form. I'm also using the provided Authentication Domain Service template which generates the following file:
[EnableClientAccess]
public class AuthenticationDomainService : AuthenticationBase<User>
{
// To enable Forms/Windows Authentication for the Web Application,
// edit the appropriate section of web.config file.
}
public class User : UserBase
{
// NOTE: Profile properties can be added here
// To enable profiles, edit the appropriate section of web.config file.
// public string MyProfileProperty { get; set; }
public int DefaultRows { get; set; }
}
Now I can login/logout without problem in my application. In the Silverlight app, after logging in, the line:
WebContext.Current.User.IsAuthenticated;
return true.
However, I need to persist this across sessions (i.e. when I reload the page using F5).
Currently, when the page reloads, I have to re-login.
Here is my Login code:
WebContext.Current.Authentication.Login(new LoginParameters(this.UserName, this.Password, true, string.Empty),
(LoginOperation loginOperation) =>
{
if (loginOperation.LoginSuccess)
{
NotificationMessage Message = new NotificationMessage(this, null, "CLOSE");
Messenger.Default.Send<NotificationMessage>(Message);
}
}, null);
The third parameter of the Login method is the IsPersistent parameter. From the MSDN Docs, I'd think that when setting it to true, the next time I load the page, the user would still be logged in. However, this is not the case.
Do I somehow need to read a cookie which has been set internally and then login in the background with the username/password provided by that cookie? Or is there some other magic at work here?
I hope somehow has already done this.
Thanks in advance
After going through the Silverlight Business Application template, I found this line of code:
WebContext.Current.Authentication.LoadUser(this.Application_UserLoaded, null);
A good place to call it is in the Application's Startup event inside App.xaml.cs. This will load the authenticated user from the server.
I thought I'd post this if anyone happens to stumble across the same problem.