Overriding IAuthSession OnRegistered handler - authentication

I am using ServiceStack's SocialBootstrapApi and it contains a class CustomUserSession that I can use to override the OnRegistered method. I want to override it because I am attempting to obtain information about the registration so that I can publish an event that a new user has registered. This handler provides an instance of the RegistrationService that handled the registration but not anything about the registration request itself or the resulting UserAuth instance. For instance, I'd like to get the e-mail address used to register.
public override void OnRegistered(IServiceBase registrationService)
{
base.OnRegistered(registrationService);
// Ideally, I could do get the registered user's primary e-mail address from the UserAuth instance.
var primaryEmail = ((RegistrationService) registrationService)
.UserAuthRepo
.GetUserAuth(this, null) //<--- 'this' is a mostly empty session instance
.PrimaryEmail;
}
This of course doesn't work because the session instance I'm using for the GetUserAuth call doesn't contain any of the necessary authentication information to be useful for looking up the user's authentication information. So GetUserAuth returns null as you would expect. So how should I go about obtaining this information? Would it be incorrect design for the OnRegistered handler to be passed the UserAuth instance created by the RegistrationService?
public interface IAuthSession
{
...
void OnRegistered(IServiceBase registrationService, UserAuth userAuth); // <-- new signature
...
}
That would be convenient! :)
Or perhaps there's another way to go about this?
Thanks in advance.

So how should I go about obtaining this information?
You should be able to access all the data of the Registration request via the registrationService. You just have to do a little digging and casting...
public override void OnRegistered(IServiceBase registrationService)
{
base.OnRegistered(registrationService);
var requestContext = (HttpRequestContext)registrationService.RequestContext;
var dto = ((Registration)requestContext.Dto);
var primaryEmail = dto.Email;
}
Would it be incorrect design for the OnRegistered handler to be passed the UserAuth instance created by the RegistrationService?
I'll leave design decisions to the professionals. The above code should work. The casting seems a bit ugly but all the necessary data is there.
I do not like hack into SS, so I chose to select user auth info from UserAuth collection by dto.UserName

Related

ServiceStack: Can we Pass Data through a RequestFilterAttribute to the calling service

Maybe I'm thinking about this wrong, but I'm trying to create a custom attribute for our CMS to handle auth checks.
https://gist.github.com/sitefinitysteve/62ab761256a64a84d8a6#file-sitefinityjwt-cs-L39
So if this service is called from within the CMS from a logged in user, user data is all there for the service method already.
But in the context of being called from an app, the user is technically Anonymous, however I can decode the token and get the user just fine... but not sure how to like pass that over to the service.
Am I just maybe looking at this wrong, and the proper thing to do is to call a CMS API method to just log that person in (seems slow if I already have the persons user object from line 33, and the service context expires instantly.
Use Request.Items Dictionary
You would use the IRequest.Items dictionary for any data you want to pass throughout ServiceStack's Request Pipeline:
//RequestFilter:
req.Items["info"] = new MyRequestInfo { ... };
In Service:
var info = (MyRequestInfo)base.Request.Items["info"];
Have DTO's share common interface
Another option for adding extra info to your Service is to have Request DTO's implement an interfaces, e.g:
public interface IHasInfo
{
MyRequestInfo Info { get; set; }
}
Which you could then populate in your Request Filter, e.g:
((MyRequestInfo)dto).Info = new MyRequestInfo { ... };
Access in Service like any other DTO property, e.g:
public object Any(Request request)
{
var info = request.Info;
}

Extending Restlet 2.3 ClientInfo

Is it possible to extend org.restlet.data.ClientInfo? I need a convenient way of adding a List<String> permissions to complement the existing List<Role> roles. In a perfect world I would be able to add List<Permission> permissions but the former is perfectly acceptable.
I need to be able to get this from the request: org.restlet.resource.Resource.getRequest().getClientInfo().getPermissions()
I don't think that it's possible to add something within the class ClientInfo since it's a class that is managed by the Restlet engine. You can't subclass it to add a field permissions (you don't have the hand on the client info instantiation).
That said, you can leverage the context attributes. I mean that you can fill within your Enroler implementation an attribute permissions for the request, as described below:
public class MyEnroler implements Enroler {
private Application application;
public MyEnroler(Application application) {
this.application = application;
}
public void enrole(ClientInfo clientInfo) {
// Roles
Role role = new Role(application, "roleId",
"Role name");
clientInfo.getRoles().add(role);
// Permissions
Request request = Request.getCurrent();
List<Permission> permissions = new ArrayList<Permission>();
request.getAttributes().put("permissions", permissions);
Permission permission = (...)
permissions.add(permission);
}
Hope it helps you,
Thierry

using log4net and IHttpModule with a WCF service

I have a Website that contains a number of webpages and some WCF services.
I have a logging IHttpModule which subscribes to PreRequestHandlerExecute and sets a number of log4net MDC variables such as:
MDC.Set("path", HttpContext.Current.Request.Path);
string ip = HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];
if(string.IsNullOrWhiteSpace(ip))
ip = HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"];
MDC.Set("ip", ip);
This module works well for my aspx pages.
To enable the module to work with WCF I have set aspNetCompatibilityEnabled="true" in the web.config and RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed on the service.
But when the service method is called the MDC no longer contains any of the set values. I have confirmed they are being set by putting a logging method in the PreRequestHandlerExecute.
I think the MDC is loosing the values because in the log I can see the PreRequestHandlerExecute handler method and service method calls are on separate
threads.
The post log4net using ThreadContext.Properties in wcf PerSession service suggests using log4net.GlobalContext but I think that solution would run into issues if two users hit the application at the same time as GlobalContext is shared by all threads.
Is there a way to make this work?
Rather than taking the values from the HttpContext and storing them in one of log4net's context objects, why not log the values directly from the HttpContext? See my answer to the linked question for some techniques that might work for you.
Capture username with log4net
If you go to the bottom of my answer, you will find what might be the best solution. Write an HttpContext value provider object that you can put in log4net's GlobalDiagnosticContext.
For example, you might do something like this (untested)
public class HttpContextValueProvider
{
private string name;
public HttpContextValueProvider(string name)
{
this.name = name.ToLower();
}
public override string ToString()
{
if (HttpContext.Current == null) return "";
var context = HttpContext.Current;
switch (name)
{
case "path":
return context.Request.Path;
case "user"
if (context.User != null && context.User.Identity.IsAuthenticated)
return context.User.Identity.Name;
case "ip":
string ip = context.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];
if(string.IsNullOrWhiteSpace(ip))
ip = context.Request.ServerVariables["REMOTE_ADDR"];
return ip;
default:
return context.Items[name];
}
return "";
}
}
In the default clause I assume the name, if it is not a specifically case that we want to handle, represents a value in the HttpContext.Current.Items dictionary. You could make it more generic by also adding the ability to access Request.ServerVariables and/or other HttpContext information.
You would use this object like so:
Somewhere in your program/web site/service, add some instances of the object to log4net's global dictionary. When log4net resolves the value from the dictionary, it will call ToString before logging the value.
GDC.Set("path", new HttpContextValueProvider("path"));
GDC.Set("ip", new HttpContextValueProvider("ip"));
Note, you are using log4net's global dictionary, but the objects that you are putting in the dictionary are essentially wrappers around the HttpContext.Current object, so you will always be getting the information for the current request, even if you are handling simultaneous requests.
Good luck!

How can I update user data form session in ServiceStack?

Very simple question: I have a session object in my Service:
var session = this.GetSession(); //IAuthSession
if (!session.IsAuthenticated)
I can modify some values in the session class (e.g. Permissions) based on the parameters passed to the service; then I want to save them.
How?
The direct way of doing it: create a UserAuth object, popolate it with all the fields from IAuthSession, get the IDbConnectionFactory, save it.
Surely there is a faster and better way, but I was not able to find it!
More generally, how can I switch between IAuthSession anf UserAuth? I.e., given a IAuthSession object, how can I obtain a UserAuth object, modify it, and persist the modifications?
I have read this question on how to append metadata to a user login info, but something is still missing.
Once you have added what you need, how do you save it? (I doubt you just add the metadata to both session and UserAuth, and then you use IDbConnectionFactory to save the latter; there must be a better way!)
Old question but worth answering.
The UserAuthRepository being used should have an implementation of the UpdateUserAuth method that can be called to save the UserAuth changes
UpdateUserAuth(UserAuth existingUser, UserAuth newUser, string password)
Another easier way would be to just call the RegisterService using PUT which will update the existing registered user for you.
/// <summary>
/// Update an existing registraiton
/// </summary>
public object Put(Register request)
{
return Post(request);
}
The service call would be something similar to this:
using (var authService = base.ResolveService<RegisterService>())
{
var authResponse = authService.Put(
new Register {
UserName = session.UserName ?? session.Email,
Email = session.Email,
etc...
});
if (authResponse is IHttpError)
throw (Exception)authResponse;
}

How to store info about the authenticated user in WCF?

I have a WCF service where I use a customUserNamePasswordValidatorType (specified in the behaviors\serviceBehaviors\serviceCredentials\userNameAuthentication section of the web.config file).
My custom UserNamePasswordValidator works that way:
public bool Authenticate(string userName, string password)
{
If ( IsUserValid(username, password) )
{
UserInfo currentUser = CreateUserInfo(username);
//
// Here I'd like to store the currentUser object somewhere so that
// it can be used during the service method execution
//
return true;
}
return false;
}
During the service call execution, I need to access the info of the authenticated user. For instance I would like to be able to implement:
public class MyService : IService
{
public string Service1()
{
//
// Here I'd like to retrieve the currentUser object and use it
//
return "Hello" + currentUser.Name;
}
}
My question is how and where should I store the information during the authentication process so that it can be accessed during the call execution process? That storage should only last as long as the "session" is valid.
By the way, I don't use (and don't want to use) secure sessions and/or reliable sessions. So I have both establishSecuritytContext and reliableSessions turned off.
I'm thinking of enabling ASP.NET Compatibility Mode to store the user info in the HttpContext.Current.Session but I have the feeling it's not how it should be done.
Store anything that needs to be persisted into a persistant store - e.g. a database, that's the best way to go.
Store the user info in a user table, e.g. the ASP.NET membership system or something of your own. Keep some kind of a identifying token (username, ID etc.) at hand to retrieve that info from the database when needed.
You should strive to have a stateless WCF service whenever possible - it should never depend on a "state" of any kind other than what's safely stored in a database.