Skip to content

Instantly share code, notes, and snippets.

@jolle-c
Last active July 12, 2016 23:04
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jolle-c/02b247b2468073608e17 to your computer and use it in GitHub Desktop.
Save jolle-c/02b247b2468073608e17 to your computer and use it in GitHub Desktop.
Lasso 9 type to handle cookie based sessions
[
/**!
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