diff --git a/README.md b/README.md index f70e469..3a8f9e6 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ -# OpenG2P Security +# OpenG2P Security ### (_Alpha version_) ### -This repo contains modules for the encryption of the registry. Refer to [OpenG2P Docs](https://docs.openg2p.org/v/1.1). +This repo contains modules for the encryption of the registry. Refer to [OpenG2P Docs](https://docs.openg2p.org/). @@ -16,8 +16,10 @@ Available addons ---------------- addon | version | maintainers | summary --- | --- | --- | --- -[g2p_encryption](g2p_encryption/) | 15.0.1.2.0 | | G2P: Encryption -[g2p_registry_encryption](g2p_registry_encryption/) | 15.0.1.2.0 | | G2P:Registry Encryption +[g2p_encryption](g2p_encryption/) | 15.0.1.2.0 | | G2P Encryption: Base +[g2p_encryption_keymanager](g2p_encryption_keymanager/) | 15.0.1.2.0 | | G2P Encryption: Keymanager +[g2p_encryption_rest_api](g2p_encryption_rest_api/) | 15.0.1.2.0 | | G2P Encryption: Rest API +[g2p_registry_encryption](g2p_registry_encryption/) | 15.0.1.2.0 | | G2P Registry: Encryption [//]: # (end addons) diff --git a/g2p_encryption/README.rst b/g2p_encryption/README.rst index bfefbff..9c8391a 100644 --- a/g2p_encryption/README.rst +++ b/g2p_encryption/README.rst @@ -1,14 +1,11 @@ -=============== -G2P: Encryption -=============== +==================== +G2P Encryption: Base +==================== -.. - !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:04f5f0d02e2f5b06062b0403ad80dc8ba932f2489ce2e21cd6aeb2ca30e8d3da - !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png :target: https://odoo-community.org/page/development-status @@ -17,9 +14,9 @@ G2P: Encryption :target: https://github.com/OpenG2P/openg2p-security/tree/15.0-develop/g2p_encryption :alt: OpenG2P/openg2p-security -|badge1| |badge2| +|badge1| |badge2| -OpenG2P Encryption +OpenG2P Encryption: Base .. IMPORTANT:: This is an alpha version, the data model and design can change at any time without warning. @@ -36,7 +33,7 @@ Bug Tracker Bugs are tracked on `GitHub Issues `_. In case of trouble, please check there if your issue has already been reported. -If you spotted it first, help us to smash it by providing a detailed and welcomed +If you spotted it first, help us smashing it by providing a detailed and welcomed `feedback `_. Do not contact contributors directly about support or help with technical issues. diff --git a/g2p_encryption/__manifest__.py b/g2p_encryption/__manifest__.py index 87bd470..d91e065 100644 --- a/g2p_encryption/__manifest__.py +++ b/g2p_encryption/__manifest__.py @@ -1,5 +1,5 @@ { - "name": "G2P: Encryption", + "name": "G2P Encryption: Base", "category": "G2P", "version": "15.0.1.2.0", "sequence": 1, @@ -8,8 +8,12 @@ "license": "Other OSI approved licence", "development_status": "Alpha", "depends": [], - "external_dependencies": {"python": ["pycrypto"]}, - "data": [], + "data": [ + "security/groups.xml", + "security/ir.model.access.csv", + "views/encryption_provider.xml", + "data/default_provider.xml", + ], "assets": { "web.assets_backend": [], "web.assets_qweb": [], diff --git a/g2p_encryption/__pycache__/__init__.cpython-310.pyc b/g2p_encryption/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index affb012..0000000 Binary files a/g2p_encryption/__pycache__/__init__.cpython-310.pyc and /dev/null differ diff --git a/g2p_encryption/data/default_provider.xml b/g2p_encryption/data/default_provider.xml new file mode 100644 index 0000000..ac42138 --- /dev/null +++ b/g2p_encryption/data/default_provider.xml @@ -0,0 +1,9 @@ + + + + + Default Encryption Provider + + diff --git a/g2p_encryption/models/__init__.py b/g2p_encryption/models/__init__.py index a2795d2..56c044c 100644 --- a/g2p_encryption/models/__init__.py +++ b/g2p_encryption/models/__init__.py @@ -1 +1 @@ -from . import crypto +from . import encryption_provider diff --git a/g2p_encryption/models/__pycache__/__init__.cpython-310.pyc b/g2p_encryption/models/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index ca804e6..0000000 Binary files a/g2p_encryption/models/__pycache__/__init__.cpython-310.pyc and /dev/null differ diff --git a/g2p_encryption/models/__pycache__/crypto.cpython-310.pyc b/g2p_encryption/models/__pycache__/crypto.cpython-310.pyc deleted file mode 100644 index d178107..0000000 Binary files a/g2p_encryption/models/__pycache__/crypto.cpython-310.pyc and /dev/null differ diff --git a/g2p_encryption/models/__pycache__/json_field.cpython-310.pyc b/g2p_encryption/models/__pycache__/json_field.cpython-310.pyc deleted file mode 100644 index ec1dce7..0000000 Binary files a/g2p_encryption/models/__pycache__/json_field.cpython-310.pyc and /dev/null differ diff --git a/g2p_encryption/models/__pycache__/partner.cpython-310.pyc b/g2p_encryption/models/__pycache__/partner.cpython-310.pyc deleted file mode 100644 index 8ad0f0b..0000000 Binary files a/g2p_encryption/models/__pycache__/partner.cpython-310.pyc and /dev/null differ diff --git a/g2p_encryption/models/crypto.py b/g2p_encryption/models/crypto.py deleted file mode 100644 index a9b6bef..0000000 --- a/g2p_encryption/models/crypto.py +++ /dev/null @@ -1,34 +0,0 @@ -import base64 -import hashlib -import logging - -from Crypto import Random -from Crypto.Cipher import AES - -_logger = logging.getLogger(__name__) - - -class AESCipher(object): - def __init__(self, key): - self.bs = AES.block_size - self.key = hashlib.sha256(key.encode()).digest() - - def encrypt(self, raw): - raw = self._pad(raw) - iv = Random.new().read(AES.block_size) - cipher = AES.new(self.key, AES.MODE_CBC, iv) - return base64.b64encode(iv + cipher.encrypt(raw.encode())) - - def decrypt(self, enc): - _logger.info("ENC String:", enc) - enc = base64.b64decode(enc) - iv = enc[: AES.block_size] - cipher = AES.new(self.key, AES.MODE_CBC, iv) - return self._unpad(cipher.decrypt(enc[AES.block_size :])).decode("utf-8") - - def _pad(self, s): - return s + (self.bs - len(s) % self.bs) * chr(self.bs - len(s) % self.bs) - - @staticmethod - def _unpad(s): - return s[: -ord(s[len(s) - 1 :])] diff --git a/g2p_encryption/models/encryption_provider.py b/g2p_encryption/models/encryption_provider.py new file mode 100644 index 0000000..91f4522 --- /dev/null +++ b/g2p_encryption/models/encryption_provider.py @@ -0,0 +1,63 @@ +from odoo import fields, models + + +class G2PEncryptionProvider(models.Model): + _name = "g2p.encryption.provider" + _description = "G2P Encryption Provider" + + name = fields.Char(required=True) + type = fields.Selection(selection=[]) + + def encrypt_data(self, data: bytes, **kwargs) -> bytes: + """ + Both input and output are NOT base64 encoded + """ + try: + encrypt_func = getattr(self, f"encrypt_data_{self.type}") + except Exception as e: + raise NotImplementedError() from e + return encrypt_func(data, **kwargs) + + def decrypt_data(self, data: bytes, **kwargs) -> bytes: + """ + Both input and output are NOT base64 encoded + """ + try: + decrypt_func = getattr(self, f"decrypt_data_{self.type}") + except Exception as e: + raise NotImplementedError() from e + return decrypt_func(data, **kwargs) + + def jwt_sign( + self, + data, + include_payload=True, + include_certificate=False, + include_cert_hash=False, + **kwargs, + ) -> str: + try: + jwt_func = getattr(self, f"jwt_sign_{self.type}") + except Exception as e: + raise NotImplementedError() from e + return jwt_func( + data, + include_payload=True, + include_certificate=False, + include_cert_hash=False, + **kwargs, + ) + + def jwt_verify(self, data: str, **kwargs): + try: + jwt_func = getattr(self, f"jwt_verify_{self.type}") + except Exception as e: + raise NotImplementedError() from e + return jwt_func(data, **kwargs) + + def get_jwks(self, **kwargs): + try: + jwk_func = getattr(self, f"get_jwks_{self.type}") + except Exception as e: + raise NotImplementedError() from e + return jwk_func(**kwargs) diff --git a/g2p_encryption/readme/DESCRIPTION.rst b/g2p_encryption/readme/DESCRIPTION.rst index 4266c24..faa2b08 100644 --- a/g2p_encryption/readme/DESCRIPTION.rst +++ b/g2p_encryption/readme/DESCRIPTION.rst @@ -1 +1 @@ -OpenG2P Encryption +OpenG2P Encryption: Base diff --git a/g2p_encryption/security/groups.xml b/g2p_encryption/security/groups.xml new file mode 100644 index 0000000..6a53fb3 --- /dev/null +++ b/g2p_encryption/security/groups.xml @@ -0,0 +1,7 @@ + + + + Crypto Admin + + + diff --git a/g2p_encryption/security/ir.model.access.csv b/g2p_encryption/security/ir.model.access.csv new file mode 100644 index 0000000..5c445d8 --- /dev/null +++ b/g2p_encryption/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +encryption_provider_crypto_admin,Encryption Provider Crypto Admin,g2p_encryption.model_g2p_encryption_provider,g2p_encryption.crypto_admin,1,1,1,1 diff --git a/g2p_encryption/static/description/index.html b/g2p_encryption/static/description/index.html index dac1b0a..0d6bd2f 100644 --- a/g2p_encryption/static/description/index.html +++ b/g2p_encryption/static/description/index.html @@ -3,18 +3,18 @@ - -G2P: Encryption + +G2P Encryption: Base -
-

G2P: Encryption

+
+

G2P Encryption: Base

-

Alpha OpenG2P/openg2p-security

-

OpenG2P Encryption

+

Alpha OpenG2P/openg2p-security

+

OpenG2P Encryption: Base

Important

This is an alpha version, the data model and design can change at any time without warning. @@ -380,32 +378,32 @@

G2P: Encryption

Table of contents

-

Bug Tracker

+

Bug Tracker

Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. -If you spotted it first, help us to smash it by providing a detailed and welcomed +If you spotted it first, help us smashing it by providing a detailed and welcomed feedback.

Do not contact contributors directly about support or help with technical issues.

-

Credits

+

Credits

-

Authors

+

Authors

  • OpenG2P
-

Maintainers

+

Maintainers

This module is part of the OpenG2P/openg2p-security project on GitHub.

You are welcome to contribute.

diff --git a/g2p_encryption/views/encryption_provider.xml b/g2p_encryption/views/encryption_provider.xml new file mode 100644 index 0000000..a5824fb --- /dev/null +++ b/g2p_encryption/views/encryption_provider.xml @@ -0,0 +1,47 @@ + + + + + view_encryption_provider_list_tree + g2p.encryption.provider + 1 + + + + + + + + + + view_encryption_provider_form + g2p.encryption.provider + 1 + +
+ + + + +
+
+
+ + + Encryption Provider + g2p.encryption.provider + tree,form + Manage encryption providers. + + + +
diff --git a/g2p_encryption_keymanager/README.rst b/g2p_encryption_keymanager/README.rst new file mode 100644 index 0000000..006f844 --- /dev/null +++ b/g2p_encryption_keymanager/README.rst @@ -0,0 +1,54 @@ +========================== +G2P Encryption: Keymanager +========================== + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png + :target: https://odoo-community.org/page/development-status + :alt: Alpha +.. |badge2| image:: https://img.shields.io/badge/github-OpenG2P%2Fopeng2p--security-lightgray.png?logo=github + :target: https://github.com/OpenG2P/openg2p-security/tree/15.0-develop/g2p_encryption_keymanager + :alt: OpenG2P/openg2p-security + +|badge1| |badge2| + +OpenG2P Encryption With Keymanager + +.. IMPORTANT:: + This is an alpha version, the data model and design can change at any time without warning. + Only for development or testing purpose, do not use in production. + `More details on development status `_ + +**Table of contents** + +.. contents:: + :local: + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* OpenG2P + +Maintainers +~~~~~~~~~~~ + +This module is part of the `OpenG2P/openg2p-security `_ project on GitHub. + +You are welcome to contribute. diff --git a/g2p_encryption_keymanager/__init__.py b/g2p_encryption_keymanager/__init__.py new file mode 100644 index 0000000..0650744 --- /dev/null +++ b/g2p_encryption_keymanager/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/g2p_encryption_keymanager/__manifest__.py b/g2p_encryption_keymanager/__manifest__.py new file mode 100644 index 0000000..ddd6d05 --- /dev/null +++ b/g2p_encryption_keymanager/__manifest__.py @@ -0,0 +1,27 @@ +{ + "name": "G2P Encryption: Keymanager", + "category": "G2P", + "version": "15.0.1.2.0", + "sequence": 1, + "author": "OpenG2P", + "website": "https://openg2p.org", + "license": "Other OSI approved licence", + "development_status": "Alpha", + "depends": [ + "g2p_encryption", + ], + "external_dependencies": {"python": ["cryptography<37", "jwcrypto", "python-jose"]}, + "data": [ + "views/encryption_provider.xml", + "data/default_provider.xml", + ], + "assets": { + "web.assets_backend": [], + "web.assets_qweb": [], + }, + "demo": [], + "images": [], + "application": False, + "installable": True, + "auto_install": False, +} diff --git a/g2p_encryption_keymanager/data/default_provider.xml b/g2p_encryption_keymanager/data/default_provider.xml new file mode 100644 index 0000000..db04bdb --- /dev/null +++ b/g2p_encryption_keymanager/data/default_provider.xml @@ -0,0 +1,10 @@ + + + + + Default Keymanager Encryption Provider + keymanager + + diff --git a/g2p_encryption_keymanager/models/__init__.py b/g2p_encryption_keymanager/models/__init__.py new file mode 100644 index 0000000..56c044c --- /dev/null +++ b/g2p_encryption_keymanager/models/__init__.py @@ -0,0 +1 @@ +from . import encryption_provider diff --git a/g2p_encryption_keymanager/models/encryption_provider.py b/g2p_encryption_keymanager/models/encryption_provider.py new file mode 100644 index 0000000..31df0a1 --- /dev/null +++ b/g2p_encryption_keymanager/models/encryption_provider.py @@ -0,0 +1,304 @@ +# pylint: disable=[W7936] + +import base64 +import json +import logging +import os +import secrets +from datetime import datetime + +import requests +from cryptography import x509 +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.serialization import Encoding +from jose import jwt +from jwcrypto import jwk + +from odoo import api, fields, models + +_logger = logging.getLogger(__name__) + +KEYMANAGER_API_BASE_URL = os.getenv( + "KEYMANAGER_API_BASE_URL", "http://keymanager.keymanager/v1/keymanager" +) +KEYMANAGER_AUTH_URL = os.getenv( + "KEYMANAGER_AUTH_URL", + "http://keycloak.keycloak/realms/openg2p/protocol/openid-connect/token", +) +KEYMANAGER_AUTH_CLIENT_ID = os.getenv( + "KEYMANAGER_AUTH_CLIENT_ID", "openg2p-admin-client" +) +KEYMANAGER_AUTH_CLIENT_SECRET = os.getenv("KEYMANAGER_AUTH_CLIENT_SECRET", "") +KEYMANAGER_AUTH_GRANT_TYPE = os.getenv( + "KEYMANAGER_AUTH_GRANT_TYPE", "client_credentials" +) + + +class KeymanagerEncryptionProvider(models.Model): + _inherit = "g2p.encryption.provider" + + type = fields.Selection(selection_add=[("keymanager", "Keymanager")]) + + @api.model + def _km_random_secret(self): + return secrets.token_urlsafe() + + @api.model + def km_generate_current_time(self): + return f'{datetime.utcnow().isoformat(timespec = "milliseconds")}Z' + + keymanager_api_base_url = fields.Char(default=KEYMANAGER_API_BASE_URL) + keymanager_auth_url = fields.Char(default=KEYMANAGER_AUTH_URL) + keymanager_auth_client_id = fields.Char(default=KEYMANAGER_AUTH_CLIENT_ID) + keymanager_auth_client_secret = fields.Char(default=KEYMANAGER_AUTH_CLIENT_SECRET) + keymanager_auth_grant_type = fields.Char(default=KEYMANAGER_AUTH_GRANT_TYPE) + + keymanager_access_token = fields.Char() + keymanager_access_token_expiry = fields.Datetime() + + keymanager_encrypt_application_id = fields.Char(default="REGISTRATION") + keymanager_encrypt_reference_id = fields.Char(default="ENCRYPT") + + keymanager_sign_application_id = fields.Char(default="REGISTRATION") + keymanager_sign_reference_id = fields.Char(default="") + + keymanager_encrypt_salt = fields.Char(default=_km_random_secret) + keymanager_encrypt_aad = fields.Char(default=_km_random_secret) + + def encrypt_data_keymanager(self, data: bytes, **kwargs) -> bytes: + self.ensure_one() + access_token = self.km_get_access_token() + current_time = self.km_generate_current_time() + url = f"{self.keymanager_api_base_url}/encrypt" + headers = {"Cookie": f"Authorization={access_token}"} + payload = { + "id": "string", + "version": "string", + "requesttime": current_time, + "metadata": {}, + "request": { + "applicationId": self.keymanager_encrypt_application_id or "", + "referenceId": self.keymanager_encrypt_reference_id or "", + "timeStamp": current_time, + "data": self.km_urlsafe_b64encode(data), + "salt": self.keymanager_encrypt_salt, + "aad": self.keymanager_encrypt_aad, + }, + } + response = requests.post(url, json=payload, headers=headers) + _logger.debug("Keymanager Encrypt API response: %s", response.text) + response.raise_for_status() + if response: + response = response.json() + if response: + response = response.get("response") + if response: + return self.km_urlsafe_b64decode(response.get("data")) + raise ValueError("Could not encrypt data, invalid keymanager response") + + def decrypt_data_keymanager(self, data: bytes, **kwargs) -> bytes: + self.ensure_one() + access_token = self.km_get_access_token() + current_time = self.km_generate_current_time() + url = f"{self.keymanager_api_base_url}/decrypt" + headers = {"Cookie": f"Authorization={access_token}"} + payload = { + "id": "string", + "version": "string", + "requesttime": current_time, + "metadata": {}, + "request": { + "applicationId": self.keymanager_encrypt_application_id or "", + "referenceId": self.keymanager_encrypt_reference_id or "", + "timeStamp": current_time, + "data": self.km_urlsafe_b64encode(data), + "salt": self.keymanager_encrypt_salt, + "aad": self.keymanager_encrypt_aad, + }, + } + response = requests.post(url, json=payload, headers=headers) + _logger.debug("Keymanager Decrypt API response: %s", response.text) + response.raise_for_status() + if response: + response = response.json() + if response: + response = response.get("response") + if response: + return self.km_urlsafe_b64decode(response.get("data")) + raise ValueError("Could not decrypt data, invalid keymanager response") + + def jwt_sign_keymanager( + self, + data, + include_payload=True, + include_certificate=False, + include_cert_hash=False, + **kwargs, + ) -> str: + self.ensure_one() + if isinstance(data, dict): + data = json.dumps(data).encode() + elif isinstance(data, str): + data = data.encode() + + access_token = self.km_get_access_token() + current_time = self.km_generate_current_time() + url = f"{self.keymanager_api_base_url}/jwtSign" + headers = {"Cookie": f"Authorization={access_token}"} + payload = { + "id": "string", + "version": "string", + "requesttime": current_time, + "metadata": {}, + "request": { + "dataToSign": self.km_urlsafe_b64encode(data), + "applicationId": self.keymanager_sign_application_id or "", + "referenceId": self.keymanager_sign_reference_id or "", + "includePayload": include_payload, + "includeCertificate": include_certificate, + "includeCertHash": include_cert_hash, + }, + } + response = requests.post(url, json=payload, headers=headers) + _logger.debug("Keymanager JWT Sign API response: %s", response.text) + response.raise_for_status() + if response: + response = response.json() + if response: + response = response.get("response", {}) + if response: + return response.get("jwtSignedData") + raise ValueError("Could not sign jwt, invalid keymanager response") + + def jwt_verify_keymanager(self, data: str, **kwargs): + self.ensure_one() + access_token = self.km_get_access_token() + current_time = self.km_generate_current_time() + url = f"{self.keymanager_api_base_url}/jwtVerify" + headers = {"Cookie": f"Authorization={access_token}"} + payload = { + "id": "string", + "version": "string", + "requesttime": current_time, + "metadata": {}, + "request": { + "jwtSignatureData": data, + "applicationId": self.keymanager_sign_application_id or "", + "referenceId": self.keymanager_sign_reference_id or "", + "validateTrust": False, + }, + } + response = requests.post(url, json=payload, headers=headers) + _logger.debug("Keymanager JWT Verify API response: %s", response.text) + response.raise_for_status() + if response: + response = response.json() + if response: + response = response.get("response", {}) + if response: + response = response.get("signatureValid", False) + else: + raise ValueError("Could not verify jwt, invalid keymanager response") + if response: + return jwt.get_unverified_claims(data) + raise ValueError("invalid jwt signature") + + def get_jwks_keymanager(self, **kwargs): + # TODO: Cache this JWKS response somehow + self.ensure_one() + access_token = self.km_get_access_token() + jwks = [] + for app_id, ref_id, use in ( + ( + self.keymanager_encrypt_application_id or "", + self.keymanager_encrypt_reference_id or "", + "enc", + ), + ( + self.keymanager_sign_application_id or "", + self.keymanager_sign_reference_id or "", + "sig", + ), + ): + url = f"{self.keymanager_api_base_url}/getAllCertificates" + if self.keymanager_sign_application_id: + url += f"?applicationId={app_id}" + if self.keymanager_sign_reference_id: + url += f"&referenceId={ref_id}" + headers = {"Cookie": f"Authorization={access_token}"} + response = requests.get(url, headers=headers) + _logger.debug("Keymanager get Certificate API response: %s", response.text) + response.raise_for_status() + certs = response.json().get("response", {}).get("allCertificates", []) + for cert in certs: + jwks.append( + self.km_convert_x509_pem_to_jwk( + cert.get("certificateData", "").encode(), + use=use, + kid=cert.get("keyId"), + ) + ) + return {"keys": jwks} + + @api.model + def km_convert_x509_pem_to_jwk(self, cert: bytes, use=None, kid=None): + x509_cert = x509.load_pem_x509_certificate(cert) + public_key = x509_cert.public_key() + new = jwk.JWK() + new.import_from_pyca(public_key) + new.update( + { + "x5c": [ + base64.b64encode(x509_cert.public_bytes(Encoding.DER)).decode() + ], + "x5t": self.km_urlsafe_b64encode(x509_cert.fingerprint(hashes.SHA1())), + "x5t#S256": self.km_urlsafe_b64encode( + x509_cert.fingerprint(hashes.SHA256()) + ), + } + ) + if kid: + new["kid"] = kid + if use: + new["use"] = use + return dict(new) + + def km_get_access_token(self): + self.ensure_one() + if ( + self.keymanager_access_token + and self.keymanager_access_token_expiry + and self.keymanager_access_token_expiry > datetime.utcnow() + ): + return self.keymanager_access_token + data = { + "client_id": self.keymanager_auth_client_id, + "client_secret": self.keymanager_auth_client_secret, + "grant_type": self.keymanager_auth_grant_type, + } + response = requests.post(self.keymanager_auth_url, data=data) + _logger.debug("Keymanager get Certificates API response: %s", response.text) + response.raise_for_status() + access_token = response.json().get("access_token", None) + token_exp = jwt.get_unverified_claims(access_token).get("exp") + self.write( + { + "keymanager_access_token": access_token, + "keymanager_access_token_expiry": datetime.fromtimestamp(token_exp) + if isinstance(token_exp, int) + else datetime.fromisoformat(token_exp) + if isinstance(token_exp, str) + else token_exp, + } + ) + return access_token + + @api.model + def km_urlsafe_b64encode(self, input_data: bytes) -> str: + return base64.urlsafe_b64encode(input_data).decode().rstrip("=") + + @api.model + def km_urlsafe_b64decode(self, input_data: str) -> bytes: + return base64.urlsafe_b64decode( + input_data.encode() + b"=" * (-len(input_data) % 4) + ) diff --git a/g2p_encryption_keymanager/readme/DESCRIPTION.rst b/g2p_encryption_keymanager/readme/DESCRIPTION.rst new file mode 100644 index 0000000..7078bb9 --- /dev/null +++ b/g2p_encryption_keymanager/readme/DESCRIPTION.rst @@ -0,0 +1 @@ +OpenG2P Encryption With Keymanager diff --git a/g2p_encryption_keymanager/static/description/icon.png b/g2p_encryption_keymanager/static/description/icon.png new file mode 100644 index 0000000..5ecb429 Binary files /dev/null and b/g2p_encryption_keymanager/static/description/icon.png differ diff --git a/g2p_encryption_keymanager/static/description/index.html b/g2p_encryption_keymanager/static/description/index.html new file mode 100644 index 0000000..eef0aa2 --- /dev/null +++ b/g2p_encryption_keymanager/static/description/index.html @@ -0,0 +1,413 @@ + + + + + + +G2P Encryption: Keymanager + + + +
+

G2P Encryption: Keymanager

+ + +

Alpha OpenG2P/openg2p-security

+

OpenG2P Encryption With Keymanager

+
+

Important

+

This is an alpha version, the data model and design can change at any time without warning. +Only for development or testing purpose, do not use in production. +More details on development status

+
+

Table of contents

+ +
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • OpenG2P
  • +
+
+
+

Maintainers

+

This module is part of the OpenG2P/openg2p-security project on GitHub.

+

You are welcome to contribute.

+
+
+
+ + diff --git a/g2p_encryption_keymanager/views/encryption_provider.xml b/g2p_encryption_keymanager/views/encryption_provider.xml new file mode 100644 index 0000000..7e45fc8 --- /dev/null +++ b/g2p_encryption_keymanager/views/encryption_provider.xml @@ -0,0 +1,41 @@ + + + + + view_keymanager_encryption_provider_form + g2p.encryption.provider + + 2 + +
+ + + + + + + + + + + + + + + + +
+
+
+
diff --git a/g2p_encryption_rest_api/README.rst b/g2p_encryption_rest_api/README.rst new file mode 100644 index 0000000..beae759 --- /dev/null +++ b/g2p_encryption_rest_api/README.rst @@ -0,0 +1,54 @@ +======================== +G2P Encryption: Rest API +======================== + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png + :target: https://odoo-community.org/page/development-status + :alt: Alpha +.. |badge2| image:: https://img.shields.io/badge/github-OpenG2P%2Fopeng2p--security-lightgray.png?logo=github + :target: https://github.com/OpenG2P/openg2p-security/tree/15.0-develop/g2p_encryption_rest_api + :alt: OpenG2P/openg2p-security + +|badge1| |badge2| + +OpenG2P Encryption REST API + +.. IMPORTANT:: + This is an alpha version, the data model and design can change at any time without warning. + Only for development or testing purpose, do not use in production. + `More details on development status `_ + +**Table of contents** + +.. contents:: + :local: + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* OpenG2P + +Maintainers +~~~~~~~~~~~ + +This module is part of the `OpenG2P/openg2p-security `_ project on GitHub. + +You are welcome to contribute. diff --git a/g2p_encryption_rest_api/__init__.py b/g2p_encryption_rest_api/__init__.py new file mode 100644 index 0000000..d6d6244 --- /dev/null +++ b/g2p_encryption_rest_api/__init__.py @@ -0,0 +1,2 @@ +from . import controllers +from . import services diff --git a/g2p_encryption_rest_api/__manifest__.py b/g2p_encryption_rest_api/__manifest__.py new file mode 100644 index 0000000..7d588de --- /dev/null +++ b/g2p_encryption_rest_api/__manifest__.py @@ -0,0 +1,21 @@ +{ + "name": "G2P Encryption: Rest API", + "category": "G2P", + "version": "15.0.1.2.0", + "sequence": 1, + "author": "OpenG2P", + "website": "https://openg2p.org", + "license": "Other OSI approved licence", + "development_status": "Alpha", + "depends": ["g2p_encryption", "base_rest"], + "data": [], + "assets": { + "web.assets_backend": [], + "web.assets_qweb": [], + }, + "demo": [], + "images": [], + "application": False, + "installable": True, + "auto_install": False, +} diff --git a/g2p_encryption_rest_api/controllers/__init__.py b/g2p_encryption_rest_api/controllers/__init__.py new file mode 100644 index 0000000..12a7e52 --- /dev/null +++ b/g2p_encryption_rest_api/controllers/__init__.py @@ -0,0 +1 @@ +from . import main diff --git a/g2p_encryption_rest_api/controllers/main.py b/g2p_encryption_rest_api/controllers/main.py new file mode 100644 index 0000000..aa8710b --- /dev/null +++ b/g2p_encryption_rest_api/controllers/main.py @@ -0,0 +1,7 @@ +from odoo.addons.base_rest.controllers.main import RestController + + +class SecurityController(RestController): + _root_path = "/api/v1/security/" + _collection_name = "base.rest.security.services" + _default_auth = "user" diff --git a/g2p_encryption_rest_api/readme/DESCRIPTION.rst b/g2p_encryption_rest_api/readme/DESCRIPTION.rst new file mode 100644 index 0000000..d1c072e --- /dev/null +++ b/g2p_encryption_rest_api/readme/DESCRIPTION.rst @@ -0,0 +1 @@ +OpenG2P Encryption REST API diff --git a/g2p_encryption_rest_api/services/__init__.py b/g2p_encryption_rest_api/services/__init__.py new file mode 100644 index 0000000..403ae06 --- /dev/null +++ b/g2p_encryption_rest_api/services/__init__.py @@ -0,0 +1 @@ +from . import well_known diff --git a/g2p_encryption_rest_api/services/well_known.py b/g2p_encryption_rest_api/services/well_known.py new file mode 100644 index 0000000..acb728a --- /dev/null +++ b/g2p_encryption_rest_api/services/well_known.py @@ -0,0 +1,40 @@ +import logging + +from odoo.addons.base_rest import restapi +from odoo.addons.component.core import Component + +_logger = logging.getLogger(__name__) + + +class WellknownRestService(Component): + _name = "security_well_known.rest.service" + _inherit = ["base.rest.service"] + _usage = ".well-known" + _collection = "base.rest.security.services" + _description = """ + Security Well-Known API Services + """ + + @restapi.method( + [ + ( + [ + "/jwks.json", + ], + "GET", + ) + ], + auth="public", + ) + def get_jwks(self): + encryption_providers = self.env["g2p.encryption.provider"].sudo().search([]) + jwks = [] + for prov in encryption_providers: + try: + prov_jwks = prov.get_jwks() + jwks.extend(prov_jwks.get("keys", []) if prov_jwks else []) + except Exception: + _logger.exception( + "Unable to get JWKS from list of encryption providers" + ) + return {"keys": jwks} diff --git a/g2p_encryption_rest_api/static/description/icon.png b/g2p_encryption_rest_api/static/description/icon.png new file mode 100644 index 0000000..5ecb429 Binary files /dev/null and b/g2p_encryption_rest_api/static/description/icon.png differ diff --git a/g2p_encryption_rest_api/static/description/index.html b/g2p_encryption_rest_api/static/description/index.html new file mode 100644 index 0000000..7804e98 --- /dev/null +++ b/g2p_encryption_rest_api/static/description/index.html @@ -0,0 +1,413 @@ + + + + + + +G2P Encryption: Rest API + + + +
+

G2P Encryption: Rest API

+ + +

Alpha OpenG2P/openg2p-security

+

OpenG2P Encryption REST API

+
+

Important

+

This is an alpha version, the data model and design can change at any time without warning. +Only for development or testing purpose, do not use in production. +More details on development status

+
+

Table of contents

+ +
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • OpenG2P
  • +
+
+
+

Maintainers

+

This module is part of the OpenG2P/openg2p-security project on GitHub.

+

You are welcome to contribute.

+
+
+
+ + diff --git a/g2p_registry_encryption/README.rst b/g2p_registry_encryption/README.rst index 40a1f9d..d1fc794 100644 --- a/g2p_registry_encryption/README.rst +++ b/g2p_registry_encryption/README.rst @@ -1,14 +1,11 @@ -======================= -G2P:Registry Encryption -======================= +======================== +G2P Registry: Encryption +======================== -.. - !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:f327c3d41cb609867269ed2e931991410e42861b8794e665b07ba65be1718803 - !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png :target: https://odoo-community.org/page/development-status @@ -17,7 +14,7 @@ G2P:Registry Encryption :target: https://github.com/OpenG2P/openg2p-security/tree/15.0-develop/g2p_registry_encryption :alt: OpenG2P/openg2p-security -|badge1| |badge2| +|badge1| |badge2| OpenG2P Registry Encryption @@ -36,7 +33,7 @@ Bug Tracker Bugs are tracked on `GitHub Issues `_. In case of trouble, please check there if your issue has already been reported. -If you spotted it first, help us to smash it by providing a detailed and welcomed +If you spotted it first, help us smashing it by providing a detailed and welcomed `feedback `_. Do not contact contributors directly about support or help with technical issues. diff --git a/g2p_registry_encryption/__manifest__.py b/g2p_registry_encryption/__manifest__.py index abe7f6d..58cee70 100644 --- a/g2p_registry_encryption/__manifest__.py +++ b/g2p_registry_encryption/__manifest__.py @@ -1,5 +1,5 @@ { - "name": "G2P:Registry Encryption", + "name": "G2P Registry: Encryption", "category": "G2P", "version": "15.0.1.2.0", "sequence": 1, @@ -7,8 +7,12 @@ "website": "https://openg2p.org", "license": "Other OSI approved licence", "development_status": "Alpha", - "depends": ["g2p_encryption", "g2p_registry_base"], - "data": [], + "depends": ["g2p_encryption", "g2p_registry_base", "g2p_registry_individual"], + "data": [ + "data/registry_encryption_provider.xml", + "views/decrypted_partner.xml", + "views/res_config_view.xml", + ], "assets": { "web.assets_backend": [], "web.assets_qweb": [], diff --git a/g2p_registry_encryption/__pycache__/__init__.cpython-310.pyc b/g2p_registry_encryption/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index aee75ff..0000000 Binary files a/g2p_registry_encryption/__pycache__/__init__.cpython-310.pyc and /dev/null differ diff --git a/g2p_registry_encryption/data/registry_encryption_provider.xml b/g2p_registry_encryption/data/registry_encryption_provider.xml new file mode 100644 index 0000000..0d6e7fc --- /dev/null +++ b/g2p_registry_encryption/data/registry_encryption_provider.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/g2p_registry_encryption/models/__init__.py b/g2p_registry_encryption/models/__init__.py index b4a972b..f927713 100644 --- a/g2p_registry_encryption/models/__init__.py +++ b/g2p_registry_encryption/models/__init__.py @@ -1 +1,3 @@ -from . import partner, phone_number, reg_ids +from . import encryption_provider +from . import partner +from . import res_config_settings diff --git a/g2p_registry_encryption/models/__pycache__/__init__.cpython-310.pyc b/g2p_registry_encryption/models/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index 2400c40..0000000 Binary files a/g2p_registry_encryption/models/__pycache__/__init__.cpython-310.pyc and /dev/null differ diff --git a/g2p_registry_encryption/models/__pycache__/crypto.cpython-310.pyc b/g2p_registry_encryption/models/__pycache__/crypto.cpython-310.pyc deleted file mode 100644 index a90b2d3..0000000 Binary files a/g2p_registry_encryption/models/__pycache__/crypto.cpython-310.pyc and /dev/null differ diff --git a/g2p_registry_encryption/models/__pycache__/json_field.cpython-310.pyc b/g2p_registry_encryption/models/__pycache__/json_field.cpython-310.pyc deleted file mode 100644 index ec1dce7..0000000 Binary files a/g2p_registry_encryption/models/__pycache__/json_field.cpython-310.pyc and /dev/null differ diff --git a/g2p_registry_encryption/models/__pycache__/partner.cpython-310.pyc b/g2p_registry_encryption/models/__pycache__/partner.cpython-310.pyc deleted file mode 100644 index 7670298..0000000 Binary files a/g2p_registry_encryption/models/__pycache__/partner.cpython-310.pyc and /dev/null differ diff --git a/g2p_registry_encryption/models/__pycache__/phone_number.cpython-310.pyc b/g2p_registry_encryption/models/__pycache__/phone_number.cpython-310.pyc deleted file mode 100644 index d2a144d..0000000 Binary files a/g2p_registry_encryption/models/__pycache__/phone_number.cpython-310.pyc and /dev/null differ diff --git a/g2p_registry_encryption/models/__pycache__/reg_ids.cpython-310.pyc b/g2p_registry_encryption/models/__pycache__/reg_ids.cpython-310.pyc deleted file mode 100644 index a43a279..0000000 Binary files a/g2p_registry_encryption/models/__pycache__/reg_ids.cpython-310.pyc and /dev/null differ diff --git a/g2p_registry_encryption/models/encryption_provider.py b/g2p_registry_encryption/models/encryption_provider.py new file mode 100644 index 0000000..c407705 --- /dev/null +++ b/g2p_registry_encryption/models/encryption_provider.py @@ -0,0 +1,26 @@ +from odoo import api, models + + +class RegistryEncryptionProvider(models.Model): + _inherit = "g2p.encryption.provider" + + @api.model + def set_registry_provider(self, provider_id, replace=True): + if provider_id and ( + replace + or not self.env["ir.config_parameter"] + .sudo() + .get_param("g2p_registry_encryption.encryption_provider_id", None) + ): + self.env["ir.config_parameter"].sudo().set_param( + "g2p_registry_encryption.encryption_provider_id", str(provider_id) + ) + + @api.model + def get_registry_provider(self): + prov_id = ( + self.env["ir.config_parameter"] + .sudo() + .get_param("g2p_registry_encryption.encryption_provider_id", None) + ) + return self.browse(int(prov_id)) if prov_id else None diff --git a/g2p_registry_encryption/models/partner.py b/g2p_registry_encryption/models/partner.py index ea8b402..c51042a 100644 --- a/g2p_registry_encryption/models/partner.py +++ b/g2p_registry_encryption/models/partner.py @@ -1,125 +1,126 @@ -import logging +import json from odoo import api, fields, models -from odoo.addons.g2p_encryption.models.crypto import AESCipher - -_logger = logging.getLogger(__name__) - class EncryptedPartner(models.Model): _inherit = "res.partner" - # is_encrypted = fields.Boolean("Is encrypted?") - - name_decrypted = fields.Char( - compute=lambda self: self._decrypt_field("name", "name_decrypted"), store=False - ) - family_name_decrypted = fields.Char( - compute=lambda self: self._decrypt_field( - "family_name", "family_name_decrypted" - ), - store=False, - ) - given_name_decrypted = fields.Char( - compute=lambda self: self._decrypt_field("given_name", "given_name_decrypted"), - store=False, - ) - addl_name_decrypted = fields.Char( - compute=lambda self: self._decrypt_field("addl_name", "addl_name_decrypted"), - store=False, - ) - email_decrypted = fields.Char( - compute=lambda self: self._decrypt_field("email", "email_decrypted"), - store=False, - ) - phone_decrypted = fields.Char( - compute=lambda self: self._decrypt_field("phone", "phone_decrypted"), - store=False, - ) - mobile_decrypted = fields.Char( - compute=lambda self: self._decrypt_field("mobile", "mobile_decrypted"), - store=False, - ) - address_decrypted = fields.Char( - compute=lambda self: self._decrypt_field("address", "address_decrypted"), - store=False, - ) - birth_place_decrypted = fields.Char( - compute=lambda self: self._decrypt_field( - "birth_place", "birth_place_decrypted" - ), - store=False, - ) + encrypted_val = fields.Binary("Encrypted value", attachment=False) + is_encrypted = fields.Boolean(default=False) + + fields_list_to_enc = { + "name", + "family_name", + "given_name", + "addl_name", + "display_name", + "address", + "birth_place", + } + + placeholder_to_encrypted_field = "encrypted" @api.model - def create(self, vals): - record = super(EncryptedPartner, self).create(vals) - # TODO encryption key should be moved to a secret vault. - encryption_key = self.env["ir.config_parameter"].get_param("g2p_enc_key", "") - if encryption_key: - crypto = AESCipher(encryption_key) - record["name"] = crypto.encrypt(record["name"]) if record["name"] else None - record["family_name"] = ( - crypto.encrypt(record["family_name"]) if record["family_name"] else None - ) - record["given_name"] = ( - crypto.encrypt(record["given_name"]) if record["given_name"] else None - ) - record["addl_name"] = ( - crypto.encrypt(record["addl_name"]) if record["addl_name"] else None - ) - record["display_name"] = ( - crypto.encrypt(record["display_name"]) - if record["display_name"] - else None - ) - record["email"] = ( - crypto.encrypt(record["email"]) if record["email"] else None - ) - record["phone"] = ( - crypto.encrypt(record["phone"]) if record["phone"] else None - ) - record["mobile"] = ( - crypto.encrypt(record["mobile"]) if record["mobile"] else None - ) - record["address"] = ( - crypto.encrypt(record["address"]) if record["address"] else None - ) - record["birth_place"] = ( - crypto.encrypt(record["birth_place"]) if record["birth_place"] else None - ) - - return record - - # @api.model - # def write(self, vals): - # record = super(EncryptedPartner, self).create(vals) - # #TODO encryption key should be moved to a secret vault. - # encryption_key = self.env['ir.config_parameter'].get_param('g2p_enc_key', '') - # if encryption_key: - # crypto = AESCipher(encryption_key) - # record["name"] = crypto.encrypt(record["name"]) if record["name"] else None - # record["family_name"] = crypto.encrypt(record["family_name"]) if record["family_name"] else None - # record["given_name"] = crypto.encrypt(record["given_name"]) if record["given_name"] else None - # record["addl_name"] = crypto.encrypt(record["addl_name"]) if record["addl_name"] else None - # record["display_name"] = crypto.encrypt(record["display_name"]) if record["display_name"] else None - # record["email"] = crypto.encrypt(record["email"]) if record["email"] else None - # record["phone"] = crypto.encrypt(record["phone"]) if record["phone"] else None - # record["mobile"] = crypto.encrypt(record["mobile"]) if record["mobile"] else None - # record["address"] = crypto.encrypt(record["address"]) if record["address"] else None - # record["birth_place"] = crypto.encrypt(record["birth_place"]) if record["birth_place"] else None - - # return record - - def _decrypt_field(self, actual_field, decrypted_field): - # TODO encryption key should be moved to a secret vault. - encryption_key = self.env["ir.config_parameter"].get_param("g2p_enc_key", "") - if encryption_key: - crypto = AESCipher(encryption_key) - for rec in self: - if rec[actual_field]: - rec[decrypted_field] = crypto.decrypt(rec[actual_field]) + def gather_fields_to_be_enc_from_dict( + self, + fields_dict: dict, + replace=True, + ): + to_be_enc = {} + for each in self.fields_list_to_enc: + if fields_dict.get(each, None): + to_be_enc[each] = fields_dict[each] + if replace: + fields_dict[each] = self.placeholder_to_encrypted_field + return to_be_enc + + def create(self, vals_list): + is_encrypt_fields = ( + self.env["ir.config_parameter"] + .sudo() + .get_param("g2p_registry_encryption.encrypt_registry", default=False) + ) + if not is_encrypt_fields: + return super().create(vals_list) + + prov = self.env["g2p.encryption.provider"].get_registry_provider() + for vals in vals_list: + if vals.get("is_registrant", False): + to_be_encrypted = self.gather_fields_to_be_enc_from_dict(vals) + vals["encrypted_val"] = prov.encrypt_data( + json.dumps(to_be_encrypted).encode() + ) + vals["is_encrypted"] = True + + return super().create(vals_list) + + def write(self, vals): + is_encrypt_fields = ( + self.env["ir.config_parameter"] + .sudo() + .get_param("g2p_registry_encryption.encrypt_registry", default=False) + ) + if not is_encrypt_fields: + return super().write(vals) + + prov = self.env["g2p.encryption.provider"].get_registry_provider() + encrypted_vals = self.get_encrypted_val() + for rec, (is_encrypted, encrypted_val) in zip(self, encrypted_vals): + if rec.is_registrant or vals.get("is_registrant", False): + if not is_encrypted: + rec_values_list = rec.read(self.fields_list_to_enc)[0] + rec_values_list.update(vals) + rec_values_list["is_encrypted"] = True + vals = rec_values_list else: - rec[decrypted_field] = "" - _logger.info("%s , %s", decrypted_field, rec[decrypted_field]) + vals = json.loads(prov.decrypt_data(encrypted_val).decode()).update( + vals + ) + to_be_encrypted = self.gather_fields_to_be_enc_from_dict(vals) + + vals["encrypted_val"] = prov.encrypt_data( + json.dumps(to_be_encrypted).encode() + ) + + return super().write(vals) + + def _read(self, fields): + fields = set(fields) + res = super()._read(fields) + enc_fields_set = self.fields_list_to_enc.intersection(fields) + if not enc_fields_set: + return res + if len(fields) == 2 and "encrypted_val" in fields and "is_encrypted" in fields: + return res + + is_decrypt_fields = ( + self.env["ir.config_parameter"] + .sudo() + .get_param("g2p_registry_encryption.decrypt_registry", default=False) + ) + if not is_decrypt_fields: + return res + prov = self.env["g2p.encryption.provider"].get_registry_provider() + for record in self: + is_encrypted, encrypted_val = record.get_encrypted_val()[0] + if is_encrypted and encrypted_val: + decrypted_vals = json.loads(prov.decrypt_data(encrypted_val).decode()) + + for field_name in enc_fields_set: + if ( + field_name in decrypted_vals + and field_name in record + and record[field_name] + ): + self.env.cache.set( + record, self._fields[field_name], decrypted_vals[field_name] + ) + return res + + def get_encrypted_val(self): + ret = self.with_context(bin_size=False).read(["is_encrypted", "encrypted_val"]) + return [ + (each.get("is_encrypted", False), each.get("encrypted_val", None)) + for each in ret + ] diff --git a/g2p_registry_encryption/models/phone_number.py b/g2p_registry_encryption/models/phone_number.py deleted file mode 100644 index 7348da7..0000000 --- a/g2p_registry_encryption/models/phone_number.py +++ /dev/null @@ -1,41 +0,0 @@ -import logging - -from odoo import api, fields, models - -from odoo.addons.g2p_encryption.models.crypto import AESCipher - -_logger = logging.getLogger(__name__) - - -class EncryptedPhoneNumber(models.Model): - _inherit = "g2p.phone.number" - - phone_no_decrypted = fields.Char( - compute=lambda self: self._decrypt_field("phone_no", "phone_no_decrypted"), - store=False, - ) - - @api.model - def create(self, vals): - record = super(EncryptedPhoneNumber, self).create(vals) - # TODO encryption key should be moved to a secret vault. - encryption_key = self.env["ir.config_parameter"].get_param("g2p_enc_key", "") - if encryption_key: - crypto = AESCipher(encryption_key) - record["phone_no"] = ( - crypto.encrypt(record["phone_no"]) if record["phone_no"] else None - ) - - return record - - def _decrypt_field(self, actual_field, decrypted_field): - # TODO encryption key should be moved to a secret vault. - encryption_key = self.env["ir.config_parameter"].get_param("g2p_enc_key", "") - if encryption_key: - crypto = AESCipher(encryption_key) - for rec in self: - if rec[actual_field]: - rec[decrypted_field] = crypto.decrypt(rec[actual_field]) - else: - rec[decrypted_field] = "" - _logger.info("%s , %s", decrypted_field, rec[decrypted_field]) diff --git a/g2p_registry_encryption/models/reg_ids.py b/g2p_registry_encryption/models/reg_ids.py deleted file mode 100644 index 9d1ce06..0000000 --- a/g2p_registry_encryption/models/reg_ids.py +++ /dev/null @@ -1,41 +0,0 @@ -import logging - -from odoo import api, fields, models - -from odoo.addons.g2p_encryption.models.crypto import AESCipher - -_logger = logging.getLogger(__name__) - - -class EncryptedRegID(models.Model): - _inherit = "g2p.reg.id" - - value_decrypted = fields.Char( - compute=lambda self: self._decrypt_field("value", "value_decrypted"), - store=False, - ) - - @api.model - def create(self, vals): - record = super(EncryptedRegID, self).create(vals) - # TODO encryption key should be moved to a secret vault. - encryption_key = self.env["ir.config_parameter"].get_param("g2p_enc_key", "") - if encryption_key: - crypto = AESCipher(encryption_key) - record["value"] = ( - crypto.encrypt(record["value"]) if record["value"] else None - ) - - return record - - def _decrypt_field(self, actual_field, decrypted_field): - # TODO encryption key should be moved to a secret vault. - encryption_key = self.env["ir.config_parameter"].get_param("g2p_enc_key", "") - if encryption_key: - crypto = AESCipher(encryption_key) - for rec in self: - if rec[actual_field]: - rec[decrypted_field] = crypto.decrypt(rec[actual_field]) - else: - rec[decrypted_field] = "" - _logger.info("%s , %s", decrypted_field, rec[decrypted_field]) diff --git a/g2p_registry_encryption/models/res_config_settings.py b/g2p_registry_encryption/models/res_config_settings.py new file mode 100644 index 0000000..14df1e7 --- /dev/null +++ b/g2p_registry_encryption/models/res_config_settings.py @@ -0,0 +1,18 @@ +from odoo import fields, models + + +class RegistryEncryptConfigSettings(models.TransientModel): + _inherit = "res.config.settings" + + registry_encryption_provider = fields.Many2one( + "g2p.encryption.provider", + config_parameter="g2p_registry_encryption.encryption_provider_id", + ) + encrypt_registry = fields.Boolean( + config_parameter="g2p_registry_encryption.encrypt_registry" + ) + + # TODO: Change this to user context + decrypt_registry = fields.Boolean( + config_parameter="g2p_registry_encryption.decrypt_registry" + ) diff --git a/g2p_registry_encryption/static/description/index.html b/g2p_registry_encryption/static/description/index.html index 75d0a76..b5fcd08 100644 --- a/g2p_registry_encryption/static/description/index.html +++ b/g2p_registry_encryption/static/description/index.html @@ -3,18 +3,18 @@ - -G2P:Registry Encryption + +G2P Registry: Encryption