I need to log all update operations from my rest resources and store to a Database log table.
The idea is to store info like:
logged user
operation description about updated / saved entity
updated fields and reference key
My application is Java EE8 compatible, it uses REST / EJB and CDI stuff.
At first I thought of dealing all this stuff on EJB side, however the exposed services don't need to have logged user on the method signature, so adding it would result on a forcing..
Is there any way to send user information, that is normally retrieved by webrequest (we use a session token model authentication) and inject through EJB ?
If your session management is setup correctly, you can just inject the session context via:
#Resource
SessionContext sessionContext;
Then:
sessionContext.getCallerPrincipal().getName()
is your logged in user.
As mentioned before, SessionContext.getCallerPrincipal().getName() doesn't work as authentication mechanism does not provide it.
After some tries I found this:
On EJB side
#RequestScoped
public class UserInfo {
private String userId;
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
if (this.userId != null) throw new UncheckedException("cannot infer userid twice");
this.userId = userId;
}
}
On REST side
#Inject
UserInfo userInfo;
void userAuthenticated(...) {
String userId = ... // get userid from access token through **WebRequest** object
userInfo.setUserId(userId);
}
Side note
I honestly would have preferred to inject userid on UserInfo constructor but I was not allowed doing this as WebRequest object does not belong to EJB context
Alternative way
Move all the logging process to REST side by using a Response filter.
Example code:
#Provider
public class LoggingFilter implements ContainerResponseFilter {
#Context
HttpServletRequest webRequest;
#Context
ResourceInfo resinfo;
#Inject
LoggingService loggingService;
#Override
public void filter(ContainerRequestContext containerRequestContext, ContainerResponseContext containerResponseContext) {
final Method resourceMethod = resinfo.getResourceMethod();
if (resourceMethod.isAnnotationPresent(Loggable.class) && containerResponseContext.getStatusInfo().getFamily() == Response.Status.Family.SUCCESSFUL) {
// get all method's info and log to database ...
}
}
Related
When getting a request in any action of any controller, I look at the jwt know which user is requesting and lookup the user in the database to get some user-data that I want to use throughout the application. E.g. which departments the user belongs to or the users preferred language.
Now I could create a object which wraps these information and send it down the layers and pass it to every method that likes to use some of this data. But I like the data to be available to every method throughout the application without passing it in every method. Like e.g. dependency injection (Seems to late at that point) or something else I can get access to that data quickly.
Any advice of how to handle it?
Try it with the Items property on the HttpContext. By using it you can store data during a single request. The only downside with this approach is that every service needs to have access to the HttpContext to read the values. Values can be added to the Items Dictionary as shown below
public class IndexModel : PageModel
{
//
public void OnGet()
{
HttpContext.Items.Add("Key", new RequestInfo { Key = "RequestKey" });
}
}
class RequestInfo
{
public string Key { get; set; }
}
You can then access the value by registering the IHttpContextAccessor to the IServiceCollection and then using Constructor injection to use the HttpContext in your service so that you can work with the Items Dictionary.
public class Service
{
private IHttpContextAccessor _htp;
public Service(IHttpContextAccessor http)
{
_htp = http;
}
public void Log()
{
Console.WriteLine(((RequestInfo)_htp.HttpContext.Items["Key"]).Key);
}
}
I want to create a column updated by so whenever a user modifies a data, the updated by column is filled with currently logged in user name. I want to use an interface that gets the name of currently logged in user and then in AppDbContext in savechanges method the username is inserted whenever the data is updated. Please someone help me out how to do it as I'm new to ASP.NET core mvc and I got an assignment from University. I will be really thankful to all the experienced programmers here.
I hope I understood your question well. Here it goes:
If you want to retrieve some information about the user that sent a request to your server and you use default identity behavior, then it should be easy. I do not know what version of ASP.NET do you use, I will presume its ASP.NET Core 3 (from you tags and such).
With each request, there is a HttpContext object that remembers information about the HTTP request, the HTTP response, etc. If you inherit your controllers from the Controller/ControllerBase, you can directly access that context. The context contains a property called "User" that contains various information, including your user name.
[AllowAnonymous]
[HttpGet("me")]
public string WhoAmI()
{
//Try this
var userName = HttpContext.User?.Identity?.Name;
//Or even
var userName = User?.Identity?.Name;
return userName;
}
Now, how can you get this name for your AppDbContext context? First of all, the best approach would be to create a service that provides you this name. Let's call it UserRepository. The user repository will accept IHttpContextAccessor that provides you with the HttpContext (and this username). The IHttpContextAccessor will be injected if you use dependency injection (used "automatically" within ASP.NET).
public class UserRepository : IUserRepository
{
private readonly IHttpContextAccessor _httpContextAccessor;
public UserRepository(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public void GetCurrentUserName()
{
var username = _httpContextAccessor.HttpContext.User.Identity.Name;
return username;
}
}
You can add this service to your services and you can inject it in various places. I should be even injectable to your DB context.
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews(); //You probably have this one
services.AddHttpContextAccessor(); //Add the context accessor
services.AddTransient<IUserRepository, UserRepository>(); //This is our user repository
}
And you should be able to use it as:
public class AppDb : DbContext
{
readonly IUserRepository userRepo;
public AppDb(DbContextOptions<AppDb> options,
IUserRepository userRepo)
: base(options)
{
this.userRepo = userRepo;
userRepo.GetCurrentUserName();
}
}
The IUserRepository will "automatically" get injected with each request.
Or, you can just inject the IUserRepository wherever you need. For example in the constructor of a Controller, similarly as shown in the AppDb object. Not sure what you really need.
I'm new to Restlet, but I've followed the tutorial on Restlet's own website and got a basic application up and running. What I'm doing right now is that I'm setting up a basic ServerResource and expose a #Get method.
What I'd like is to be able to invoke /user/{userId} and get the user representation back. Is it possible, somehow, to hand over the mapping of {userId} to Restlet, which in turn would invoke getUser(String userId) in my ServerResource?
Such feature (binding path variables into annotated method parameters) isn't natively supported in the framework. Such mapping in the annotated method signatures is only supported with input representation.
To get the path variables of a request, you can get them from the request object (method getAttribute), as described below:
public class UserServerResource extends ServerResource {
#Get
public User getUser() {
String userId = getAttribute("userId");
User user = (...)
(...)
return user;
}
}
If you want to share this path variable across several methods, you can define it as a instance variable (notice that a new instance of the server resource is created for each request unlike to Spring REST where each controller is a singleton and such variable must be defined in method signatures). We can leverage the method doInit of the server resource, as described below:
public class UserServerResource extends ServerResource {
private String userId;
private User user;
#Override
protected void doInit() throws ResourceException {
super.doInit();
userId = getAttribute("userId");
// for example
user = loadUser(userId);
// throws a status 404 if user can't be found
setExisting(user != null);
}
#Get
public User getUser() {
return user;
}
#Put
public User saveUser(User user) {
saveUser(user);
return user;
}
#Delete
public void deleteUser() {
deleteUser(user);
}
}
If you really want to use a mapping from request elements (like path variables, ...) to method parameters, you should use JAXRS. Restlet provides a support of this specification. Implementing a similar server resource as above but with JAXRS is described below:
#Path("/users/{userId}")
public class UserResource {
#GET
#Produces("text/xml")
public String getUser(#PathParam("userId") String userId) {
(...)
}
}
For more details, you can have a look at the corresponding documentation: http://restlet.com/technical-resources/restlet-framework/guide/2.2/extensions/jaxrs.
Hop it helps,
Thierry
I implemented Basic Auth for my services. Since ServiceStack's AuthFeature is strongly coupled with the session concept, I implemented a custom RequestFilter that performs stateless basic auth (credentials go in, on every request). Our auth strategy internally contemplates roles and permissions.
Besides authentication, we need to enforce authorization (e.g., user is manipulating a product that he owns). We are using FluentValidation, for all service validations.
Authorization validations include cross checking auth data with request parameters. Question is, where should I place the auth data produced in the BasicAuthRequestFilter? Should I key value pair it in cache, associating, for instance, RequestContext (or any other object that uniquely identifies the request scope) with an Authentication object?
I could just plug the AuthData in the Request Dto, which is available directly at the RequestFilter, however this would mess up our service contract design. We define dtos in a separate DLL, where only service input/output details are defined.
Any suggestions?
Thanks in advance.
I too use my own custom authentication mechanism and make custom role information available to my service. I do this by authenticating the request in a custom ServiceRunner which can then pass the information directly to my custom Service base. This ultimately means accessing information about the users permissions is exceptionally easy.
Create a custom ServiceRunner:
public class ServiceRunner<T> : ServiceStack.ServiceHost.ServiceRunner<T>
{
public ServiceRunner(IAppHost appHost, ActionContext actionContext) : base(appHost, actionContext)
{
}
public override object Execute(IRequestContext requestContext, object instance, T request)
{
// Check if the instance is of type AuthenticatedBase
var authenticatedBase = instance as AuthenticatedBase;
// If the request is not using the AuthenticatedBase, then allow it to run, as normal.
if(authenticatedBase == null)
return base.Execute(requestContext, instance, request);
/*
* Authentication required. Do you authorization check here.
* i.e.
* var authorization = requestContext.GetHeader("Authorization");
* bool authorised = ... some condition;
*/
/* You have access to your service base so if you injected the Db connection
* in you app config using IoC, then you can access the Db here.
* i.e.
* authenticatedBase.Db
*/
/*
* Not authorized?
* throw new UnauthorizedException();
*/
/*
* If authorized:
* Then simple set the details about their permissions
*/
authenticatedBase.AuthData = new AuthData { Id = 123, Roles = [], Username = "" };
// Pass back the authenticated base
return base.Execute(requestContext, authenticatedBase, request);
}
}
Configure you application to use it by adding this to your AppHost:
public override IServiceRunner<TRequest> CreateServiceRunner<TRequest>(ActionContext actionContext)
{
return new ServiceRunner<TRequest>(this, actionContext);
}
Create a custom class to hold your auth data i.e. the user session information, such as:
public class AuthData
{
public int Id { get; set; }
public string Username { get; set; }
public int[] Roles { get; set; }
...
}
Then create a custom service base
public class AuthenticatedBase : Service
{
public AuthData AuthData { get; set; }
}
To then use the AuthData in the service is simply a case of extending AuthenticatedBase.
public class CustomerHandler : AuthenticatedBase
{
public object Get(ListCustomers request)
{
// You can access the AuthData now in the handler
var roles = AuthData.Role; // Check they have the role required to list customers
...
}
}
You are probably wondering why go to all the trouble of using the ServiceRunner over a RequestFilter but the main advantage is it gives direct access to the instance of the Service base, which isn't available to a RequestFilter.
The RequestFilters are run before the Service base is instantiated, so you can't populate it from there. See order of operations for more information.
By having access to the ServiceBase we can populate values (in this case AuthData) and we have access to our injected dependancies such as the database connection.
I hope you find this useful. You should be able to copy most of your existing RequestFilter into the service runner. If you need any further help with this just let me know.
Update to support Attributes:
Since you are unable to avoid using the attribute method to handle your authentication needs you can still use this method:
Continue doing your authentication and access filtering the way you were before.
In your existing authentication mechanism use req.Items.Add to set the AuthData i.e. Where req is your request object
req.Items.Add("AuthData", new AuthData { Username = "", Roles = [] ... });
Then access your AuthData item in your service base:
public class AuthenticatedBase : Service
{
public AuthData AuthData
{
get { return base.Request.Items["AuthData"] as AuthData; }
}
}
My application calls a web service during the Authentication process (as shown in code below).
How can I save some information in HttpSession during this process?
This information like customer-account-number will be used in various other places in the application after the user is logged in.
Is it possible to pass HttpSession parameter to the MyServiceManager's static login method?
public class MyAuthenticationManager implements AuthenticationProvider {
#Override
public boolean supports(Class<? extends Object> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
#Override
public Authentication authenticate(Authentication authentication) {
//MyServiceManager.login - makes a call to web service
if(MyServiceManager.login(authentication.getName(), authentication.getCredentials().toString(), XXX_HTTP_SESSION_XXX))
{
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority> ();
authorities.add(new GrantedAuthorityImpl("ROLE_USER"));
authorities.add(new GrantedAuthorityImpl("ROLE_SUPERVISOR"));
return new UsernamePasswordAuthenticationToken(authentication.getName(), authentication.getCredentials(),authorities);
}
else
{
return null;
}
}
}
After breaking a lot of head on this issue, I was able to achive the objective using following work around.
Getting hold of session is really not feasible in following method
public Authentication authenticate(Authentication authentication)
I created a class
import java.security.Principal;
public class UserInfo implements Principal{
private String customerId;
private String accountNumber;
private String name;
}
The information which I wanted to store in session (like customerId, accountNumber etc), I saved it in userInfo object.
and this object was passed to UsernamePasswordAuthenticationToken
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.add(new GrantedAuthorityImpl("ROLE_USER"));
authorities.add(new GrantedAuthorityImpl("ROLE_SUPERVISOR"));
return new UsernamePasswordAuthenticationToken(**userInfo**, authentication.getCredentials(),authorities);
This information is readily available in the user's session using
(UserInfo)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
I home this is a good enough way to tackle the problem.
We can do this by:
ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
HttpSession session= attr.getRequest().getSession(false);
I recommend false as it is assumed that no one without valid session should be inside this method.