diff --git a/.gitignore b/.gitignore
index 374f13c..3f4c507 100644
--- a/.gitignore
+++ b/.gitignore
@@ -102,3 +102,4 @@ ENV/
.idea/
main.py
+s3air-authz-config.json
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 18d1e36..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-language: python
-python:
- - "3.6"
- - "pypy"
-install:
- - pip3 install -r requirements.txt
-script:
- python3 -m unittest discover
diff --git a/keycloak/__init__.py b/keycloak/__init__.py
index 722a666..79e3761 100644
--- a/keycloak/__init__.py
+++ b/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 .
-
-
+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
+
+
+
diff --git a/keycloak/authorization/__init__.py b/keycloak/authorization/__init__.py
new file mode 100644
index 0000000..e5ab8bd
--- /dev/null
+++ b/keycloak/authorization/__init__.py
@@ -0,0 +1,80 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2017 Marcos Pereira
+#
+# 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 .
+
+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)
+
diff --git a/keycloak/authorization/permission.py b/keycloak/authorization/permission.py
new file mode 100644
index 0000000..d67e396
--- /dev/null
+++ b/keycloak/authorization/permission.py
@@ -0,0 +1,82 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2017 Marcos Pereira
+#
+# 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 .
+
+
+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 "" % (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
+
diff --git a/keycloak/authorization/policy.py b/keycloak/authorization/policy.py
new file mode 100644
index 0000000..2abdf5e
--- /dev/null
+++ b/keycloak/authorization/policy.py
@@ -0,0 +1,84 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2017 Marcos Pereira
+#
+# 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 .
+
+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 "" % (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)
diff --git a/keycloak/authorization/role.py b/keycloak/authorization/role.py
new file mode 100644
index 0000000..ff7efde
--- /dev/null
+++ b/keycloak/authorization/role.py
@@ -0,0 +1,27 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2017 Marcos Pereira
+#
+# 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 .
+
+
+class Role:
+
+ def __init__(self, name, required=False):
+ self.name = name
+ self.required = required
+
+ @property
+ def get_name(self):
+ return self.name
diff --git a/keycloak/connection.py b/keycloak/connection.py
index 43781e4..8a1bd55 100644
--- a/keycloak/connection.py
+++ b/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.
diff --git a/keycloak/exceptions.py b/keycloak/exceptions.py
index a9a7313..0ce69bb 100644
--- a/keycloak/exceptions.py
+++ b/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: