Close

Quick Note on Users and Security

A project log for Project Stark Framework

This is a YET ANOTHER attempt at an "Iron Man" Jarvis-like system

robweberrobweber 07/20/2016 at 15:500 Comments

The very first versions of the framework made no distinction about users or actions security. It was designed as a personal project to extend some accessible frameworks and provide basic automation. As things grew the need for creating individual users, and a security context, became more apparent. Family members wanted to use parts of the functionality; with the integration of the Icinga Monitoring system co-workers wanted to use parts of the functionality. Pretty soon I needed a way to make sure I could segment what users were allowed to use which functions.

There are 2 main security areas. The first is at the User level, the second is at a Group level. The StarkUser object is how a user accesses the system. Different modules can extend the user object by adding additional meta-data keys that can be tied to user. By default things like the username, password, phone, and email are given. In the screenshot below you can see that there is the addition of a field for a GitLab user token from the GitLab module, the Minecraft username from the Minecraft module, and a Pushover token from the Pushover Module. Any OAuth tokens for other apps (like SmartThings) are also stored per StarkUser. There is one very simple flag that serves as an override to the entire system - the "Is Admin" boolean value. If this is checked the user is given Administrator rights to the system and any other security group setting is ignored.

Once a user is set up they can authenticate to the framework with their username and password. Each user can set up their own monitors, triggers, and jobs. When using a client the authentication system returns an "application key" that is tied to the user. After their initial authentication this key is used for all interactions with the system. For simple clients, like the Bash client script, the application key is just a value inserted into the top of the script.

Unless they are an administrator, a basic user really can't do a whole lot. Any additional functionality is added via the Security Groups system. By default every user is placed in the "Everyone" group with a very basic set of permissions. Additional groups can be made, and they are explicitly given permission to perform various functions within the framework. These functions are pulled from each module's self-reported list of methods. Users can belong to more than one security group, allowing for overlap of permission sets.

If a user is allowed to create new Jobs on the system, they are creating jobs that are only visible to their user account. These jobs can be shared with other security groups on the Job screen. For example, I created a set of jobs adding some integration with the Icinga Monitoring system that we use where I work. I've shared these tools with some co-workers so we can all easily schedule downtime or acknowledge issues via our IM client. The co-workers can't do anything else on the system, but I've shared these jobs with them.

The flow of operations during any method call checks a few things. The first is if the user authentication (application key) is valid. If it is the Job is found and a check is done to see if this Job is accessible to the user. If it is the job is created based on the input. Since each job may contain multiple Methods (actions), as it is executed each action is checked to see if the user is allowed to run this method. There are some built in security functions so the check is quickly done across all security groups with something like:

StarkMethod foundMethod =  m_modules.findMethod(job.getNext())

if(foundMethod != null)
{
    if(job.getOrigin().getUser() != null && m_security.hasPermission(job.getOrigin().getUser(),foundMethod))
    {
         //run the command here
    }
    else
    {
        //abort job, return message to user
    }
}
else
{  
   //method no longer exists (module disabled?)
   //abort and return no-method error to user
}

These two security objects are very simple to use, but provide some needed segmentation to the framework. Individual users can be locked to specific functionality, which is easily modified through a group system.

The only thing that I'm not very happy about is how the Clients are currently registered in the framework for event monitoring purposes. Right now a client registers itself system-wide, rather than per-user. This is just a hold over from the original one user days of the framework and has never been corrected. What ends up happening is that events may be dispatched to clients where users have since changed but the framework doesn't know that. A work around is to have the clients themselves filter based on login, but that doesn't scale very well. I'm currently working to migrate client registrations to happen on a per-user basis, and make the event dispatching smart enough to only dispatch events to clients logged in as particular users. Once this is done the user security portion of the framework will truly extend throughout the system.

Discussions