I am working with Liberty Create - Citizen Hub and am struggling to get privileges to play nicely. The user privileges set up on central, do not sync to the other hosts!! Which baffles me.
So I am investigating using Code Studio Access Check, However I cant find any documentation or examples of using this element.
I would like to perform a lookup of the logged in user and check if have been assigned to a specific group to provide access.
Does anyone know how you would go about doing this? Have any code that lookups current user etc.
Code studio access check’s work on the premise of a simple return of true or false depending if you want to grant access. Bear in mind that this code would need to determine for any and all user objects/roles etc. else you pose the risk of blocking out the wrong people.
Obtaining the current user to look up against would look like this -
//Get current logged in user and convert to record
let user_id = cs.get_session_user_id()
let user = cs.record(user_id)
You might want to decide based on a user object, in which case you would consider the object they are in then compare that against references for your user objects -
// Get the user's object to distinguish build vs client user
let user_object = user.get_object_id()
Your evaluations for privileges will work in the same way, getting user roles, attributes, relationships to the base record etc. all in the same way you would achieve this in any other code studio function. At the end of each evaluation where appropriate you can then return your outcome -
I got this working in build environment, but unsure if this is the right way to code it.
I am posting my solution in case anyone else wants to do something similar, and to hopefully get some feedback around best practises, alterations that should be made for use in production server.
/**
* Function Type: Access Check
* Name: Has Councillor Access
* Checks if user can access given navigation element, returns true of false
*
* Checks is user is in a specific Group - in this case 'Councillor (Access Check)'
* Uses the cs.get_session_user_id() function to get the local host user_id
*
* We check 'obj_group_user_mapping' data object, any records returned we have a match
*/
return {
/**
* @param {Object} params - component_id, component (the navigation element), context_record_id
* @returns {bool} true/false
*/
main: function (params) {
var debug = true; // Set debug mode to true for logging
// [1] Initialize variables
const group_name = 'Councillor (Access Check)'; // Name of the group to check
var user_id = cs.get_session_user_id(); // Get the local host's User ID
// [2] Search for group mappings using the group_name and user_id
debug ? cs.log('*** Searching Group User Mappings... using group_name of [' + group_name + '] and user_id [' + user_id + '] ***') : null;
var obj_group_user_mapping = cs.search({
'base_object_id': cs.ref('obj_group_user_mapping'), // Reference to the group user mapping object
'selects': [':id', 'field_group_map_group_name', 'field_group_map_userid'], // Fields to select
'filters': [ // Filters
{ 'field_path': cs.ref('field_group_map_group_name'), 'value': group_name, 'comparator': 'equal_to' }, // - by group name
{ 'field_path': cs.ref('field_group_map_userid'), 'value': user_id, 'comparator': 'equal_to' }, // - by user ID
],
'return': 'data' // Specify that we want to return data
});
debug ? cs.log('++ obj_group_user_mapping: ' + JSON.stringify(obj_group_user_mapping)) : null; // Log the results of the search for debugging purposes
// [3] Check if there are any group user mappings found
if (obj_group_user_mapping.length) {
debug ? cs.log('Group mapping FOUND for [' + group_name + '] for user_id [' + user_id + ']') : null;
return true; // Return true if mappings are found
} else {
debug ? cs.log('Group mapping NOT FOUND for [' + group_name + '] for user_id [' + user_id + ']') : null;
return false; // Return false if no mappings are found
}
}
}
Not sure what I was doing wrong, but I couldn’t get user or user_object objects to return anything useful, what am I missing?
let user = cs.record(user_id)
You might want to decide based on a user object, in which case you would consider the object they are in then compare that against references for your user objects -
// Get the user's object to distinguish build vs client user
let user_object = user.get_object_id()
user in this instance would be a record, therefore you could use anything in the record context. For example, you may have a subset that says the user is in the group ‘Councillor (Access Check)’. Here could you use something simple such as:
You could also make other requests such as user.get_related(cs.ref(‘some_relationship_path’)); to get an object of records over a relationship path. This particular call gives the ability to add additional filters so you could get related mappings where mapping is related to x group as an example. The below demonstrates this where I am fetching related reservation records related to the user where the reservation dates are in a specified window.
let filters = [];
filters.push({'field_path': cs.ref("Reservation_Start_Date_FP"), 'comparator':'greater_or_equal_to', 'value': start_date});
filters.push({'field_path': cs.ref("Reservation_End_Date_FP"), 'comparator':'less_or_equal_to', 'value': end_date});
let overlap_reservations = logged_in_user.get_related(cs.ref("User_Reservations_Rel"), filters);
The get_object_id() call would return a CUID of the object the logged in user belongs. This would be helpful in an application where you may have many user objects e.g. build users, council users and resident users. You may have a reference for a specific object, e.g. residents, in which you want to always deny access to or build users who you always want unconditional access to.
A couple of comments on your code above.
Firstly, I would avoid hard coding the group name if at all possible, especially if it is configurable in the front end as it seems to suggest. This is a scenario where it is easily sleep walked into a trap in which someone updates the group name record not knowing it is referenced in logic elsewhere like this. This quickly renders the access check non-functional and always returning false. Special records in this instance is better to be used.
Secondly, using the get_related() call as above could replace your cs.search and potentially save you a couple of references.
Lastly, as a general rule of practice, it is always better to use let over var. This serves less potential scoping issues with variables (less problematic in a small block of code like this) and var is generally considered deprecated at this point unless you are writing front end code where you must support an older browser.
The subset reference is excellent, this has been most educational for me.
It’s working and simpler, cleaner code (+1 subset, +1 cs ref, +below)
return {
main: function (params) {
// Get current logged in user and convert to record
let user_id = cs.get_session_user_id();
let user = cs.record(user_id)
// Get the user's object to distinguish build vs client user
let user_object = user.get_object_id()
// Check if user is in the subset
if(user.in_subset(cs.ref('user_in_councillor_group_subset'))){
return true;
} else {
return false;
}
}
}