17. Security¶
There are two levels of security definitions: authentication and authorisation.
17.1. Authentication¶
Authentication is implemented within the core code brain. ask_question is called for each user inquiry. One of its parameters is ‘clientid’, which is an ID for each user making utterance. It is recommended that you perform the necessary authentication procedures separately. In the following example, the ‘clientid’ is set to ‘console’ for the console client, but if multiple people are accessing the site, such as by REST, it is necessary to assign a unique ID.
def ask_question(self, bot, clientid, sentence) -> str:
if self.authentication is not None:
if self.authentication.authenticate(clientid) is False:
logging.error("[%s] failed authentication!")
return self.authentication.configuration.denied_srai
The authentication service is defined by a dynamically loaded class, and its base class is defined as follows。。
class Authenticator(object):
def __init__(self, configuration: BrainSecurityConfiguration):
self._configuration = configuration
@property
def configuration(self):
return self._configuration
def get_default_denied_srai(self):
return self.configuration.denied_srai
def authenticate(self, clientid: str):
return False
This implementation is a simple one that matches IDs in the ‘authorised’ list. For more advanced authentication, an external service can be implemented, but it is recommended that additional authentication procedures be performed when using this program on the server.
class ClientIdAuthenticationService(Authenticator):
def __init__(self, configuration: BrainSecurityConfiguration):
Authenticator.__init__(self, configuration)
self.authorised = [
"console"
]
# Its at this point that we would call a user auth service, and if that passes
# return True, appending the user to the known authorised list of user
# This is a very naive approach, and does not cater for users that log out, invalidate
# their credentials, or have a TTL on their credentials
# #Exercise for the reader......
def _auth_clientid(self, clientid):
authorised = False # call user_auth_service()
if authorised is True:
self.authorised.append(clientid)
return authorised
def authenticate(self, clientid: str):
try:
if clientid in self.authorised:
return True
else:
if self._auth_clientid(clientid) is True:
return True
return False
except Exception as excep:
logging.error(str(excep))
return False
To use the above features, the following items must be enabled in the Security section of config.yaml.
brain:
security:
authentication:
classname: programy.security.authenticate.clientidauth.ClientIdAuthenticationService
denied_srai: AUTHENTICATION_FAILED
Parameter Name | Description |
---|---|
classname | Defines the path to python which implements the base class ‘Authenticator’. |
denied_srai | If authentication fails, the interpreter can use the document defined in this configuration as a SRAI. (In the above example, ‘AUTHENTICATION_FAILED’ is set to SRAI.). The AIML file must contain this as a category pattern with appropriate text to indicate that access is denied. |
17.2. Authorisation¶
Authorisation is defined by users, groups, and roles.
Parameter Name | Description |
---|---|
User | Define authorisation information for a single user. By including users in one or more groups, you can assign both specific and inherited roles. |
Group | A group of users assigned one or more roles. |
Role | An arbitrary authority string to be assigned to the user group. |
The base authorisation class is defined as follows。
class Authoriser(object):
def __init__(self, configuration: BrainSecurityConfiguration):
self._configuration = configuration
@property
def configuration(self):
return self._configuration
def get_default_denied_srai(self):
return self.configuration.denied_srai
def authorise(self, userid, role):
return False
The implementation of this base class for user, group, and role-based authorisation is as follows。
class BasicUserGroupAuthorisationService(Authoriser):
def __init__(self, config: BrainSecurityConfiguration):
Authoriser.__init__(self, config)
self.load_users_and_groups()
def load_users_and_groups(self):
self._users = {}
self._groups = {}
if self.configuration.usergroups is not None:
loader = UserGroupLoader()
self._users, self._groups = loader.load_users_and_groups_from_file(self.configuration.usergroups)
else:
logging.warning("No user groups defined, authorisation tag will not work!")
def authorise(self, clientid, role):
if clientid not in self._users:
raise AuthorisationException("User [%s] unknown to system!"%clientid)
if clientid in self._users:
user = self._users[clientid]
return user.has_role(role)
else:
return False
To use the above features, the following items must be enabled in the Security section of config.yaml.
security:
authorisation:
classname: programy.security.authorise.usergroupsauthorisor.BasicUserGroupAuthorisationService
denied_srai: AUTHORISATION_FAILED
usergroups: ../storage/security/roles.yaml
Parameter Name | Description |
---|---|
classname | Define the path of python that implements the base class ‘Authenticator’. |
denied_srai | If the authentication fails, the interpreter can use the text defined in the configuration as the SRAI. (In the above example, ‘AUTHORISATION_FAILED’ is set to SRAI.) The AIML file should be included as a category pattern with appropriate text to indicate that access is denied. |
usergroups | Specify users, user groups, and role configuration files. |
The format of the role file is as follows.
users:
console:
roles:
user
groups:
sysadmin
groups:
sysadmin:
roles:
root, admin, system
groups:
user
user:
roles:
ask
When using the AIML authorisation method, enclose the template in the ‘authorise’ tag. If the input is ‘’ALLOW ACCESS’’ and the user does not have the ‘root’ privilege associated with them, then SRAI is set to what is defined in denied_srai.
<category>
<pattern>ALLOW ACCESS</pattern>
<template>
<authorise role="root">
Access Allowed
</authorise>
</template>
</category>