Spring Security ROLE_ prefix no longer needed? - spring-security

I was investigating on how to create custom role prefix until I realized that it doesn't matter. As long as my role from my db matches something like:
<security:intercept-url pattern="/person/myProfile/**" access= "hasRole('BlaBla')" />
And it is not example, in db I literally set up role BlaBla to test and it works.
I don't like when I get different behavior - many people had problem of setting up custom prefix to create custom role. What happens in here and should I expect hidden rocks?
I have 3.0.7 release. And in my query for authorities I don't have 'default' values...
Is it caused by version?

Probably you're using:
<http use-expressions="true">
that configures a WebExpressionVoter which will vote true for the users who have the granted authority "BlaBla" (in your case)
Remember that the Authorization for a secured object (an URL for instance) is performed by an AccessDecisionManager.
There are three concrete AccessDecisionManagers: affirmative, consensus and unanonimous.
For taking the decissions, they use a list of AccessDecissionVoters.
RoleVoter, the one that you expected, that has the rolePrefix configurable (ROLE_ by default), AuthenticatdVoter and the new WebExpressionVoter.
Don't forget that the combination of the AccessDecissionManager and its Voters could allow or deny the permission in a way that you'd think ilogical.
And I recommend you to debug the requests to see if the URL and the pattern matches as you expected.

Related

Rails 4 multitencancy with subdomain login management

Scenario: Multi-tenant rails app that uses subdomains and devise
Problem: I want the user to be able to log into mydomain.com then be forwarded to their own subdomain1.mydomain.com address as a logged-in user. Right now they can only log directly into their own subdomain.
I'm a relative Rails newbie and I can't find a simple solution (although it seems like there must be one). Ideally I would like to have mydomain.com and subdomain1.mydomain.com share one cookie, but my skills aren't there for writing custom middleware. Obviously since it's multitenant I can't share one session across all subdomains. Stuck on this for a few days and curious if there is a simple solution (such as a config.session_store domain setting) that I'm missing before I start looking at OAuth or other more cumbersome solutions. Any help will be appreciated!
Edit: Of course I only found this after posting. Log a user into their subdomain after registration with Rails and Devise . Will try the config.session_store domain: :all with a before filter recommendation and post any details if it doesn't work, seems like a good idea at least.
Edit: SOLUTION that worked for my particular Devise with subdomains setup:
class ApplicationController < ActionController::Base
before_action :check_subdomain
def check_subdomain
unless request.subdomain == "" or request.subdomain == session[:subdomain]
redirect_to request.protocol+request.domain
end
end
end
session_store.rb
My::Application.config.session_store :cookie_store, key: '_my_session' , :domain => :all, :tld_length => 2
Basically I set the subdomain in the session with session[:subdomain] at login and use that to scope the session to the current user. Otherwise when the domain is set to :all in session_store it breaks the scope. If the user is not authorized it redirects them to the public home page via the request.protocol (http:// or https://) +request.domain redirect. Simple! Now users can move between the base domain and their subdomain within the same session.
Cookie
From what you've posted, I'd estimate you have a problem with the tracking of your session cookie. We had a similar problem with our subdomain-powered application, which lead to the cookie being dropped each time you switched between the two
We found the remedy here:
Share session (cookies) between subdomains in Rails?
#config/initializers/session_store.rb
Your_App::Application.config.session_store :cookie_store, key: '_your_app_session', domain: :all, tld_length: 2
The trick is the tld_length argument - this allows you to define how many "levels" of the domain can be accommodated; IE if you're using a sub domain, you'll need to set the tld_length to reflect it
Forwarding
I'm not sure whether you have a problem with your forwarding or not; I'll give you some ideas anyway.
When you log into a "subdomain", unless you've got a true multi-tenancy implementation of Rails (where each user is stored in a different database), you should be able to allow the users to login on the main form, and then redirect them to the subdomain without an issue
Something you need to consider is the subdomain constraint will only be populated if you use _url path helpers:
<%= link_to "Your Name", path_url(subdomain: "subdomain_1") %>
The reason for this is the _path helper is relative to the base URL, and consequently cannot populate the subdomain option. Alternatively, the _url path helper points to the URL in its entirety -- allowing you to define the sub domain as required
--
If you send the request & continue to want the user to remain signed-in, you'll need to ensure you're able to persist the authentication across the sub-domains. IE if you have a single-sign in form on the "main" page, you'll want to ensure you can continue the authentication into the subdomains

DataSnap and Database Connection / Login

I am trying to work out the "right" way to establish a connection to my database from the server of my DataSnap application.
Each (most) of my tables in the database have fields (whose values are set via a trigger on insert and update) called Updated and Created (this is the current time stamp when the record is written) and updated_by and created_by which (should) contain the currently logged in user.
I want the user to 'login' from the client side such that these fields reflect the user who has logged in (and by extension, I'll get user authentication from the database, not just from the server). I can handle the authentication to the server itself from the client ok handling the OnUserAuthenticate and OnUserAuthorize events on the server. I am trying to then pass the credentials to my database so that the triggers can set the fields stated above correctly.
So what is the way to approach this scenario? I am wonder if the DSAuthProxyUser and DSAuthProxyPassword from the client can be used but I can't find much (any) documentation on how I would use that. Do I establish a new connection for every user who connects? This seems most logical to me. I'm not going to have a lot of concurrent users. Tops 30. Most likely 5-10. But what is the "normal" way this is done? I don't want to (hope I don't have to) pass in the username to each of my insert/updates to set the values in the tables.
I hope I have explained my situation clearly.
Thanks
I haven't used it yet, but it looks to me that the RDB$SET_CONTEXT() and RDB$GET_CONTEXT() introduced in Firebird 2 are what you need. Using these functions, you can set (and get) additional information specific to the user session (namespace USER_SESSION) or the current transaction (namespace USER_TRANSACTION). You can also retrieve additional system information for the current session (namespace SYSTEM), but that is probably not relevant for your case.
What you would need to do is call the RDB$SET_CONTEXT() method in that OnUserAuthorize event, eg using (as a query):
SELECT RDB$SET_CONTEXT('USER_SESSION', 'actualuser', '<name of user')
FROM RDB$DATABASE
Here 'actualuser' is the context variable we use. In your triggers you can then retrieve the name (assuming PSQL, with a declared variable actualuser)
actualuser = RDB$GET_CONTEXT('USER_SESSION', 'actualuser');
You can then use actualuser in the rest of your trigger. Just make sure you also account for the case where the context variable is not set (eg an administrator making direct changes to the database or something like that).
Firebird has the CURRENT_USER keyword which can be used in SQL.
The example below is based on http://www.firebirdsql.org/refdocs/langrefupd15-current_user.html
create trigger bi_customers for customers before insert as
begin
New.created_by = CURRENT_USER;
end
To have the user name for updates, simply declare a before update trigger like
create trigger bi_customers for customers before update as
begin
New.updated_by = CURRENT_USER;
end
This solution requires a 1:1 mapping of database users to external users. In the most simple implementation, it means that the DataSnap session authenticates the user credentials against the database.
In your description however, it seems to be a two step authentification (first against the DataSnap layer, then against the database). I am not sure how this can be done regarding safe password handling, and if you plan to have a separate user/password table only for the first authentication stage (DataSnap) and a user login mapping from DataSnap to database as some kind of 'decoupling'.

Grails Spring Security Custom UserDetailsService (goal of Email in place of Username)

I am using Grails Spring Security Plugin 1.2.7.3 and would like to have the User authenticate via Email Address and Password. I would like Email Address to be the primary Login ID.
I came across the following Nabble post regarding case insensitive usernames, where the original poster is attempting to do the same. However, I was a bit confused on a few points:
I understand that I need to write my own implementation of UserDetailsService and register it in grails-app/conf/spring/resources.groovy.
However, I wasn't sure which folder the custom implementation of UserDetailsService should go (best practices). My guesses are either /grails-app/services, /grails-app/utils, or /src/groovy. Has anyone done this before and where is the best place for the custom UserDetailsService?
I read this chapter in the Docs: http://grails-plugins.github.com/grails-spring-security-core/docs/manual/guide/11%20Custom%20UserDetailsService.html but I didn't really see guidance on what Grails folder to put it in.
I created my User domain class via s2 command:
s2-quickstart com.philiptenn.security User Role Requestmap
If I were to rename the field User.username to User.email so that my code is clearer, will I be in for a world of hurt?
I did a Find Usages on this field, and one stuck out in DefaultSecurityConfig.groovy: userLookup.usernamePropertyName = 'username'
Could I just update this to read:
DefaultSecurityConfig.groovy: userLookup.usernamePropertyName = 'email'
Thank you.
As you pointed out, you can override the default security configs in your own Config.groovy, the options all start with grails.plugins.springsecurity, so it would be grails.plugins.springsecurity.userLookup.usernamePropertyName = 'email' (docs). That's the first thing I'd try. If you check out the source of the GormUserDetailsService, the default userDetailsService, you'll see it uses that property for the lookup.

Thoughts regarding model ids in rails routes and validation

I am new to RoR and started working on a typical 'has_many' association (ie. a user has many friends). I have everything working correctly, but I don't like having the ids exposed in the url. I find that I need to add extra validation in my controller to make sure the ids represent valid associations in case the user manually entered different ids.
Personally I would like to see the ids out of the url and passed via some other means but that is not always possible. Shallow nesting of resources will help reduce the number of ids I need to validate at least.
What is the RoR philosophy on this? I have not seen anything specific to this issue.
Thanks
the URL has parameters if it is a GET url.
Try using POST parameters, which means your url will no longer be cluttered. Note that a malicious user can still send a made-up POST request using curl.
My approach to this is implementing proper authorization. If the user requests information for an object he is not permitted to read, this should be handled by an authorization framework.
With CanCan or Declarative Authorization you can define rules that replace your "manual" (and error-prone) checks in controllers.
I like the IDs being in the URL. That is what REST is about. Getting information for specific Resources, which have to be identified with an ID.
You can use Friendly ID in order to replace the integer ID by a slug (e.g. users/tollbooth instead of users/42).
basically ror routes by default takes id as key to generate urls. If you are not fan of id based urls then you can always override urls by using to_param inside model.
def to_param
# make sure this field is always present & unique
username
end
then by default you will start seeing username instead of id inside urls
How to find object inside controller actions
User.find_by_username(params[:id])
If you dont want to do this manually make use of slug gems like friendly id

sfGuardAuth across multiple apps

I've got 3 apps: Backend, Frontend and Members.
And I've got 2 credentials: Administrators and Members.
Administrators are imported by default but Members are created in the Backend.
When a Member is created, an event handler also inserts this Member as a sf_guard_user and of course, the proper relations in sf_guard_user_group and sf_guard_user_permission.
That's the boring part, now the fun:
Frontend is not secured, but Members is and using these credentials: [administrator, member].
According to all this, Members created in the Backend that also get inserted (correctly as far as I can tell) should be able to login to the Members secured app, since they get the member group/permission/credential.
This is not happenning, the only ones that can login to the Members app are the administrators, which is not wrong, but either is the fact that correctly created Member users can't login to it.
The error thrown by the guard is the classic: The username and/or password is invalid.
Now that I edit the error, the salt comes to mind: How would one emulate the inserting of the salt as the guard does it? Maybe that's what I'm not inserting correctly (or inserting at all) and thus the password invalid error (and in fact everythine else I've described is ok! omg)
And that's my problem.
Thanks in advance :)
[administrator, member] means both are required, I believe.
I think you want [[administrator, member]] for the credential requirement.
Also, yes, you will want to make sure you use a salt, and set the algo.
parent::_set('password', call_user_func_array($algorithm, array($salt.$password)));
Salt before password, as well.

Resources