1. VERTICALLY
CHALLENGED
1
Tuesday, October 20, 2009
2. Strong Application Permissions,
using PostgreSQL
Aurynn Shaw, Command Prompt, Inc.
PostgreSQL Conference West, 2009
2
Tuesday, October 20, 2009
3. APPLICATION
ARCHITECTURE
Wherein
Basic Architecture
Limitations
Points of Authority
Why use DB permissions?
Implementation
3
Tuesday, October 20, 2009
4. Basic Architecture
Web Server
Application Standard tiered design
Database
4
Tuesday, October 20, 2009
5. Basic Architecture
Web Server
Standard tiered design
Application Standard ingress, logic,
egress cycle.
Database
5
Tuesday, October 20, 2009
6. Layered Communications
Web Server
Limitations in
Application
communication
Database
6
Tuesday, October 20, 2009
7. Layered Communications
Web Server
Limitations in
communication
Application
Influences on application
design
Database
7
Tuesday, October 20, 2009
8. Application Authority
Web Server
The Application becomes
Application
permissions arbiter
Database
8
Tuesday, October 20, 2009
9. Application Authority
Web Server
The Application becomes
Application permissions arbiter
Loss of Portability
Database
9
Tuesday, October 20, 2009
10. Application Authority
Web Server
The Application becomes
permissions arbiter
Application Loss of Portability
Increased work for other
consumers
Database
10
Tuesday, October 20, 2009
12. Using DB Permissions
Why ?
It’s better than what you have now
12
Tuesday, October 20, 2009
13. Using DB Permissions
Why ?
It’s better than what you have now
Easy Integration
13
Tuesday, October 20, 2009
14. Using DB Permissions
Why ?
It’s better than what you have now
Easy Integration
Once it’s set up, it’s easier to use
14
Tuesday, October 20, 2009
15. Basic Mechanica
Entirely DB users
Why this is hard to implement
15
Tuesday, October 20, 2009
16. Basic Mechanica
Entirely DB users
Why this is hard to implement
DB users, with an external auth store
Still hard
Why it’s Worthwhile
16
Tuesday, October 20, 2009
17. Basic Mechanica
Entirely DB users
Why this is hard to implement
DB users, with an external auth store
Still hard
Why it’s Worthwhile The Hybrid approach - use
The Hybrid approach DB roles + rows in the
database, instead of DB
users.
17
Tuesday, October 20, 2009
19. Componentry
Postgres Implementation
SET ROLE
GRANT / NOINHERIT Grant / noinherit - How
this works together to
NOLOGIN provide VC.
19
Tuesday, October 20, 2009
20. Componentry
Postgres Implementation
SET ROLE
GRANT / NOINHERIT
NOLOGIN
Great for PCI
sudo for the Database compliance
20
Tuesday, October 20, 2009
21. Setup
vc=# CREATE ROLE vc NOINHERIT;
CREATE ROLE
vc=# CREATE ROLE auth_level NOLOGIN;
CREATE ROLE
vc=# GRANT auth_level TO vc;
GRANT ROLE
vc=#
21
Tuesday, October 20, 2009
22. Setup
vc=> CREATE TABLE auth_only();
CREATE
vc=> REVOKE SELECT ON auth_only FROM
PUBLIC, vc;
REVOKE
vc=> SET ROLE TO vc;
SET
vc=>
22
Tuesday, October 20, 2009
23. sudo for Databases
vc=> SELECT * FROM auth_only;
ERROR: permission denied for relation
auth_only
vc=> SET ROLE auth_level;
SET
vc=> SELECT * FROM auth_only;
--
(0 rows)
23
Tuesday, October 20, 2009
24. It’s How We Role
Basic roles
24
Tuesday, October 20, 2009
25. It’s How We Role
Basic roles
Defines your permissions tree
25
Tuesday, October 20, 2009
26. It’s How We Role
Basic Roles
Defines your permissions tree
Privileged Roles
The over-arching Container roles
User, Moderator, Admin, etc.
26
Tuesday, October 20, 2009
27. It’s How We Role
Basic Roles
Defines your permissions tree
Privileged Roles
The over-arching permissions definitions
User, Moderator, Admin, etc. Entry point has
LOGIN granted.
Entry Point
27
Tuesday, October 20, 2009
29. A Quick Check
IF users.can(user_id, ‘type.read’) THEN
RETURN QUERY
SELECT * FROM type;
ELSE
exceptable.permission_denied(‘User
lacks type.read permissions’);*
END IF;
* Exceptable function
29
Tuesday, October 20, 2009
30. Wait.. I don’t know you.
users.can
users.validate
30
Tuesday, October 20, 2009
31. User Legitimacy
CREATE OR REPLACE FUNCTION users.validate
(
in_username text,
in_password text
) RETURNS int
SELECT users.validate(‘vc’,‘password’);
31
Tuesday, October 20, 2009
32. The Forge of Users
users.can
users.validate
users.create
32
Tuesday, October 20, 2009
33. Creating Users
CREATE OR REPLACE FUNCTION users.create (
in_username text,
in_role text,
in_password text
) RETURNS int
SELECT users.create(‘user’,‘auth’,md5
(‘password’));
33
Tuesday, October 20, 2009
34. Basic User Get
users.can
users.validate
users.create
users.get
34
Tuesday, October 20, 2009
35. Collecting Users
CREATE OR REPLACE FUNCTION users.get (
in_user_id int
) RETURNS users.user
CREATE TYPE users.user AS (
id int,
username text,
roles text[] -- Used for app checks
);
35
Tuesday, October 20, 2009
36. STANDING ON PYLONS
Wherein
Exceptions
Permissions checks
Repoze.Who
36
Tuesday, October 20, 2009
37. Exceptions
Repoze.who cares about 401s and 403s
37
Tuesday, October 20, 2009
38. Exceptions
Repoze.who cares about 401s and 403s
Trap VC errors in the Application
Emit an appropriate 401 or 403
38
Tuesday, October 20, 2009
39. Exceptions
Repoze.who cares about 401s and 403s
Trap VC errors in the Application
Emit an appropriate 401 or 403
Exceptions are handled ONCE
39
Tuesday, October 20, 2009
40. Once
class BaseController(WSGIController):
def __call__(self, environ, start_response):
"""Invoke the Controller"""
# WSGIController.__call__ dispatches to the
Controller method
# the request is routed to.
try:
return WSGIController.__call__(self, environ,
start_response)
except PermissionsError, e:
abort(403)
except NoSuchUserError, e:
abort(401)
40
Tuesday, October 20, 2009
41. I have @needs too!
Easily Decorate Pylons Views - @needs(‘type.read’)
41
Tuesday, October 20, 2009
42. I have @needs too!
Easily Decorate Pylons Views - @needs(‘type.read’)
Using DB permissions in the App layer
42
Tuesday, October 20, 2009
43. I have @needs too!
Easily Decorate Pylons Views - @needs(‘type.read’)
Using DB permissions in the App layer
A single DB query - Not a query per object.
43
Tuesday, October 20, 2009
44. I have @needs too!
Easily Decorate Pylons Views - @needs(‘type.read’)
Using DB permissions in the App layer
A single DB query - Not a query per object.
Why? Multiple tiers are good.
44
Tuesday, October 20, 2009
45. Pylons in Repose
The Application Cycle
45
Tuesday, October 20, 2009
46. Pylons in Repose
The Application Cycle
A Basic Repoze Configuration
46
Tuesday, October 20, 2009
47. A Basic Configuration
[plugin:form]
use =
repoze.who.plugins.form:make_redirecting_plugin
login_form_url = /account/login
login_handler_path = /account/dologin
logout_handler_path = /account/logout
rememberer_name = auth_tkt
[plugin:auth_tkt]
use = repoze.who.plugins.auth_tkt:make_plugin
secret = ticket_secret
[mdproviders]
plugins = vc.repoze:MetadataProvider;browser
47
Tuesday, October 20, 2009
48. Pylons in Repose
The Application Cycle
A Basic Repoze Configuration
The Permissions Swap
48
Tuesday, October 20, 2009
49. The Permissions Swap
class MetadataProvider(object):
def add_metadata(self, environ, identity):
if userid:
try:
from vc.model import user
u = user.user(userid)
u.become(u.role)
except PermissionError, e:
if “model” in identity:
del identity[“model”]
return None
identity[“model”] = u
49
Tuesday, October 20, 2009
50. And the Procedure
CREATE FUNCTION users.become (
in_user_id int, in_role text
) RETURNS VOID AS $$
BEGIN
IF users.can($1, $2) THEN
SET ROLE TO quote_literal($2);
ELSE
except.permission_denied('User
cannot become specified permission level');
END IF;
END;
$$ LANGUAGE PLPGSQL;
50
Tuesday, October 20, 2009
51. Pylons in Repose
The Application Cycle
A Basic Repoze Configuration
The Permissions Swap
Anonymous Access
51
Tuesday, October 20, 2009
52. Anonymous Access
A Wrapping Identifier
Test For an Identity
52
Tuesday, October 20, 2009
53. Wrap the Identifier
class AnonymousWrapper(object):
def identify(self, environ):
i = environ['repoze.who.plugins']
['auth_tkt'].identify(environ)
if i:
# Not anonymous
return i
else:
# Is anonymous
# return a really basic anonymousness.
return {"repoze.who.userid":
anonymous_user_id}
53
Tuesday, October 20, 2009
54. Anonymous Access
A Wrapping Identifier
Test For an Identity
Return a User, or Anonymous
Anonymous users are otherwise identical
54
Tuesday, October 20, 2009
56. Advantages
Same roles in your Procedures, as your Application code
Great for consistency
Can Implement Row-level Permissions
56
Tuesday, October 20, 2009
57. Row-Level Example
PERFORM id FROM table WHERE id = i_id;
IF FOUND THEN
PERFORM owner_id FROM table
WHERE owner_id = user_id AND id = i_id;
IF NOT FOUND THEN
exceptable.permission_denied(‘User
does not own specified record.’);
END IF;
END IF;
57
Tuesday, October 20, 2009
58. Disadvantages
No PG-backed row-level authorization
58
Tuesday, October 20, 2009
59. Disadvantages
No PG-backed row-level authorization
DDL permissions take work to implement
59
Tuesday, October 20, 2009
60. Disadvantages
No direct row-level authorization
DDL permissions take work to implement
No ad-hoc Users
60
Tuesday, October 20, 2009
61. Disadvantages
No direct row-level authorization
DDL permissions take work to implement
No ad-hoc Users
Custom permissions require new entry points
61
Tuesday, October 20, 2009
62. Vulnerabilities
Any possible PG escalation attack
SET ROLE could switch to an Admin user
User errors a perennial favourite
62
Tuesday, October 20, 2009
63. AND THUS
Questions?
63
Tuesday, October 20, 2009
64. GET IT
https://projects.commandprompt.com/
public/verticallychallenged
64
Tuesday, October 20, 2009