Shiro custom authentication logic - java

I have the following requirements for authenticating a user with Shiro:
Username and password must match with that stored in the database. If the username and password do not match then an error message should be displayed indicating authentication failure.
Account must be active - a user activates their account via an activation email. If the user's account is not active then an error message should be displayed indicating that they have not clicked the activation email.
Account must not be expired - user accounts have an expiry date. If the user's account is expired then an error message should be displayed indicating account expiry.
Note: there are two databases for storing user information. One of them stores authentication information (username, and password) and the other database stores information like when the account expires.
I can easily accomplish the first requirement simply by configuring a JDBC realm in shiro.ini.
I'm guessing some custom Java logic needs to be implemented to accomplish requirements 2 and 3. Any hints about how to implement the above? Would I need to implement a custom realm?

One way to do this is indeed creating a custom realm.
We had some custom demands aon authentication as well. We implemented this by creating our own custom realm implementation. We extended AuthorizingRealm and overridden the doGetAuthenticationInfo method to check if a user can be checked for logging in. You can put your cases 2 & 3 there.
If you only use shiro in a web environment, you might consider overriding the standation authc filter and override the isAccessAllowed method we you can implement some custom redirection if the user is not yet activated or expired.

Related

How to allow unauthorized request for the first time and then require authorization?

So, we are implementing this special kind of authorization, where after logging in, user is presented with basic dashboard. When trying to get to another location, he is asked to authorize with password.
The thing is that in order to present the basic dashboard, some requests are sent and need to come with 200 response, while the rest just returns error message and redirects to authorization screen.
To summarize, we're gonna have 3 kinds of endpoints:
- blocked, until user authorizes
- allowed to return proper data for the first time while each consecutive request will require authorization
- no authorization required
I cannot find a way to overcome the 2nd type. Is there a way to record the number of requests sent per specific endpoint? Or is there any way to actually allow first unauthorized use and then required authorization?
One way would be to authenticate user without any role the first time that users access to yours controllers.
Then when he try again to access you could sent them to login process if user is logged without role.
To securize this methods could use the annotation below:
#Secure("IsAuthenticated()==false || (IsAuthenticated() && hasRole("WRITE"))")
To securize manually a user should use the SecurityContextHolder class

Spring Security returning the same token each time

We're using spring security (Authorisation and Resource server ) in our project.
client sends a token request (/oauth/token) with the oauth2 parameters.
spring security app creates a token for the user and respond to the client with the access_token, refresh_token, custom user object (name, organisation, email etc) and authorities (Roles).
Client adds additional roles (say ROLE_CLIENT, ROLE_USER).
spring application will store the above roles for the given user.
Next time when client sends a token request, spring security returns the previously created token (not expired yet) along with the user and authority information. This authority information is not having the latest roles (added in step4).
Here spring security always using the existing token (as it is not expired) and returning the valid token. Is this the expected behaviour even though the user object is being modified?
It sounds like you need to revoke the access token when the users roles change if you want the next request to get a new access token with the new roles and not return an existing token with existing roles if it's still valid.
At the point where you update the users roles you'd likely want to revoke the token.
I haven't personally tested this but I found a guide for it here https://www.baeldung.com/spring-security-oauth-revoke-tokens so your milage may vary.
I want to add that this does not sound like the normal OAuth2 process and you may be breaking a few conventions here which might bite you later. That said, you don't have to follow a standard if you're confident in your proposed solution.
Edit: To clarify the users roles and access is normally part of a resource and not part of the token exchange. For example you have a normal OAuth2 request which generates a token which you can exchange for an access token, as you've laid out in steps 1 and 2. Then you'd normally take that access token and request user access information from a resource such as "userinfo" service or something similar.
Your security service can also be a resource server but the two steps should be seen as different. Then when you want to modify the users roles you do this again through a resource. This means the next time you invoke the resource it'll have the up to date information without needing to authenticate the user again.

wso2 IS 520 user accountDisabled claim verify as 'admin'

Is there a way to verify a multi-tenant environment USER account is enabled or disabled using WS ?
getUserClaimValue
IS the only one I could see !, unfortunately it asks for User credentials !
Cant we do it at admin level ?
Its not even storing into ldap-attributes. How can I get this verified as super-admin.
Claim URI :
http://wso2.org/claims/identity/accountDisabled
Worked out to get the claim as 'ref'.
How to get the value from this 'ref' direct using LDAP with JAVA ?
Any claims which has the pattern http://wso2.org/claims/identity/XXXXX is considered as a special claim. Hence they are ignored by getUserClaimValue (and by setUserClaimValue when setting value).
You'll need to use either getUserClaimValues or getUserClaimValuesForClaims for the above purpose (And setUserClaimValues to set).
Update
Due to the tenant separation model it is not allowed to get claim details by other tenant admins (Even for super tenant admin). In case you really need that, one possible option would be to write a custom admin service extending the org.wso2.carbon.um.ws.service.UserStoreManagerService class (which reflects RemoteUserStoreManagerService) where it will start a tenant flow for the user's tenant, and call super class method to get the claim value.

EJB authentication

Here is what I'm trying to do: I have an authentication EJB that can grant user a "ticket" (pair of key and token) by validating username and password. Clients should pass the ticket every call to remote EJB and such ticket should be able to be fetched by EJB (method, or interceptor) and EJB/ or interceptor should validate the ticket and determine whether the call is valid or not.
I don't want to add ticket as parameter to each function that requires authentication, and I believe there should be a way to implement this. I've looked through several articles about JAAS custom login module, but still can't find a non-vendor specific way to do this. (And I am trying to avoid vendor specific implementation)
If JAAS can't do the job, is there anything like "header" in EJB?
Couldn't you just use roles to perform this authorization? Instead of granting a ticket to a user, assign him a role.
Then, it's just a matter of using the inbuilt functionality of checking whether a user is in a role or not.
Although I agree with #EdH, i.e. use #RunAs, you can accomplish what you want by adding to the EJB context your token.
Red Hat does something similar here by using the interface EJBClientInterceptor.
By looking the code of ReceiverInterceptor (which implements the EJBClientInterceptor) you can see how to modify the EJB context (on the client-side) and add your token:
context.setReceiverInvocationContext(new
EJBReceiverInvocationContext(context, receiverContext));
context.sendRequest();

Allow only one session per user

We have a web-application developed using struts2, spring & hibernate.
The application needs a functionality that one user can login from only one browser.
Say if user x, is logged in on pc-1 browser ff, then he cannot be logged in from any other place.
I tried it by implemention session map and store the sessions in global map, but this fails when user logs off and tries to login again.
Even it fails critically if the user does not logs off and session time-outs, but the map is not cleared.
Any better idea to implement this functionality.
We do not want to obstruct the user to login but do not want users to exploit the application by allowing him to share the creditionals and allow multiple users with same login to happen.
Since you are already using Spring, I would recommend you to integrate your application with Spring Security.
Spring security lets you define maximum sessions allowed per user concurrently.
<session-management>
<concurrency-control max-sessions="1" />
</session-management>
If set when user having valid session tries to login again it will inform user that maximum concurrent access is set to 1.
Read more at the reference documentation of Spring Security: v3.2.x, v4.2.x or v5.1.x.
If spring security is not an option for you then:
Use a SessionInterceptor which will check for session validity, if session is valid it will check if user is already logged in to the application (for this you will have to maintain session somewhere for eg database for every successful login), if valid login is found, redirect user again to login page with custom message, or logout already valid session and then redirect him to login again. If you logout earlier session it would mean any successive action in that browser session will have to deal with invalid session.
If case you are also using Servlet in your application then Interceptor wont work for you, in this case you should use a Filter and follow the same steps as detailed above for Interceptor.
The best solution is to log-off user from other session when he logs in in new session. It is often that user would not logoff when closing browser and restricting him from logging in other window would be the pitfall.
Automaticly closing any previous user sessions is good, because in normal usage, it is no problem, but when sharing login and password, no two persons can work simultanously with your application.
At the login give the user a generated ID/cookie (sessionid suffices) stored with the user data. If a user does a request to the server with an old ID/cookie, say that he logged in elsewhere.
The other way round, forbidding the new login attempt, has its drawbacks - as you've experienced.
Create a map.
At the time of logging check that user id is present into that map or not.
If its not exist then put user id into map, at the time of logout remove that user id.
To be honest I would revisit the reasons why you have to restrict a user to a single login. Whilst preventing them from logging in from two different browsers is easy enough - any of the suggestions provided would work - with the Spring Security option being the easiest to implement if you can - they all break down when your user opens a second tab in the same browser. That is considered to be part of the same session.
Maintain user stack in servlet context,as it will be one for web container.perform a check before user getting logged in, if user name found in servlet context redirect him to login page.
All you should do is add a field in database userprofile table saying: alreadyLogin.
If user logins, make it Y. If user logs out, make it N. Now every time when user tries to login from new location, check this value and prevent login if Value is Y.
As many said, you can have a Map<String, User> (static Map or better an attribute in ServletContext) of (sessionId, user) of active users.
When a user tries to login, first check the existence in theMap.values(), and if it is okay add it to theMap.
Instead of removing from theMap on logout, implement a javax.servlet.http.HttpSessionListener, and on sessionDestroyed method, remove the item from it (the parameter of the method gives you the sessionId). This way if a user closes the browser, after session timeout period, it will be removed automatically.
On logout, invalidate the session, so it will be destroyed, and again this listener get executed.
Don't forget adding the listener to your web.xml.

Categories

Resources