Browse Source

Added authorization services.

hotfix/merge
Marcos Pereira 7 years ago
parent
commit
4e3fe9c7e1
  1. 1
      .gitignore
  2. 8
      .travis.yml
  3. 79
      keycloak/__init__.py
  4. 80
      keycloak/authorization/__init__.py
  5. 82
      keycloak/authorization/permission.py
  6. 84
      keycloak/authorization/policy.py
  7. 27
      keycloak/authorization/role.py
  8. 35
      keycloak/connection.py
  9. 4
      keycloak/exceptions.py

1
.gitignore

@ -102,3 +102,4 @@ ENV/
.idea/
main.py
s3air-authz-config.json

8
.travis.yml

@ -1,8 +0,0 @@
language: python
python:
- "3.6"
- "pypy"
install:
- pip3 install -r requirements.txt
script:
python3 -m unittest discover

79
keycloak/__init__.py

@ -14,8 +14,8 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from keycloak.authorization import Authorization
from keycloak.exceptions import KeycloakAuthorizationConfigError
from .exceptions import raise_error_from_response, KeycloakGetError, KeycloakSecretNotFound, \
KeycloakRPTNotFound
from .urls_patterns import (
@ -30,34 +30,61 @@ from .urls_patterns import (
)
from .connection import ConnectionManager
from jose import jwt
import json
class Keycloak:
def __init__(self, server_url, client_id, realm_name, client_secret_key=None):
self.client_id = client_id
self.client_secret_key = client_secret_key
self.realm_name = realm_name
self._client_id = client_id
self._client_secret_key = client_secret_key
self._realm_name = realm_name
self.connection = ConnectionManager(base_url=server_url,
self._connection = ConnectionManager(base_url=server_url,
headers={},
timeout=60)
self._authorization = Authorization()
@property
def get_client_id(self):
return self.client_id
def client_id(self):
return self._client_id
@client_id.setter
def client_id(self, value):
self._client_id = value
@property
def get_client_secret_key(self):
return self.client_secret_key
def client_secret_key(self):
return self._client_secret_key
@client_secret_key.setter
def client_secret_key(self, value):
self._client_secret_key = value
@property
def get_realm_name(self):
return self.realm_name
def realm_name(self):
return self._realm_name
@realm_name.setter
def realm_name(self, value):
self._realm_name = value
@property
def get_connection(self):
return self.connection
def connection(self):
return self._connection
@connection.setter
def connection(self, value):
self._connection = value
@property
def authorization(self):
return self._authorization
@authorization.setter
def authorization(self, value):
self._authorization = value
def _add_secret_key(self, payload):
"""
@ -229,3 +256,27 @@ class Keycloak:
return jwt.decode(token, key, algorithms=algorithms,
audience=self.client_id, **kwargs)
def load_authorization_config(self, path):
"""
Load Keycloak settings (authorization)
:param path: settings file (json)
:return:
"""
authorization_file = open(path, 'r')
authorization_json = json.loads(authorization_file.read())
self.authorization.load_config(authorization_json)
authorization_file.close()
def get_permissions(self):
if not self.authorization.policies:
raise KeycloakAuthorizationConfigError(
"Keycloak settings not found. Load Authorization Keycloak settings."
)
return

80
keycloak/authorization/__init__.py

@ -0,0 +1,80 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2017 Marcos Pereira <marcospereira.mpj@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import ast
import json
from keycloak.authorization.permission import Permission
from keycloak.authorization.policy import Policy
from keycloak.authorization.role import Role
class Authorization:
def __init__(self):
self._policies = {}
@property
def policies(self):
return self._policies
@policies.setter
def policies(self, value):
self._policies = value
def load_config(self, data):
"""
:param data:
:return:
"""
for pol in data['policies']:
if pol['type'] == 'role':
policy = Policy(name=pol['name'],
type=pol['type'],
logic=pol['logic'],
decision_strategy=pol['decisionStrategy'])
config_roles = json.loads(pol['config']['roles'])
for role in config_roles:
policy.add_role(Role(name=role['id'],
required=role['required']))
self.policies[policy.name] = policy
if pol['type'] == 'scope':
permission = Permission(name=pol['name'],
type=pol['type'],
logic=pol['logic'],
decision_strategy=pol['decisionStrategy'])
permission.scopes = ast.literal_eval(pol['config']['scopes'])
for policy_name in ast.literal_eval(pol['config']['applyPolicies']):
self.policies[policy_name].add_permission(permission)
if pol['type'] == 'resource':
permission = Permission(name=pol['name'],
type=pol['type'],
logic=pol['logic'],
decision_strategy=pol['decisionStrategy'])
permission.resources = ast.literal_eval(pol['config']['resources'])
for policy_name in ast.literal_eval(pol['config']['applyPolicies']):
self.policies[policy_name].add_permission(permission)

82
keycloak/authorization/permission.py

@ -0,0 +1,82 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2017 Marcos Pereira <marcospereira.mpj@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
class Permission:
def __init__(self, name, type, logic, decision_strategy):
self._name = name
self._type = type
self._logic = logic
self._decision_strategy = decision_strategy
self._resources = []
self._scopes = []
def __repr__(self):
return "<Permission: %s (%s)>" % (self.name, self.type)
def __str__(self):
return "Permission: %s (%s)" % (self.name, self.type)
@property
def name(self):
return self._name
@name.setter
def name(self, value):
self._name = value
@property
def type(self):
return self._type
@type.setter
def type(self, value):
self._type = value
@property
def logic(self):
return self._logic
@logic.setter
def logic(self, value):
self._logic = value
@property
def decision_strategy(self):
return self._decision_strategy
@decision_strategy.setter
def decision_strategy(self, value):
self._decision_strategy = value
@property
def resources(self):
return self._resources
@resources.setter
def resources(self, value):
self._resources = value
@property
def scopes(self):
return self._scopes
@scopes.setter
def scopes(self, value):
self._scopes = value

84
keycloak/authorization/policy.py

@ -0,0 +1,84 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2017 Marcos Pereira <marcospereira.mpj@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from keycloak.exceptions import KeycloakAuthorizationConfigError
class Policy:
def __init__(self, name, type, logic, decision_strategy):
self._name = name
self._type = type
self._logic = logic
self._decision_strategy = decision_strategy
self._roles = []
self._permissions = []
def __repr__(self):
return "<Policy: %s (%s)>" % (self.name, self.type)
def __str__(self):
return "Policy: %s (%s)" % (self.name, self.type)
@property
def name(self):
return self._name
@name.setter
def name(self, value):
self._name = value
@property
def type(self):
return self._type
@type.setter
def type(self, value):
self._type = value
@property
def logic(self):
return self._logic
@logic.setter
def logic(self, value):
self._logic = value
@property
def decision_strategy(self):
return self._decision_strategy
@decision_strategy.setter
def decision_strategy(self, value):
self._decision_strategy = value
@property
def roles(self):
return self._roles
@property
def permissions(self):
return self._permissions
def add_role(self, role):
if self.type != 'role':
raise KeycloakAuthorizationConfigError(
"Can't add role. Policy type is different of role")
self._roles.append(role)
def add_permission(self, permission):
self._permissions.append(permission)

27
keycloak/authorization/role.py

@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2017 Marcos Pereira <marcospereira.mpj@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
class Role:
def __init__(self, name, required=False):
self.name = name
self.required = required
@property
def get_name(self):
return self.name

35
keycloak/connection.py

@ -33,26 +33,41 @@ class ConnectionManager(object):
"""
def __init__(self, base_url, headers={}, timeout=60):
self.base_url = base_url
self.headers = headers
self.timeout = timeout
self._base_url = base_url
self._headers = headers
self._timeout = timeout
@property
def get_base_url(self):
def base_url(self):
""" Return base url in use for requests to the server. """
return self.base_url
return self._base_url
@base_url.setter
def base_url(self, value):
""" """
self._base_url = value
@property
def get_timeout(self):
def timeout(self):
""" Return timeout in use for request to the server. """
return self.timeout
return self._timeout
@timeout.setter
def timeout(self, value):
""" """
self._timeout = value
@property
def get_headers(self):
def headers(self):
""" Return header request to the server. """
return self.headers
return self._headers
@headers.setter
def headers(self, value):
""" """
self._headers = value
def get_param_headers(self, key):
def param_headers(self, key):
""" Return a specific header parameter.
:arg
key (str): Header parameters key.

4
keycloak/exceptions.py

@ -76,6 +76,10 @@ class KeycloakRPTNotFound(KeycloakOperationError):
pass
class KeycloakAuthorizationConfigError(KeycloakOperationError):
pass
def raise_error_from_response(response, error, expected_code=200):
if expected_code == response.status_code:

Loading…
Cancel
Save