Permissions
This Package provides different scenarios and features for testing authentification and permission requiring resources.
Note
This section covers the raw implementation of authentification and permission access tests. If your application-under-test is a web application, have a look into the contrib section of the balderhub-http and balderhub-webdriver project. These packages already provide a ready-to-use implementation for validating your web endpoints.
If you already use an implementation of the
balderhub.http.lib.scenario_features.client.WebSessionFeature feature (from
balderhub-http Package)
or the balderhub.webdriver.lib.scenario_features.WebdriverControlFeature (from
balderhub-webdriver Package, that is implemented by
balderhub.selenium.lib.scenario_features.SeleniumFeature) you normally do not have to define more
than the urls to test. The BalderHub packages will care about everything else.
More information are available within the contrib section of the balderhub-http package documentation or the contrib section of the balderhub-webdriver package documentation.
Permission Tests Introduction
Main Configuration Features
There are three main features that allows to define the access rights of your system:
Device Type |
Feature |
Description |
|---|---|---|
Server |
Provides all |
|
Server |
Provides all |
|
Client |
|
Provides all |
Client |
|
Autonomous feature specifying that the device is an unauth device |
Within these features a list of resouce roules need to be configured, that describes the expected behaviour.
You can use the normal ResourceRule, but you can also use your custom implementation
of the UnresolvedResource, for defining unresolved resources, mostly used for
defining specific data-item related endpoints. The latter also allows you to check individual object permissions, f.e.
for data-item related tests.
Defining Resources and Rules
If you have different user roles, you can add them by creating multiple client devices connected to the server instance.
For example, a setup for testing permissions could look like shown below:
import balder
class SetupExample(balder.Setup):
class Server(balder.Device):
exists = MyExistenceForConfig()
auth_for = MyAuthenticationForConfig()
@balder.connect(Server, over_connection=balder.Connection)
class AdminUser(balder.Device):
perm = AdminHasPermissionsForConfig()
...
@balder.connect(Server, over_connection=balder.Connection)
class NormalUser(balder.Device):
perm = NormalUserHasPermissionsForConfig()
...
@balder.connect(Server, over_connection=balder.Connection)
class UnauthUser(balder.Device):
_unauth = IsUnauthenticatedFeature()
...
Implementing the OperationHandlingFeature
All permission scenarios needs an implementation of the
OperationHandlingFeature. Find an example implementation below:
...
from balderhub.auth.lib.scenario_features.client import OperationHandlingFeature
class MyImplementationOfOperationHandlingFeature(OperationHandlingFeature):
def enter_operation(self, operation: Operation) -> bool:
.. # TODO develop process to enter an operation (resource over an specific Action)
return True
def leave_operation(self, operation: Operation) -> bool:
.. # TODO develop process to leave an operation (resource over an specific Action) - f.e. clean up
return True
Note
If you’re testing a web application, you can use the ready-to-use implementation of this feature within the
balderhub-http or the balderhub-webdriver (mostly used with selenium):
balderhub.http.contrib.auth.setup_features.client.OperationHandlingOverWebsessionFeature: Can be used for testing web end points specially REST endpoints (uses therequestspackage as base)balderhub.webdriver.contrib.auth.setup_features.client.OperationHandlingOverWebdriverFeature: Can be used for testing graphical web interfaces, specially if you already use selenium or other webdriver supported balderhub packages
Providing your own UnresolvedResource type
This package has been implemented in a very versatile way. The main resource type, that is used, when testing object
permission is the UnresolvedResource. This is an abstract class that need to be
overwritten, before it can be used:
from balderhub.auth.lib.utils.unresolved_resource import UnresolvedResource
from balderhub.auth.lib.utils.resource import Resource
from tests.lib.utils.my_resource import MyResource
class UserAccountResource(UnresolvedResource):
class Parameter(UnresolvedResource.Parameter):
def __init__(self, username: str):
self.username = username
def __eq__(self, other):
return isinstance(other, UserAccountResource.Parameter) and self.username == other.username
def __hash__(self):
return hash(self.username)
def __eq__(self, other):
return isinstance(other, MyUnresolvedResource) and self.name == other.name
def __hash__(self):
return hash(self.name)
def get_resolved_resource(self, param: 'UserAccountResource.Parameter') -> Resource:
return MyResource(f'/home/{param.username}')
It is important to implement the get_resolved_resource(), because this
method resolves the unresolved resource into an resolved one.
Note
If your parameters are specified by data-items from the
balderhub-data package you can use the already implemented
unresolved-resource balderhub.data.contrib.auth.utils.ResourceForSpecificDataItem.
The package also provides an implementation of the
UnresolvedResourceParameterConfig described below. You can
use the factory balderhub.data.contrib.auth.setup_features.factories.AutoDataParamProviderFactory or
implementing your own version by overwriting the
balderhub.data.contrib.auth.setup_features.DataItemParamProvider directly.
Additionally to the normal features, that are used for the normal scenarios, the object-permission test scenarios also
need an implementation of the UnresolvedResourceParameterConfig.
This feature defines which object-variants are verified within the test:
from balderhub.auth.lib.scenario_features.client.unresolved_resource_parameter_config import UnresolvedResourceParameterConfig
from balderhub.auth.lib.utils.unresolved_resource import UnresolvedResource
from tests.lib.utils.my_unresolved_resource import MyUnresolvedResource
class UnresolvedParamConfig(UnresolvedResourceParameterConfig):
"""Provides parameters for unresolved resources used in tests."""
def get_parameters_for(self, resource_rule) -> list[UnresolvedResource.Parameter]:
if isinstance(resource_rule.resource, UserAccountResource):
return [
UserAccountResource.Parameter('admin'),
UserAccountResource.Parameter('guest'),
UserAccountResource.Parameter('developer'),
]
else:
raise NotImplementedError()
Testing Non-Object-Permissions
This BalderHub project provides two different scenarios for testing resolved resources (defined by normal
ResourceRule).
The ScenarioAuthpermUnauthenticated:
The following code shows an example setup that runs the
ScenarioAuthpermUnauthenticated:
import balder
class SetupExample(balder.Setup):
class Server(balder.Device):
exists = MyExistenceForConfig()
auth_for = MyAuthenticationForConfig()
@balder.connect(Server, over_connection=balder.Connection)
class UnauthUser(balder.Device):
_is_unauth = balderhub.auth.lib.scenario_features.server.IsUnauthenticatedFeature()
operation = MyImplementationOfOperationHandlingFeature()
The ScenarioAuthpermAuthenticated:
The following code shows an example setup that runs the
ScenarioAuthpermAuthenticated:
import balder
class SetupExample(balder.Setup):
class Server(balder.Device):
exists = MyExistenceForConfig()
auth_for = MyAuthenticationForConfig()
@balder.connect(Server, over_connection=balder.Connection)
class AdminUser(balder.Device):
operation = MyImplementationOfOperationHandlingFeature()
perm = AdminHasPermissionsForConfig()
Testing Object-Permissions
Similar to the previous scenarios, there is also a dedicated scenario for each one that explicitly tests object
permissions by using the UnresolvedResource. These resources allow you to define
programmatic rules to specify the conditions that must apply to an element for the user to have permission or for
authentication to be required.
The ScenarioAuthpermUnauthenticatedObjperm:
The following code shows an example setup that runs the
ScenarioAuthpermUnauthenticatedObjperm:
import balder
class SetupExample(balder.Setup):
class Server(balder.Device):
exists = MyExistenceForConfig()
auth_for = MyAuthenticationForConfig()
@balder.connect(Server, over_connection=balder.Connection)
class UnauthUser(balder.Device):
_is_unauth = balderhub.auth.lib.scenario_features.server.IsUnauthenticatedFeature()
operation = MyImplementationOfOperationHandlingFeature()
The ScenarioAuthpermAuthenticatedObjperm:
The following code shows an example setup that runs the
ScenarioAuthpermAuthenticatedObjperm:
import balder
class SetupExample(balder.Setup):
class Server(balder.Device):
exists = MyExistenceForConfig()
auth_for = MyAuthenticationForConfig()
@balder.connect(Server, over_connection=balder.Connection)
class AdminUser(balder.Device):
operation = MyImplementationOfOperationHandlingFeature()
perm = AdminHasPermissionsForConfig()