Last active
July 12, 2016 23:04
-
-
Save jolle-c/02b247b2468073608e17 to your computer and use it in GitHub Desktop.
Lasso 9 type to handle cookie based sessions
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[ | |
/**! | |
jc_session | |
Lasso 9 type to handle cookie based sessions | |
NOTE, as of this release the code is using json_encode/json_decode as a replacement for json_serialize/json_deserialize. If you are running this in an environment prior to Lasso 9.3 you will have to replace all instances of json_encode with json_serialize and json_decode with json_deserialize! | |
NOTE, if this is an updated version you'll need to add an additional field to the session table. Run the following sql for Mysql: | |
ALTER TABLE `jc_session` | |
ADD COLUMN `expire_value` INT (6) AFTER `user_data`; | |
Requires that Ke Carltons DS is installed | |
https://github.com/zeroloop/ds | |
Before first use create the needed table and set the proper values for | |
ds_sessionDB and jc_session_name | |
Create query for Mysql: | |
CREATE TABLE `jc_session` ( | |
`id` bigint(10) NOT NULL AUTO_INCREMENT, | |
`expire` int(10) DEFAULT NULL, | |
`session_name` varchar(100) DEFAULT NULL, | |
`cookie_id` char(36) DEFAULT NULL, | |
`fk_user_id` char(36) DEFAULT NULL, | |
`user_permission` text, | |
`user_data` text, | |
`expire_value` int(6) DEFAULT NULL, | |
`updated_datetime` datetime DEFAULT NULL, | |
`updated_ip` varchar(100) DEFAULT NULL, | |
`created_datetime` datetime DEFAULT NULL, | |
`created_ip` varchar(100) DEFAULT NULL, | |
PRIMARY KEY (`id`), | |
UNIQUE KEY `cookie_id` (`cookie_id`), | |
KEY `fk_user_id` (`fk_user_id`), | |
KEY `expire` (`expire`) | |
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT; | |
EXAMPLE USAGE | |
On wherever you handle user logins place this code after you have successfully verified that it is a legit user logging in. | |
jc_session -> addsession( | |
'user_id', // this should NOT be a hardcoded value, instead use the proper user id as established by the login process | |
map(// here you set the permissions this user is trusted with, one item in the map for each permission. Permissions can later be expanded or revoked if need be | |
'some_permission' = true, | |
'another_permission' = true | |
), | |
map(// here you set possible data you want to store in this users session. Could be for example the users name, email etc. Data can later be added to or removed if need be | |
'some_data' = 'Some Value', | |
'another_data' = 'Another Value' | |
), | |
30 // this is the expire value in minutes | |
) | |
To add some session data to the session | |
jc_session -> data('one_more_key', 'More Data') | |
Note that whatever session data you add must be compatible with the json format. Complex data types are not allowed | |
To remove a data item | |
jc_session -> removedata('the_key') | |
To add a permission | |
jc_session -> permission('permission_key', true) | |
To revoke a permission | |
jc_session -> permission('permission_key', false) | |
To retrieve some data stored in the session | |
jc_session -> data('a_key') | |
This will return the value stored for the key. If there's no matching key it will return void | |
To check for a specific permission | |
jc_session -> permission('permission_key') | |
Will return true or false | |
To check if the user has any of a set of permissions use a staticarray | |
jc_session -> permission((:'a_permission', 'another_permission')) | |
Will return true if one of the keys match a permission or else false | |
To expire a session | |
jc_session -> expire | |
To get an array of all available keys for data stored in a session | |
jc_session -> keys | |
To get the session status | |
jc_session -> status | |
Can return any of 'not initiated', 'new', 'load', 'expired', 'not found' | |
VERSION HISTORY | |
2015-07-11 JC Added a method to provide bulk permissions by supplying a map. | |
2015-12-27 JC Changed handling of no cookie id to not cause failure. Now returns false instead. | |
2015-11-01 JC Changed to json_encode/json_decode as a replacement for json_serialize/json_deserialize. | |
Added support for httponly and-secure in the cookie set | |
Fixed bug where renewing a session did not use the specified expire value and instead used the default value. NOTE this requires that the session table be updated | |
2014-10-20 JC Added missing data method | |
2014-10-17 JC First published version, based on an internally used type | |
*/ | |
/**! | |
ds_jc_sessionDB | |
Stores the DS database object used for session storage | |
*/ | |
define ds_jc_sessionDB => ds('Replace_with_DB_name') | |
/**! | |
ds_jc_session | |
Stores the DS table object used for session storage | |
*/ | |
define ds_jc_session() => ds_jc_sessionDB -> table(::jc_session) | |
/**! | |
jc_session_name | |
Stores the session name used for sessions | |
*/ | |
define jc_session_name => var(__jc_session_name) || $__jc_session_name := 'Replace with your preferred session name here' // you could also have a suitable session name stored in some site wide setting and fetch it here | |
/**! | |
jc_session | |
Method that acts as a wrapper for the actual session object. This is the method to call whenever interacting with a session | |
*/ | |
define jc_session => var(__jc_session) or $__jc_session := jc_sessionhandler -> init(jc_session_name) | |
// if you need to handle multiple sessions in the same thread, create multiple jc_session methods each with a unique method name and unique session name | |
/**! | |
jc_sessionhandler | |
The actual session type. Should never be called directly. Instead use jc_session | |
*/ | |
define jc_sessionhandler => type { | |
parent activerow | |
public ds => ds_jc_session | |
public created_column => 'created_datetime' | |
public modified_column => 'updated_datetime' | |
data | |
public sessionname::string, | |
public permissions::map = map, | |
public user_data::map = map, | |
public expiredatetime, | |
public expireval::integer = 20, | |
public cookie_id::string, | |
public sessionvalid::boolean = false, | |
public sessionstatus::string = 'not initiated' | |
public asstring => .sessionvalid | |
public status => .sessionstatus | |
public addsession( | |
user_id::string, | |
permissions::map = map, | |
user_data::map = map, | |
expireval::integer = .expireval | |
) => { | |
local( | |
expiredatetime = date -> asinteger + (#expireval * 60), | |
cookie_id = lasso_uniqueid | |
) | |
.expireval = #expireval | |
.expiredatetime = #expiredatetime | |
.cookie_id = #cookie_id | |
.permissions = #permissions | |
.user_data = #user_data | |
.updatedata(map( | |
'expire' = #expiredatetime, | |
'expire_value' = #expireval, | |
'session_name' = .sessionname, | |
'cookie_id' = #cookie_id, | |
'fk_user_id' = #user_id, | |
'user_permission' = json_encode(#permissions), | |
'user_data' = json_encode(#user_data), | |
'updated_datetime' = date, | |
'updated_ip' = string(client_ip), | |
'created_datetime' = date, | |
'created_ip' = string(client_ip), | |
)) | |
.ds -> keycolumn('id') | |
.create() | |
.sessionvalid = true | |
.sessionstatus = 'new' | |
web_response -> setcookie( | |
.sessionname = #cookie_id, | |
-domain = server_name, | |
-path = '/', | |
-httponly, | |
-secure = web_request -> isHttps | |
) | |
return self | |
} // addsession | |
public init( | |
name::string | |
) => { | |
local( | |
cookie_id = cookie(#name), | |
cutoff = date -> asinteger | |
) | |
.sessionname = #name | |
.row = .ds -> getrow('cookie_id' = #cookie_id) | |
if(.row and integer(.row -> find('expire') ) > #cutoff) => { | |
.expireval = integer(.row -> find('expire_value') or .expireval) | |
.expiredatetime = #cutoff + (.expireval * 60) | |
.permissions = json_decode(.row -> find('user_permission') or map) | |
.user_data = json_decode(.row -> find('user_data') or map) | |
.cookie_id = #cookie_id | |
.sessionvalid = true | |
.sessionstatus = 'load' | |
.row -> update('expire' = .expiredatetime, 'updated_datetime' = date) | |
else(.row) // session found but has expired | |
.permissions = map | |
.user_data = map | |
.cookie_id = string | |
.sessionvalid = false | |
.sessionstatus = 'expired' | |
else // no session found | |
.permissions = map | |
.user_data = map | |
.cookie_id = string | |
.sessionvalid = false | |
.sessionstatus = 'not found' | |
} | |
return self | |
} // init | |
public expire() => { | |
local( | |
cookie_id = cookie(.sessionname) | |
) | |
.expiredatetime = date -> asinteger | |
.row = .ds -> getrow('cookie_id' = #cookie_id) | |
if(.row and .row -> cols -> size) => { | |
.row -> update('expire' = .expiredatetime, 'updated_datetime' = date) | |
} | |
.permissions = map | |
.user_data = map | |
.cookie_id = string | |
.sessionvalid = false | |
.sessionstatus = 'expired' | |
} // expire | |
public removedata(param::string) => { | |
.user_data -> remove(#param) | |
.row = .ds -> getrow('cookie_id' = .cookie_id) | |
if(.row) => { | |
.row -> update( | |
'user_data' = json_encode(.user_data), | |
'updated_datetime' = date, | |
'updated_ip' = string(client_ip), | |
) | |
else | |
return false | |
} | |
} | |
public data(param::string) => .user_data -> find(#param) | |
public data(param::integer) => .user_data -> find(string(#param)) | |
public data(param::string, value::any) => { | |
if(not(.cookie_id)) => { | |
log_critical('No cookie id present for data') | |
return false | |
} | |
.user_data -> insert(#param = #value) | |
.row = .ds -> getrow('cookie_id' = .cookie_id) | |
if(.row) => { | |
.row -> update( | |
'user_data' = json_encode(.user_data), | |
'updated_datetime' = date, | |
'updated_ip' = string(client_ip), | |
) | |
else | |
return false | |
} | |
} | |
public keys => .user_data -> keys | |
public permission(param::string) => .permissions -> find(#param) or false | |
public permission(params::staticarray) => { | |
with param in #params do { | |
.permissions -> find(#param) ? return true | |
} | |
return false | |
} | |
public permission(param::string, value::boolean) => { | |
if(not(.cookie_id)) => { | |
log_critical('No cookie id present for permission') | |
return false | |
} | |
.permissions -> insert(#param = #value) | |
.row = .ds -> getrow('cookie_id' = .cookie_id) | |
if(.row) => { | |
.row -> update( | |
'user_permission' = json_encode(.permissions), | |
'updated_datetime' = date, | |
'updated_ip' = string(client_ip), | |
) | |
else | |
return false | |
} | |
} | |
public permission(params::map, update::boolean) => { | |
with param in #params -> eachpair do { | |
.permissions -> insert(#param) | |
} | |
if(#update) => { | |
if(not(.cookie_id)) => { | |
log_critical('No cookie id present for permission array add') | |
return false | |
} | |
.row = .ds -> getrow('cookie_id' = .cookie_id) | |
if(.row) => { | |
.row -> update( | |
'user_permission' = json_encode(.permissions), | |
'updated_datetime' = date, | |
'updated_ip' = string(client_ip), | |
) | |
else | |
return false | |
} | |
} | |
} | |
} | |
] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment