Hi Guys I was looking through some example FW1 we...
# fw1
c
Hi Guys I was looking through some example FW1 websites: https://github.com/framework-one/fw1/blob/develop/examples/userManagerAccessControl/controllers/security.cfc My question is, are we allowed to use the
session
scope inside the controller, if the controller is a singleton?
Looking at the example:
Copy code
component {

    function init( fw ) {
        variables.fw = fw;
    }

    function session( rc ) {
        // set up the user's session
        session.auth = {};
        session.auth.isLoggedIn = false;
        session.auth.fullname = 'Guest';
    }

    function authorize( rc ) {
        // check to make sure the user is logged on
        if ( not ( structKeyExists( session, "auth" ) && session.auth.isLoggedIn ) && 
             !listfindnocase( 'login', variables.fw.getSection() ) && 
             !listfindnocase( 'main.error', variables.fw.getFullyQualifiedAction() ) ) {
            variables.fw.redirect('login');
        }
    }

}
It would seem we can? But wouldn't this increase the risk of user data, getting mixed up?
a
No - the session is tied to the user. However, it is generally considered best practice to not reference things like the
session
scope in your controllers. You can easily create a service which hides how that information is stored from the controller.
c
So, a singleton means that only one instance of each controller is created, per application?
a
correct
c
So, in the code above, the user session object must be changing all the time, inside the
security.cfc
controller? Isn't this dangerous? I didn't write this code, by the way. This is an FW1 example website.
If it was a transient then each user request would get their own encapsulated
security.cfc
instance? And there would be no issue?
a
no. If you think of the
session
as
MySessionServiceSingleton
then the when you do
session.auth.isLoggedIn
it's a bit like
MySessionServiceSingleton.getAuthIsLoggedIn()
session
scope is not like the
variables
scope
c
But surely, we might get a race condition? When two different users are trying to access
session.auth.isLoggedIn
, inside the same
cfc
instance?
For starters, there is no read lock on that code?
a
no - as the
session
is tied to each user (created in onSessionStart)
This is nothing to do with
fw1
btw - it's just how the session scope works in CFML
You do not need a read lock - you are literally just referencing a stored value - why do you need to lock it?
c
Yes. I get that...
Like:
Copy code
var lckisloggedin = false;

lock timeout="10" scope="session" type="read" { 
    lckisloggedin = structKeyExists( session, "auth" ) ? session.auth.isLoggedIn : false; 
}

if ( lckisloggedin ) && 
    !listfindnocase( 'login', variables.fw.getSection() ) && 
    !listfindnocase( 'main.error', variables.fw.getFullyQualifiedAction() ) ) {
    variables.fw.redirect('login');
}
a
Yes, I know what you meant, but you don't need the lock
c
Technically, you should lock the session scope to prevent a race condition?
a
if you want to add it though - go ahead - no harm
πŸ‘ 1
It is worth doing a lock when you write as in theory you could have two request from the same person hit at exactly the same time. For the read i it doesn't matter if one or more read from it at the same time as nothing is mutating the value you are reading
c
Nothing is mutated, but you might read the wrong user object, if we are within a singleton?
a
It really matters when you are changing as session value based on it's previous value (for example incrementing a value)
πŸ‘ 1
I'm not sure how to convince you but you are referencing a session scope inside a singleton, the session scope is not part of the singleton's own 'instance data'
c
OK. I think I see what you mean
So, why do they say it is bad practice to use the session scope inside components?
OK. So , using the variables scope inside a singleton is no go, unless it is a constant value, like a DSN, but the session scope is external to the component and so is not influenced by changes inside the component?
πŸ‘ 1
a
Well, it's more about encapsulation. So imagine you write your app and it all works beautifully. Now you decide that you want to change the way security works so that it's using Redis or something. You now need to find every reference to
session
scope and change it. If you'd started off with a
SecurityService
then the
session
scope is only referenced in that one place so you can switch out how you track who is logged in by changing very little code as your controllers don't care how it is stored.
πŸ‘ 1
c
I think I get it now...
Yes. Nice explanation. Thanks πŸ™‚
Thank god for that. I have a few session variables inside my controllers and I was wondering whether to remove them.
😁 1
a
I'd treat the FW1 examples as examples of how FW1 works, rather than this is a perfectly architected app using FW1
c
I guess injecting the service into the controller is OK, even though it is in the variables scope, because the service is a singleton and should contain pure functions [UDF]
a
correct
πŸ‘ 1
c
Hoooooray. I have got it now. Its good to clarify this stuff from time to time πŸ™‚
I guess, if you had variables, in the variables scope, in a
singleton
that everyone could read and write to, it would turn into a veritable variables bun fight 😊
a
yes - that would be a race condition. Which is why
var
scoping is crucial
πŸ‘ 1
c
Or the implied method
local
scope. But I actually tend to write at the top of each method:
Copy code
var local = {};
Because this is compatible with ACF9, onwards...
a
Oh wow - ACF 9!
🀣 1
Personally I hate that
local
prefix but I understand people like it as they can clearly see what is scoped.
If you can't see where a variable is
var
scoped then your code is way too long πŸ™‚
πŸ‘ 1
c
I just use the
local
prefix, for ease. Sometimes I also write
var
scoped variables at the top of each method, if these variables are extra important, and I want to emphasize them.
I know you can now write the
var
scope anywhere, in a method. But I am pretty sure this use to throw an error in ACF10 <
I still write all my
var
scoped variables, at the top of my method. I guess this is just habit?
a
c
OK. Maybe, I got this totally wrong πŸ˜‘
I’ll just say that it was in ACF9 and then you can’t test it πŸ˜…
😁 1
Anyway, it’s good to have a fellow FW1 Developer to talk to, who knows their stuff. πŸ™
a
I'm actually porting to ColdBox but have used FW1 for years and do like it