FullAuditedEntity, ObjectMapper: CreatorUserId Null when Creating New Entry or Deleting - c#

I have been posting data to a Full Audited Entity via the API. As it is FullAuditedEntity, it should automatically be created with creatorId, creationTime and a couple other column values. But when I checked in the database, CreatorUserID is null even though CreationTime is there. It should be 1 cos I posted with the default admin. Furthermore, when I delete the rows, the same happens: I can only see DeletionTime but not DeleterUserId.
Below is the data captured by the API Endpoints that I can see using breakpoints:
I experimented with two Object Mapping Methods creating output and output2 but both of them gives the same null value for CreatorUserId. By right, both CreatorUserId and CreationTime should have values by this stage.
[AbpAuthorize]
public async Task<Rule> CreateAsync(CreateRuleInput input)
{
Rule output = Mapper.Map<CreateRuleInput, Rule>(input);
Rule output2 = ObjectMapper.Map< Rule>(input);
return await _ruleManager.Create(output);
}
Is there anything wrong with my object mapping functions?

There is nothing wrong with your object mapping functions.
I experimented with two Object Mapping Methods... By right, both CreatorUserId and CreationTime should have values by this stage.
CreatorUserId and CreationTime only have values after SaveChanges() is called.
I have to put [TenantId] manually in the JSON post object, and it shows in the database.
You should not do that. CreatorUserId is only set if the entity belongs to the current tenant.
I can see UserId which is 1 in breakpoint. Somehow it gets ignored during mapping.
The proper way to create an entity as "Default" tenant admin, is to log in as the tenant admin.
UserId = 1 means you are logged in as host admin. "Default" tenant admin has UserId = 2.
If I don't specify tenantId or put 0, it gives Server Internal Error: 500.
If you want host to create Rule, make sure it implements IMayHaveTenant not IMustHaveTenant.

The problem is with the default Admin because it has no tenant.
From another tenant, I created a new user and post data using the new user's credentials. Then I post the data by leaving out tenantId in the post body. And it works just fine.
I used to specify tenantId in the post data object for the default Admin (Host tenant). If I don't specify tenantId or put 0, it gives Server Internal Error: 500. So, I have to specify tenantId in the post body object. I think because of that it somehow messes with the mappings inside the API because default Admin has no tenant.

Related

C# HttpRequestMessage.GetCorrelationId method returns duplicates

My team and I have been working on this issue for a couple of days and we can't determine the root cause why GetCorrelationId() returns duplicates GUID sometimes.
Within the application that I'm working right now we use the correlation id to tie up the request path.
For example the UI sends a Save request to the API in .NET. When the API calls the save method in the query service from the controller, it pass through the result of Request.GetCorrelationId() to the method call.
The save method in the query service uses this parameter to insert a new row in the audit_logs table with the request information.
The save method then calls other save methods on nested objects that belongs to the main Object, passing the correlation_id generated in the controller.
Something like this
[ Controller ]
var correlation_id = HttpRequestMessage.GetCorrelationId();
{
ParentObject.save(correlation_id) -> {
ChildObject1.save(correlation_id),
ChildObject2.save(correlation_id),
}
}
My question is. Is this an issue of how GetCorrelationId creates the GUID object or there is something wrong with the configuration of the framework?
The framework is .NET Framework 4.6.1
We see this issue in IIS Express and IIS server
I can't share code but I'll do my best to share as much information is needed to troubleshoot this issue.
This are some examples of duplicates GUID
Thanks
OK I found a solution. I'm posting it so if someone else has the same issue can get this example.
After some more digging on Google I found this post
https://www.codeproject.com/Articles/392926/Grouping-application-traces-using-ActivityId
Sebastian is using the ActivityId generated by IIS to tied together the different commands executed during his process.
Then I found the source code of HttpRequestMessageExtensions https://github.com/aspnet/AspNetWebStack/blob/master/src/System.Web.Http/HttpRequestMessageExtensions.cs
There, you can see that the method GetCorrelationId() uses the ActivityId to assign a correlation ID to the request.
ActivityId is not completely random. It is tied to the thread started by IIS. I notice that all our request from localhost:port have the same string at the end b63f-84710c7967bb.
We wanted to tied all the commands executed to save an object or retrieve it. So all we needed was a random Guid generated per request. Following the example posted by Sebastian, I stored the current ActivityId in an aux variable, then assigned a new GUID to it, executed Request.GetCorrelationId() and finally assigned back the ID stored in the aux variable to the ActivityId.
In pseudo code =>
Guid prevActivityId = Trace.CorrelationManager.ActivityId
Trace.CorrelationManager.ActivityId = Guid.NewGuid()
...
guid correlation_id = Request.GetCorrelationId()
...
Trace.CorrelationManager.ActivityId = prevActivityId
Hope this is helpful to someone else.

Multi-site, single code-base user registration using ASPNet.Identity

So I have multiple websites running off one code base (asp.net standard MVC). I'm using the built in ASPNet.Identity methods for users to register/log in (using ApplicationSignInManager, ApplicationUserManager).
Currently, all websites are using a single database to store user information. This is causing a couple of issues:
When user A registers on website A, they are now able to log into website B with the same details. As far as they are aware, they did not register on website B. Not good!
If I constrain user A to only access website A, if that user then tried to register on website B, they get the 'email address already in use' error. Also not good!
I've tried separating the databases, one per site, to get around this issue but I don't know how to dynamically change the DBContext assigned to the ApplicationSignInManager and ApplicationUserManager in my controller.
For example, when a user comes to website A, I grab the connection string for website A and perform login/register actions. I need the domain name to work out which connection string to load, which I can't access until after startup.cs code has run, configuring my manager instances.
I figure other people must have done this. Ideally I need to dynamically change the DBContext AFTER Startup.cs has run. Failing that, I need a nice approach to storing multiple identical email addresses in the same DB
Thanks
If you have a flag somewhere which website the current context is for, i'd say easiest to achieve that is to do two things:
extend the IdentityUser with a Website property, something simple either just an int WebsiteId or a String.
extend the AccountController to use that property wherever needed, I think you'd need to modify "Register" and all "Login" functions to verify the website the account is for.
I managed to find a solution. It's not the most elegant but it solves the issue until I can figure out how to dynamically change the DB Context used during login/register
In IdentityConfig.cs I switched 'RequireUniqueEmail' to false:
manager.UserValidator = new UserValidator<ApplicationUser>(manager)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = false
};
Then when a user registers, I take the email address, make it unique to the website they registered on, and store it in the UserName field (I don't use the UserName for anything). When they log in, I make the same alteration to the entered email address before attempting login.
If this user registers on a different website, the username will be different even though the email is identical. Not perfect, but it works.
E.g.
string uniqueCode = "Website_Specific_String";
var user = new ApplicationUser { uniqueCode + model.Email, Email = model.Email};
var result = await UserManager.CreateAsync(user, model.Password);
and the login
var user = await UserManager.FindByNameAsync(uniqueCode + model.Email);
Still open to better ideas. Thanks

ASP.NET Storing Cross Request Data

In my application I have this entity:
public class Franchise
{
public Guid Id {get;set;}
public string Name {get;set;}
}
When each of my users log in, they are associated with a franchise.
There is another entity:
public class Client
{
public Guid FranchiseId {get;set;}
public virtual Franchise Franchise {get;set;}
public string Name {get;set;}
/* other useful client information */
}
Depending on my user franchise association will determine which clients they shall see (or are allowed to see)
So equivalent of doing
dbContext.Set<Client>().Where(x => x.FranchiseId.Equals(associatedFranchiseId));
I was wondering what the options where of storing that associatedFranchiseId so each request for data can use that Id to select the appropriate dataset.
APPROACH 1
I created a service that gets these associations and returns the correct dataset. I thought I could use this in each controller where I need to get the information. But I thought this maybe costly in database lookup terms. It would have to based on the User, so getting that out of the request.
I just am not sure how I would go about doing this.
The process is:
User Logs In
Application determines the associated franchise
User request some information
The request uses the associated franchise to select the right dataset
I've used something similar and use the session and application spaces for the objects.
So, when the application fires up load all the franchise objects into application:
Application["Franchise"] = MethodToLoadFranchiseInfo();
You can then access this at anytime via Franchise f = Application["Franchise"];
Similarly, for the clients, when they login, load the client info into Session in a similar fashion.
The only caveat if that you'll need to refresh the Application object when there's an update, and same for the session, or require a log out and log back in.
This way you only have one hit to the database and in memory accessible objects!
* Edit to Add *
Just had some more thoughts on this, and probably something I'll look to implement myself. If you put a timestamp in both the application and session objects, if the session object is older than the application object (which will be updated centrally being application wide), then hit the database and refresh the session object.
That way you do not get the log out / log back in situation when something is changed by the backend / admin.

access Auth() object from TryAuthenticate

I am logging into my service from a c# client like so:
serviceClient.Send<ServiceStack.ServiceInterface.Auth.AuthResponse>(
new ServiceStack.ServiceInterface.Auth.Auth() {
UserName = "xxx",
Password = "yyy" }
);
I would now like to use one of the unused strings in the Auth class to pass some additional information (like a programid). I am using my own subclassed CredentialsAuthProvider. Two questions:
Do you recommend any of the "extra" properties in the Auth class to stuff my programid over any others? I was considering using "State", will that mess anything up if I put a string in there?
Is there a way from within the TryAuthenticate override of my CredentialsAuthProvider to access the Auth class instance that was sent to me (so I can access the programid that I stuck into the State property).
Thank you.
Answered half of my question.
Not sure if any of the properties are better than any other, but I'm using "State" to stuff some extra data. Now that multiple end user programs are accessing the same service, sending a program ID in the State property lets me log the programs attempting to log into the service, along with the users.
If you are authenticating by overriding CredentialsAuthProvider, you can override the Authenticate method to gain access to the Authenticate object that is passed in from the user. From there, you can read the State property (or any other).

ServiceStack Authorization - Access Route Information

In the documentation for ServiceStack, it says that the best practice is:
Normally ServiceStack calls the method bool HasPermission(string
permission) in IAuthSession. This method checks if the list
List Permissions in IAuthSession contains the required
permission.
IAuthSession is stored in a cache client as explained above You can
fill this list in the method OnAuthenticated you've overriden in the
first part of this tutorial.
I am integrating with an existing system, and have my custom BasicAuthProvider working (inherited from the base BasicAuthProvider). Authentication is working perfectly, now I am building out the Authorization portion. I plan on using the Permissions list as listed above, but I need access to the Route information to determine if a user has access to a particular resource. I see in the IAuthServiceBase there is an IRequestContext which has the absolute URL, but before going through and parsing that out, I figured there has to be a way to gain access to the ServiceStack Route structure to give me either the class name of the Service being requested, or the DTO the requested service is related to.
Here is the OnAuthenticated method from my BasicAuthProvider class:
public override void OnAuthenticated(IServiceBase authService, IAuthSession session, IOAuthTokens tokens, Dictionary<string, string> authInfo)
{
UserSession sess = (UserSession)session;
Model.User currentUser = UserRepository.GetUserByUsername(session.UserAuthName);
//Fill the IAuthSession with data which you want to retrieve in the app eg:
session.FirstName = currentUser.Person.FirstName;
session.LastName = currentUser.Person.LastName;
session.UserName = currentUser.User1;
sess.CurrentUser = currentUser;
//Important: You need to save the session!
authService.SaveSession(session, TimeSpan.FromDays(1));
}
Under MVC I have used some of the Raw Request Data to get the Controller and Action name before, to determine resource authorization, but this is the first project I am using ServiceStack with.
You may find the [RequiredPermission] attribute or even the implementation of it will help you, e.g. the 3rd parameter passed in a RequestFilter is the Request DTO.
And since a Request DTO maps 1:1 with the service, you can be sure that the request is destined for the IService<TRequest> (or its subclasses e.g. ServiceBase<T>, RestServiceBase<T>).
You can access the type of the service programatically as done in the FilterAttributeCache:
var serviceType = EndpointHost.Metadata.GetServiceTypeByRequest(requestDtoType);
I'm not sure of the exact context/use-case you're trying to support but using the [RequiredPermission] or [RequiredRole] attributes may have what you need which by default validates against the list of roles and permissions available in the built-in UserAuth table.
Externally you can use the /assignroles and /unassignroles web services (as part of the AuthorizationFeature plugin) to assign roles and permissions to users (it requires a user with the Admin role by default).
For more info see the documentation pages on Authentication/Authorization and Validation on the ServiceStack GitHub project wiki.

Categories

Resources