Getting ready for talk at ApacheCon / AppSec…
The slides are here:
Getting ready for talk at ApacheCon / AppSec…
The slides are here:
And it don’t need no stinking badges. Yeah, I’m mixing clichés, happens sometimes when coding long hours in a stretch. 🙂
What I’m talking about is a new access management system, released to PyPI yesterday for the first time.
Considering we just started coding a couple of months ago that’s pretty good progress.
What is it? A toolkit designed for Python3, with APIs that developers can use to do security in an RBAC-compliant way. Today, it requires an LDAP server to store the policies, but a file-based backend will soon be ready. No, I’m not recommending files in production, but it’s fine for getting started, within dev envs.
You can check the py-fortress project here:
Inside are links to some documents to help get started. The quickstart doesn’t require cloning the project GIT repo, but you’ll need a Linux machine, Python3, PIP and Docker engine installed. Everything else is covered.
Why would you want it? That’s a long story. It starts with an imperative to follow standards, in security processes, like authentication and authorization. That there’s value in committing to best-practices, in this case ANSI RBAC.
It may help to know that this effort is backed by my employer — Symas.
Eventually, this code may end up inside of an Apache project, like Apache Fortress. Or, it might land somewhere else. It’s too early to know. What’s certain is that it’ll remain open, available to use and learn from.
py-fortress implements standards-based RBAC in Python. There have been numerous postings lately here about it.
Very soon we’ll have a release of py-fortress that works with just the file system. This is to lower complexity of testing and developing with this package. In the meantime, an LDAP server has to be running on the network, so follow the instructions in the quickstart below to use a Docker container, if you don’t already have one installed.
from pyfortress import ( # model User, Role, Perm, PermObj, # apis: review_mgr, admin_mgr, access_mgr, #exception handling: FortressError, global_ids )
These are used to check the policies at runtime. For example, to authenticate is create_session and authorization is check_access here.
def test_create_session(self):
"""
create session
"""
print('test_create_session')
try:
session = access_mgr.create_session(User(uid='foo1', password='secret'), False)
if not session:
print('test_create_session fail')
self.fail('test_create_session fail')
else:
print('test_create_session pass')
pass
except FortressError as e:
self.fail('test_create_session failed, exception=' + e.msg)
The session will then be held on to by the client for subsequent calls like check_access and session_perms
def test_check_access(self):
"""
create session and check perm
"""
print('test_check_access')
try:
session = ... obtained earlier
result = access_mgr.check_access(session, Perm(obj_name='ShoppingCart', op_name='add'))
if not result:
print('test_check_access fail')
self.fail('test_check_access fail')
else:
print('test_check_access pass')
pass
except FortressError as e:
self.fail('test_check_access failed, exception=' + e.msg)
def test_session_perms(self):
"""
create session and get perms for user
"""
print('test_check_access')
try:
session = ... obtained earlier
perms = access_mgr.session_perms(session)
if not perms:
print('test_session_perms failed')
self.fail('test_session_perms failed')
for perm in perms:
print_perm(perm, 'session_perms: ')
pass
except FortressError as e:
self.fail('test_session_perms failed, exception=' + e.msg)
These are for programs that manage and search the data. For example admin guis, conversion programs, reporting apps.
def test_add_user(self):
"""
Add a basic user
"""
print('test_add_user')
try:
admin_mgr.add_user(User(uid='foo1', password='secret'))
print('test_add_user success')
except FortressError as e:
self.fail('test_add_user failed, exception=' + e.msg)
def test_add_role(self):
"""
Add a basic role
"""
print('test_add_role')
try:
admin_mgr.add_role(Role(name='Customer'))
print('test_add_role success')
except FortressError as e:
self.fail('test_add_role failed, exception=' + e.msg)
def test_add_obj(self):
"""
Add a basic perm object
"""
print('test_add_obj')
try:
admin_mgr.add_object(PermObj(obj_name='ShoppingCart'))
print('test_add_obj success')
except FortressError as e:
self.fail('test_add_obj failed, exception=' + e.msg)
def test_add_perm(self):
"""
Add a basic perm
"""
print('test_add_perm')
try:
admin_mgr.add_perm(Perm(obj_name='ShoppingCart', op_name='add'))
print('test_add_perm success')
except FortressError as e:
self.fail('test_add_perm failed, exception=' + e.msg)
def test_assign_user(self):
"""
Assign a user to a role
"""
print('test_assign_user')
try:
admin_mgr.assign(User(uid='foo1'), Role(name='Customer'))
print('test_assign_user success')
except FortressError as e:
self.fail('test_assign_user failed, exception=' + e.msg)
def test_grant_perm(self):
"""
Grant a permission to a role
"""
print('test_grant_perm')
try:
admin_mgr.grant(Perm(obj_name='ShoppingCart', op_name='add'), Role(name="Customer"))
print('test_grant_perm success')
except FortressError as e:
self.fail('test_grant_perm failed, exception=' + e.msg)
7. Read a user:
def test_read_user(self): """ Read a user that was created earlier. Expects a unique uid that points to an existing user. """ print_test_name() try: out_user = review_mgr.read_user(User(uid='foo1')) print_user(out_user) except FortressError as e: print_exception(e) self.fail()
thows exception if user is not present
8. Search for users with a matching uid:
def test_search_users(self):
"""
Search for users that match the characters passed into with wildcard appended. Will return zero or more records, one for each user in result set.
"""
print_test_name()
try:
users = review_mgr.find_users(User(uid='foo*'))
for user in users:
print_user(user)
except FortressError as e:
print_exception(e)
self.fail()
returns a list of type user
9. Search for users assigned a role:
def test_assigned_users(self):
"""
Search for users that are assigned a particular role. Will return zero or more records, one for each user in result set.
"""
print_test_name()
try:
uids = review_mgr.assigned_users(Role(name='Customer'))
for uid in uids:
print_test_msg('uid=' + uid)
except FortressError as e:
print_exception(e)
self.fail()
returns a list of type string contain the user id
10. Search for users who have a permission:
def test_perm_users(self):
"""
Search for users that have been authorized a particular permission. Will return zero or more records, of type user, one for each user authorized that particular perm.
"""
print_test_name()
try:
users = review_mgr.perm_users(Perm(obj_name='ShoppingCart', op_name='add'))
for user in users:
print_user(user)
except FortressError as e:
print_exception(e)
self.fail()
returns a list of type user
11. Read a role:
def test_read_role(self):
"""
The read role expects the role name to point to an existing entry and will throw an exception if not found or other error occurs.
"""
print_test_name()
try:
out_role = review_mgr.read_role(Role(name='Customer'))
print_role(out_role)
except FortressError as e:
print_exception(e)
self.fail()
returns a single entity of type role
12. Search for roles matching a particular name:
def test_search_roles(self):
"""
Search for roles that match the characters passed into with wildcard appended. Will return zero or more records, one for each user in result set.
"""
print_test_name()
try:
roles = review_mgr.find_roles(Role(name='Customer*'))
for role in roles:
print_role(role)
except FortressError as e:
print_exception(e)
self.fail()
returns a list of type role
13. Search for roles assigned to a user:
def test_assigned_roles(self):
"""
Return the list of roles that have been assigned a particular user. Will return zero or more records, of type constraint, one for each role assigned to user.
"""
print_test_name()
try:
constraints = review_mgr.assigned_roles(User(uid='foo1'))
for constraint in constraints:
print_test_msg('role name=' + constraint.name)
except FortressError as e:
print_exception(e)
self.fail()
returns a list of type role_constraint
14. Search for roles who have granted a particular permission:
def test_perm_roles(self):
"""
Return the list of roles that have granted a particular perm. Will return zero or more records, containing the role names, one for each role assigned to permission.
"""
print_test_name()
try:
names = review_mgr.perm_roles(Perm(obj_name='ShoppingCart', op_name='add'))
for name in names:
print_test_msg('role name=' + name)
except FortressError as e:
print_exception(e)
self.fail()
returns a list of strings containing the role names
15. Read an object:
def test_read_obj(self):
"""
The ob_name is the only required attribute on a fortress object. Will throw an exception if not found.
"""
print_test_name()
try:
out_obj = review_mgr.read_object(PermObj(obj_name='ShoppingCart'))
print_obj(out_obj)
except FortressError as e:
print_exception(e)
self.fail()
return a single entity of type object
16. Search for objects matching obj_name field:
def test_search_objs(self):
"""
Search for ojects that match the characters passed into with wildcard appended. Will return zero or more records, one for each user in result set.
"""
print_test_name()
try:
objs = review_mgr.find_objects(PermObj(obj_name='ShoppingCart*'))
for obj in objs:
print_obj(obj)
except FortressError as e:
print_exception(e)
self.fail()
returns a list of type object
17. Read a permission entry:
def test_read_perm(self):
"""
Permissions require obj_name and op_name, obj_id is optional. This will throw an exception if not found.
"""
print_test_name()
try:
out_perm = review_mgr.read_perm(Perm(obj_name='ShoppingCart', op_name='add'))
print_perm(out_perm)
except FortressError as e:
print_exception(e)
self.fail()
returns a single entity of type permission
18, Search for perms:
def test_search_perms(self):
"""
Search for perms that match the characters passed into with wildcard appended. Will return zero or more records, one for each user in result set.
"""
print_test_name()
try:
perms = review_mgr.find_perms(Perm(obj_name='ShoppingCart*', op_name='*'))
for perm in perms:
print_perm(perm)
except FortressError as e:
print_exception(e)
self.fail()
returns a list of type perm
19. Search for perms by role:
def test_role_perms(self):
"""
Search for perms that have been granted to a particular role. Will return zero or more records, of type permission, one for each grant.
"""
print_test_name()
try:
perms = review_mgr.role_perms(Role(name='Customer'))
for perm in perms:
print_perm(perm)
except FortressError as e:
print_exception(e)
self.fail()
returns a list of type permission
20. Search for perms by user:
def test_user_perms(self):
"""
Search for perms that have been authorized to a particular user based on their role assignments. Will return zero or more records, of type permission, one for each perm authorized for user.
"""
print_test_name()
try:
perms = review_mgr.user_perms(User(uid='foo1'))
for perm in perms:
print_perm(perm)
except FortressError as e:
print_exception(e)
self.fail()
returns a list of type permission
The Command Line Interpreter (CLI) may be used to drive the RBAC System APIs, to test, verify and understand a particular RBAC policy.
This document also resides here: README-CLI-AUTH
Sample RBAC0 Policy
uid | timeout | begin_time | end_time | … |
---|---|---|---|---|
chorowitz | 30min |
name | timeout | begin_time | end_time | … |
---|---|---|---|---|
account-mgr | 30min | |||
auditor | 5min |
constraints are optional and include time, date, day and lock date validations
user | account-mgr | auditor |
---|---|---|
chorowitz | true | true |
obj_name | op_name |
---|---|
page456 | edit |
page456 | remove |
page456 | read |
role | page456.edit | page456.remove | page456.read |
---|---|---|---|
account-mgr | true | true | false |
auditor | false | false | true |
clitest operation --arg1 --arg2 ...
pyfortress.test.cli_test_auth
Where operations => functions here: access_mgr.py
___________________________________________________________________________________
To setup RBAC test data, we’ll be using another utility that was introduced here: README-CLI.md.
From the py-fortress/test folder, enter the following commands:
1. user add – chorowitz
$ cli user add --uid chorowitz --password 'secret' --timeout 30
user chorowitz has a 30 minute inactivity timeout
2. role add – account-mgr
$ cli role add --name 'account-mgr'
3. role add – auditor
$ cli role add --name 'auditor' --timeout 5
role auditor has a 5 minute inactivity timeout, more later about this…
4. user assign – chorowitz to role account-mgr
$ cli user assign --uid 'chorowitz' --role 'account-mgr'
5. user assign – chorowitz to role auditor
$ cli user assign --uid 'chorowitz' --role 'auditor'
6. object add – page456
$ cli object add --obj_name page456
7. perm add – page456.read
$ cli perm add --obj_name page456 --op_name read
8. perm add – page456.edit
$ cli perm add --obj_name page456 --op_name edit
9. perm add – page456.remove
$ cli perm add --obj_name page456 --op_name remove
10. perm grant – page456.edit to role account-mgr
$ cli perm grant --obj_name page456 --op_name edit --role account-mgr
11. perm grant – page456.remove to role account-mgr
$ cli perm grant --obj_name page456 --op_name remove --role account-mgr
12. perm grant – page456.read to role auditor
$ cli perm grant --obj_name page456 --op_name read --role auditor
________________________________________________________________________________
From the py-fortress/test folder, enter the following commands:
1. auth – access_mgr.create_session – authenticate, activate roles:
$ clitest auth --uid 'chorowitz' --password 'secret'
uid=chorowitz
auth
success
Now the session has been pickled in on file system in current directory.
2. show – output user session contents to stdout:
$ clitest show
show
session
is_authenticated: True
user:
last_access:
user
cn: chorowitz
constraint:
system: []
roles: ['account-mgr', 'auditor']
dn: uid=chorowitz,ou=People,dc=example,dc=com
uid: chorowitz
internal_id: 552c1a24-5087-4458-98f1-8c60167a8b7c
reset: []
sn: chorowitz
User Constraint:
name: chorowitz
raw: chorowitz$30$$$$$$$
timeout: 30
User-Role Constraint[1]:
name: account-mgr
raw: account-mgr$0$$$$$$$
User-Role Constraint[2]:
name: auditor
raw: auditor$5$$$$$$$
timeout: 5
success
Displays the contents of session to stdout.
3. check – access_mgr.check_access – perm page456.read:
$ clitest check --obj_name page456 --op_name read
op_name=read
obj_name=page456
check
success
The user has auditor activated so unless timeout validation failed this will succeed.
4. check – access_mgr.check_access – perm page456.edit:
$ clitest check --obj_name page456 --op_name edit
op_name=edit
obj_name=page456
check
success
The user has account-mgr activated and this will succeed.
5. check – access_mgr.check_access – perm page456.remove:
$ clitest check --obj_name page456 --op_name remove
op_name=remove
obj_name=page456
check
success
The user has account-mgr activated and this will succeed.
6. get – access_mgr.session_perms:
$ clitest perms
perms
page456.read:0
abstract_name: page456.read
roles: ['auditor']
internal_id: d6887434-050c-48d8-85b0-7c803c9fcf07
obj_name: page456
op_name: read
page456.edit:1
abstract_name: page456.edit
roles: ['account-mgr']
internal_id: 02189535-4b39-4058-8daf-af0e09b0d235
obj_name: page456
op_name: edit
page456.remove:2
abstract_name: page456.remove
roles: ['account-mgr']
internal_id: 10dea5d1-ff1d-4c3d-90c8-edeb4c7bb05b
obj_name: page456
op_name: remove
success
Display all perms allowed for activated roles to stdout confirms that user indeed can read, edit and remove from Page456.
7. drop – access_mgr.drop_active_role – auditor:
$ clitest drop --role auditor
drop
role=auditor
success
RBAC distinguishes between roles assigned or activated and privileges can be altered in the midst of a session.
8. roles – access_mgr.session_roles
$ clitest roles
roles
account-mgr:0
raw: account-mgr$30$$$20180101$none$$$1234567
end_date: none
name: account-mgr
timeout: 30
success
Notice the audit role is no longer active.
9. check – access_mgr.check_access – perm page456.read (again):
$ clitest check --obj_name page456 --op_name read
op_name=read
obj_name=page456
check
failed
The auditor role was deactivated so even though it’s assigned, user cannot perform as one.
10. add – access_mgr.add_active_role – auditor:
$ clitest add --role auditor
add
role=auditor
success
Now the user should be allowed to resume audit activities.
11. roles – access_mgr.session_roles:
$ clitest roles
roles
account-mgr:0
raw: account-mgr$30$$$20180101$none$$$1234567
end_date: none
name: account-mgr
timeout: 30
auditor:1
raw: auditor$5$$$20180101$none$$$1234567
timeout: 5
name: auditor
begin_lock_date: success
success
Notice the audit role has been activated once again.
12. check – access_mgr.check_access – perm page456.read (for the 3rd time):
$ clitest check --obj_name page456 --op_name read
op_name=read
obj_name=page456
check
success
The auditor role activated once again so user can do auditor things again.
13. Wait 5 minutes before performing the next step.
Allow enough time for auditor role timeout to occur before moving to the next step. Now, if you run the roles command, the auditor role will once again be missing. This behavior is controlled by the ‘timeout’ attribute on either a user or role constraint.
14. check – access_mgr.check_access – perm page456.read:
$ clitest check --obj_name page456 --op_name read
op_name=read
obj_name=page456
check
failed
Because the auditor role has timeout constraint set to 5 (minutes), it was deactivated automatically from the session.
END
The Command Line Interpreter (CLI) drives the admin and review APIs, allowing ad-hoc RBAC setup and interrogation. More info in the README.
This document also resides here: README-CLI.
Completed the setup described: README-QUICKSTART
cli entity operation --arg1 --arg2 ...
pyfortress.test.cli
(These are source pointers to their locations in github)
The operation is (pick one):
(These are just meta tags)
Consists of two dashes ‘- -‘ plus the attribute name and value pair, with a space between them.
--attribute_name value
if an attribute value contains white space, enclose in single ‘ ‘ or double tics ” “.
--attribute_name 'some value' --attribute_name2 "still more values"
For example, a perm grant:
$ cli perm grant --obj_name myobj --op_name add --role 'my role'
This command invokes Python’s runtime with the program name, cli.py, followed by an entity type, operation name and multiple name-value pairs.
The above used –role is the only argument that isn’t an entity attribute name. It’s used on user assign, deassign, perm grant, revoke operations.
For multi-occurring attributes, pass in as a list of string values, separated by whitespace
—phones
--phones '+33 401 851 4679' '1-212-251-1111' '(028) 9024 6609'
–mobiles
--mobiles ' 017x-1234567' '+44 020 7234 3456' '1-212-650-9632'
–emails
--emails 'f.lst@somewhere.com' 'myaccount@gmail.com' 'myworkaccount@company.com'
–props
--props 'name1:value1', 'name2:value2', 'name3:value3'
each value contains a name:value pair
Both the user and role entity support adding temporal constraint.
–-name : label for user, i.e uid
--name foo3
For users, this can be any safe text. For role, it must already be passed in, with the role’s name.
–timeout : 99 – set the integer timeout that contains max time (in minutes) that entity may remain inactive.
--timeout 30
30 minutes
–begin_time : HHMM – determines begin hour entity may be activated.
--begin_time 0900
9:00 am
— end_time : HHMM – determines end hour when entity is no longer allowed to activate.
--end_time 2359
11:59 pm
–begin_date : YYYYMMDD – determines date when entity may be activated.
--begin_date 20150101
Jan 1, 2015
–end_date : YYMMDD – indicates latest date entity may be activated.
--end_date 20191231
Dec 31, 2019
–begin_lock_date : YYYYMMDD – determines beginning of enforced inactive status
--begin_lock_date 20180602
Jun 2, 2018
–end_lock_date : YYMMDD – end of enforced inactive status.
--end_lock_date 20180610
Jun 10, 2018
–day_mask : 1234567, 1 = Sunday, 2 = Monday, etc – day of week entity may be activated.
--day_mask 1246
Sun, Mon, Wed, Fri
all together
cli user mod --uid someuser --name anysafetext --timeout 30 --begin_time 0900 --end_time 2359 --begin_date 20150101 --end_date 20191231 --begin_lock_date 20180602 --end_lock_date 20180610 --day_mask 1246
cli role add --name manager --description 'manager works 8-5, M-F' --timeout 10 --begin_time 0800 --end_time 1700 --begin_date 20100101 --end_date none --day_mask 1246
A Few Tips More
Two sections, one each for admin and review commands. They’re not real-world use cases but include what’s currently working although this code is, how to say it, fresh. 🙂
a. user add
$ cli user add --uid chorowitz --password 'secret' --description 'added with py-fortress cli' uid=chorowitz description=added with py-fortress cli user add success
b. user mod
$ cli user mod --uid chorowitz --l my location --ou my-ou --department_number 123 uid=chorowitz department_number=123 l=my location ou=my-ou user mod success
c. user del
$ cli user del --uid chorowitz uid=chorowitz user del success
d. user assign
$ cli user assign --uid chorowitz --role account-mgr uid=chorowitz role name=account-mgr user assign success
e. user deassign
$ cli user deassign --uid chorowitz --role account-mgr uid=chorowitz role name=account-mgr user deassign success
f. role add
$ cli role add --name account-mgr name=account-mgr role add success
g. role mod
$ cli role mod --name account-mgr --description 'this desc is optional' description=cli test role name=account-mgr role mod success
h. role del
$ cli role del --name account-mgr name=account-mgr role del success
i. object add
$ cli object add --obj_name page456 obj_name=page456 object add success
j. object mod
$ cli object mod --obj_name page456 --description 'optional arg' --ou 'another optional arg' obj_name=page456 ou=another optional arg description=optional arg object mod success
k. object del
$ cli object del --obj_name page789 obj_name=page789 object del success
l. perm add
$ cli perm add --obj_name page456 --op_name read obj_name=page456 op_name=read perm add success
m. perm mod
$ cli perm mod --obj_name page456 --op_name read --description 'useful for human readable perm name' obj_name=page456 op_name=read description=useful for human readable perm name perm mod success
n. perm del
$ cli perm del --obj_name page456 --op_name search obj_name=page456 op_name=search perm del success
o. perm grant
$ cli perm grant --obj_name page456 --op_name update --role account-mgr obj_name=page456 op_name=update role name=account-mgr perm grant success
p. perm revoke
$ cli perm revoke --obj_name page456 --op_name update --role account-mgr obj_name=page456 op_name=update role name=account-mgr perm revoke success
a. user read
$ cli user read --uid chorowitz uid=chorowitz user read chorowitz uid: chorowitz dn: uid=chorowitz,ou=People,dc=example,dc=com roles: ['account-mgr'] ... *************** chorowitz ******************* success
b. user search
$ cli user search --uid c uid=c user search c*:0 uid: canders dn: uid=canders,ou=People,dc=example,dc=com roles: ['csr', 'tester'] ... *************** c*:0 ******************* c*:1 uid: cedwards dn: uid=cedwards,ou=People,dc=example,dc=com roles: ['manager', 'trainer'] ... *************** c*:1 ******************* c*:2 uid: chandler dn: uid=chandler,ou=People,dc=example,dc=com roles: ['auditor'] ... *************** c*:2 ******************* c*:3 uid: chorowitz dn: uid=chorowitz,ou=People,dc=example,dc=com roles: ['account-mgr'] ... *************** c*:3 ******************* success
c. role read
$ cli role read --name account-mgr name=account-mgr role read account-mgr dn: cn=account-mgr,ou=Roles,dc=example,dc=com members: ['uid=cli-user2,ou=People,dc=example,dc=com', 'uid=chorowitz,ou=People,dc=example,dc=com'] internal_id: 5c189235-41b5-4e59-9d80-dfd64d16372c name: account-mgr constraint: <model.constraint.Constraint object at 0x7fc250bd9e10> Role Constraint: raw: account-mgr$0$$$$$$$ timeout: 0 name: account-mgr *************** account-mgr ******************* success
d. role search
$ cli role search --name py- name=py- role search py-*:0 dn: cn=py-role-0,ou=Roles,dc=example,dc=com description: py-role-0 Role constraint: <model.constraint.Constraint object at 0x7f17e8745f60> members: ['uid=py-user-0,ou=People,dc=example,dc=com', 'uid=py-user-1,ou=People,dc=example,dc=com', ... ] internal_id: 04b82ce3-974b-4ff5-ad21-b19ecca57722 name: py-role-0 *************** py-*:0 ******************* py-*:1 dn: cn=py-role-1,ou=Roles,dc=example,dc=com description: py-role-1 Role constraint: <model.constraint.Constraint object at 0x7f17e8733128> members: ['uid=py-user-8,ou=People,dc=example,dc=com', 'uid=py-user-9,ou=People,dc=example,dc=com'] internal_id: 70524da8-3be6-4372-a606-d8175e2ca63b name: py-role-1 *************** py-*:1 ******************* py-*:2 dn: cn=py-role-2,ou=Roles,dc=example,dc=com description: py-role-2 Role constraint: <model.constraint.Constraint object at 0x7f17e87332b0> members: ['uid=py-user-3,ou=People,dc=example,dc=com', 'uid=py-user-5,ou=People,dc=example,dc=com', 'uid=py-user-7,ou=People,dc=example,dc=com'] internal_id: d1b9da70-9302-46c3-b21b-0fc45b863155 name: py-role-2 *************** py-*:2 ******************* ... success
e. object read
$ cli object read --obj_name page456 obj_name=page456 object read page456 description: optional arg dn: ftObjNm=page456,ou=Perms,dc=example,dc=com internal_id: 1635cb3b-d5e2-4fcb-b61a-b8e91437e536 obj_name: page456 ou: another optional arg success
f. object search
$ cli object search --obj_name page obj_name=page object search page*:0 props: obj_name: page456 description: optional arg dn: ftObjNm=page456,ou=Perms,dc=example,dc=com ou: another optional arg internal_id: 1635cb3b-d5e2-4fcb-b61a-b8e91437e536 page*:1 obj_name: page123 description: optional arg dn: ftObjNm=page123,ou=Perms,dc=example,dc=com ou: another optional arg internal_id: a823ef98-7be4-4f49-a805-83bfef5a0dfb success
g. perm read
$ cli perm read --obj_name page456 --op_name read op_name=read obj_name=page456 perm read page456.read internal_id: 0dc55181-968e-4c60-8755-e20fa1ce017d dn: ftOpNm=read,ftObjNm=page456,ou=Perms,dc=example,dc=com abstract_name: page456.read description: useful for human readable perm name obj_name: page456 op_name: read success
h. perm search
$ cli perm search --obj_name page obj_name=page perm search page*.*:0 abstract_name: page456.read op_name: read internal_id: 0dc55181-968e-4c60-8755-e20fa1ce017d obj_name: page456 dn: ftOpNm=read,ftObjNm=page456,ou=Perms,dc=example,dc=com description: useful for human readable perm name page*.*:1 roles: ['account-mgr'] abstract_name: page456.update op_name: update internal_id: 626bca86-014b-4186-83a6-a583e39868a1 obj_name: page456 dn: ftOpNm=update,ftObjNm=page456,ou=Perms,dc=example,dc=com page*.*:2 roles: ['account-mgr'] abstract_name: page456.delete op_name: delete internal_id: 6c2fa5fc-d7c3-4e85-ba7f-5e514ca4263f obj_name: page456 dn: ftOpNm=delete,ftObjNm=page456,ou=Perms,dc=example,dc=com success
i. perm search (by role)
$ cli perm search --role account-mgr perm search account-mgr:0 abstract_name: page456.update obj_name: page456 op_name: update roles: ['account-mgr'] dn: ftOpNm=update,ftObjNm=page456,ou=Perms,dc=example,dc=com internal_id: 626bca86-014b-4186-83a6-a583e39868a1 account-mgr:1 abstract_name: page456.delete obj_name: page456 op_name: delete roles: ['account-mgr'] dn: ftOpNm=delete,ftObjNm=page456,ou=Perms,dc=example,dc=com internal_id: 6c2fa5fc-d7c3-4e85-ba7f-5e514ca4263f success
j. perm search (by user)
$ cli perm search --uid chorowitz perm search chorowitz:0 dn: ftOpNm=update,ftObjNm=page456,ou=Perms,dc=example,dc=com internal_id: 626bca86-014b-4186-83a6-a583e39868a1 roles: ['account-mgr'] abstract_name: page456.update obj_name: page456 op_name: update chorowitz:1 dn: ftOpNm=delete,ftObjNm=page456,ou=Perms,dc=example,dc=com internal_id: 6c2fa5fc-d7c3-4e85-ba7f-5e514ca4263f roles: ['account-mgr'] abstract_name: page456.delete obj_name: page456 op_name: delete success
END
Next up, Testing the py-fortress RBAC0 System
py-fortress is a Python API implementing Role-Based Access Control level 0 – Core. It’s still pretty new so there’s going to be some rough edges that will need to be smoothed out in the coming weeks.
To try it out, clone its git repo and use one of the fortress docker images for OpenLDAP or Apache Directory. The README has the details.
The API is pretty simple to use.
# Add User: admin_mgr.add_user(User(uid='foo', password='secret')) # Add Role: admin_mgr.add_role(Role(name='customer')) # Assign User: admin_mgr.assign(User(uid='foo'), Role(name='customer')) # Add Permission: admin_mgr.add_perm_obj(PermObj(obj_name='shopping-cart')) admin_mgr.add_perm(Perm(obj_name='shopping-cart', op_name='checkout')) # Grant: admin_mgr.grant(Perm(obj_name='shopping-cart', op_name='checkout'),Role(name='customer'))
# Create Session, False means mandatory password authentication. session = access_mgr.create_session(User(uid='foo', password='secret'), False) # Permission check, returns True if allowed: result = access_mgr.check_access(session, Perm(obj_name='shopping-cart', op_name='checkout')) # Get all the permissions allowed for user: perms = access_mgr.session_perms(session) # Check a role: result = access_mgr.is_user_in_role(session, Role(name='customer')) # Get all roles in the session: roles = access_mgr.session_roles(session)
In addition, there’s the full compliment of review apis as prescribed by RBAC. If interested, look at the RBAC modules:
Each of the modules have comments that describe the functions, along with their required and optional attributes.
Try it out and let me know what you think. There will be a release in the near future that will include some additional tooling. If it takes off, RBAC1 – RBAC3 will follow.
The goals of this tutorial are to
To get started, follow the instructions in the README located on github:
https://github.com/shawnmckinney/role-engineering-sample
More info here: A Practical Guide to Role Engineering.
David Goodman’s keynote, LDAP 2020 Paradise Lost or Regained?, provides a retrospective for us to contemplate. In it, he describes LDAP’s roots (X.500), where it’s been (U of Mich, Netscape, Sun, Symas, Microsoft, ForgeRock, etc.), and offered insights of what needs to change.
Bottom line, it’s healthy to continually ask the question – Is LDAP dead? For the answer, we’ll only slightly alter Mark Twain’s famous quotation:
Reports of LDAP’s death have been greatly exaggerated.
Why is that? For starters, because of conferences like LDAPCon. More than its in-depth technical analysis and tutorials, is what happens in the spaces between the talks.
These spaces nurture the protocol by allowing free discussions on the flaws, and room to create plans for corresponding fixes/enhancements.
Just what are these fixes and enhancements? Have a look at the program and slides. Subscribe to the LDAPExt mailing list, share in best practices, and above all, keep attending LDAPCon!
See you at LDAPCon 2017!!