Skip to content

Commit

Permalink
Move models to own files. Apply OCA conventions
Browse files Browse the repository at this point in the history
  • Loading branch information
fkantelberg committed Mar 13, 2021
1 parent 8126fa4 commit ed3c7ab
Show file tree
Hide file tree
Showing 15 changed files with 242 additions and 218 deletions.
3 changes: 3 additions & 0 deletions vault/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@
from . import (
abstract_vault,
res_users,
res_users_key,
vault,
vault_entry,
vault_field,
vault_inbox,
vault_log,
vault_right,
vault_tag,
)
40 changes: 1 addition & 39 deletions vault/models/abstract_vault.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,50 +3,12 @@

import logging

from odoo import _, api, fields, models
from odoo import _, api, models
from odoo.exceptions import AccessError

_logger = logging.getLogger(__name__)


class VaultLog(models.Model):
_name = "vault.log"
_description = _("Log entry of a vault")
_order = "create_date DESC"
_rec_name = "message"

def _get_log_state(self):
return [
("info", _("Information")),
("warn", _("Warning")),
("error", _("Error")),
]

vault_id = fields.Many2one(
"vault",
"Vault",
ondelete="cascade",
required=True,
readonly=True,
)
entry_id = fields.Many2one(
"vault.entry",
"Entry",
ondelete="cascade",
readonly=True,
)
user_id = fields.Many2one("res.users", "User", required=True, readonly=True)
state = fields.Selection(_get_log_state, readonly=True)
message = fields.Char(readonly=True, required=True)

@api.model
def create(self, values):
res = super().create(values)
if not self.env.context.get("skip_log", False):
_logger.info("Vault log: %s", res.message)
return res


class AbstractVault(models.AbstractModel):
"""Models must have the following fields:
`perm_user`: The permissions are computed for this user
Expand Down
87 changes: 12 additions & 75 deletions vault/models/res_users.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,27 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

import logging
import re
from hashlib import sha256
from uuid import uuid4

from odoo import _, api, fields, models
from odoo.exceptions import ValidationError
from odoo import api, fields, models

_logger = logging.getLogger(__name__)


class ResUsers(models.Model):
_inherit = "res.users"

active_key = fields.Many2one(
"res.users.key",
compute="_compute_active_key",
store=False,
)
keys = fields.One2many("res.users.key", "user_id", readonly=True)
vault_right_ids = fields.One2many("vault.right", "user_id", readonly=True)
inbox_enabled = fields.Boolean(default=True)
inbox_link = fields.Char(compute="_compute_inbox_link", readonly=True, store=False)
inbox_token = fields.Char(default=lambda self: uuid4(), readonly=True)

@api.depends("keys", "keys.current")
def _compute_active_key(self):
for rec in self:
Expand All @@ -27,17 +35,6 @@ def _compute_inbox_link(self):
for rec in self:
rec.inbox_link = f"{base_url}/vault/inbox/{rec.inbox_token}"

active_key = fields.Many2one(
"res.users.key",
compute=_compute_active_key,
store=False,
)
keys = fields.One2many("res.users.key", "user_id", readonly=True)
vault_right_ids = fields.One2many("vault.right", "user_id", readonly=True)
inbox_enabled = fields.Boolean(default=True)
inbox_link = fields.Char(compute=_compute_inbox_link, readonly=True, store=False)
inbox_token = fields.Char(default=lambda self: uuid4(), readonly=True)

@api.model
def action_get_vault(self):
return self.sudo().env.ref("vault.action_res_users_keys").read()[0]
Expand Down Expand Up @@ -65,63 +62,3 @@ def get_vault_keys(self):
"salt": self.active_key.salt,
"uuid": self.active_key.uuid,
}


class ResUsersKey(models.Model):
_name = "res.users.key"
_description = _("User data of a vault")
_rec_name = "fingerprint"
_order = "create_date DESC"

@api.depends("public")
def _compute_fingerprint(self):
for rec in self:
if rec.public:
hashed = sha256(rec.public.encode()).hexdigest()
rec.fingerprint = ":".join(re.findall(r".{2}", hashed))
else:
rec.fingerprint = False

user_id = fields.Many2one("res.users", required=True)
uuid = fields.Char(default=lambda self: uuid4(), required=True, readonly=True)
current = fields.Boolean(default=True, readonly=True)
fingerprint = fields.Char(compute=_compute_fingerprint, store=True)
public = fields.Char(required=True, readonly=True)
salt = fields.Char(required=True, readonly=True)
iv = fields.Char(required=True, readonly=True)
iterations = fields.Integer(required=True, readonly=True)
# Encrypted with master password of user
private = fields.Char(required=True, readonly=True)

def store(self, iterations, iv, private, public, salt):
if not all(isinstance(x, str) and x for x in [public, private, iv, salt]):
raise ValidationError(_("Invalid parameter"))

if not isinstance(iterations, int) or iterations < 4000:
raise ValidationError(_("Invalid parameter"))

domain = [
("user_id", "=", self.env.uid),
("private", "=", private),
]
if not self.search(domain):
# Disable all current keys
self.env.user.keys.write({"current": False})

rec = self.create(
{
"iterations": iterations,
"iv": iv,
"private": private,
"public": public,
"salt": salt,
"user_id": self.env.uid,
"current": True,
}
)
return rec.uuid
return False

def extract_public_key(self, user):
user = self.sudo().search([("user_id", "=", user), ("current", "=", True)])
return user.public or None
75 changes: 75 additions & 0 deletions vault/models/res_users_key.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# © 2021 Florian Kantelberg - initOS GmbH
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

import logging
import re
from hashlib import sha256
from uuid import uuid4

from odoo import _, api, fields, models
from odoo.exceptions import ValidationError

_logger = logging.getLogger(__name__)


class ResUsersKey(models.Model):
_name = "res.users.key"
_description = _("User data of a vault")
_rec_name = "fingerprint"
_order = "create_date DESC"

user_id = fields.Many2one("res.users", required=True)
uuid = fields.Char(default=lambda self: uuid4(), required=True, readonly=True)
current = fields.Boolean(default=True, readonly=True)
fingerprint = fields.Char(compute="_compute_fingerprint", store=True)
public = fields.Char(required=True, readonly=True)
salt = fields.Char(required=True, readonly=True)
iv = fields.Char(required=True, readonly=True)
iterations = fields.Integer(required=True, readonly=True)
# Encrypted with master password of user
private = fields.Char(required=True, readonly=True)

@api.depends("public")
def _compute_fingerprint(self):
for rec in self:
if rec.public:
hashed = sha256(rec.public.encode()).hexdigest()
rec.fingerprint = ":".join(re.findall(r".{2}", hashed))
else:
rec.fingerprint = False

def _prepare_values(self, iterations, iv, private, public, salt):
return {
"iterations": iterations,
"iv": iv,
"private": private,
"public": public,
"salt": salt,
"user_id": self.env.uid,
"current": True,
}

def store(self, iterations, iv, private, public, salt):
if not all(isinstance(x, str) and x for x in [public, private, iv, salt]):
raise ValidationError(_("Invalid parameter"))

if not isinstance(iterations, int) or iterations < 4000:
raise ValidationError(_("Invalid parameter"))

domain = [
("user_id", "=", self.env.uid),
("private", "=", private),
]
if not self.search(domain):
# Disable all current keys
self.env.user.keys.write({"current": False})

rec = self.create(
self._prepare_values(iterations, iv, private, public, salt)
)
return rec.uuid
return False

def extract_public_key(self, user):
user = self.sudo().search([("user_id", "=", user), ("current", "=", True)])
return user.public or None
78 changes: 39 additions & 39 deletions vault/models/vault.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,45 @@ class Vault(models.Model):
_inherit = ["vault.abstract"]
_order = "name"

user_id = fields.Many2one(
"res.users",
"Owner",
readonly=True,
default=lambda self: self.env.user,
required=True,
)
right_ids = fields.One2many(
"vault.right",
"vault_id",
"Rights",
default="_get_default_rights",
)
entry_ids = fields.One2many("vault.entry", "vault_id", "Entries")
field_ids = fields.One2many("vault.field", "vault_id", "Fields")
file_ids = fields.One2many("vault.file", "vault_id", "Files")
log_ids = fields.One2many("vault.log", "vault_id", "Log", readonly=True)

# Access control
perm_user = fields.Many2one("res.users", compute="_compute_access", store=False)
allowed_read = fields.Boolean(compute="_compute_access", store=False)
allowed_share = fields.Boolean(compute="_compute_access", store=False)
allowed_write = fields.Boolean(compute="_compute_access", store=False)
allowed_delete = fields.Boolean(compute="_compute_access", store=False)

master_key = fields.Char(
compute="_compute_master_key",
inverse="_inverse_master_key",
store=False,
)

uuid = fields.Char(default=lambda self: uuid4(), required=True, readonly=True)
name = fields.Char(required=True)
note = fields.Text()

_sql_constraints = [
("uuid_uniq", "UNIQUE(uuid)", _("The UUID must be unique.")),
]

@api.depends("right_ids.user_id")
def _compute_access(self):
user = self.env.user
Expand Down Expand Up @@ -68,45 +107,6 @@ def _get_default_rights(self):
)
]

user_id = fields.Many2one(
"res.users",
"Owner",
readonly=True,
default=lambda self: self.env.user,
required=True,
)
right_ids = fields.One2many(
"vault.right",
"vault_id",
"Rights",
default=_get_default_rights,
)
entry_ids = fields.One2many("vault.entry", "vault_id", "Entries")
field_ids = fields.One2many("vault.field", "vault_id", "Fields")
file_ids = fields.One2many("vault.file", "vault_id", "Files")
log_ids = fields.One2many("vault.log", "vault_id", "Log", readonly=True)

# Access control
perm_user = fields.Many2one("res.users", compute=_compute_access, store=False)
allowed_read = fields.Boolean(compute=_compute_access, store=False)
allowed_share = fields.Boolean(compute=_compute_access, store=False)
allowed_write = fields.Boolean(compute=_compute_access, store=False)
allowed_delete = fields.Boolean(compute=_compute_access, store=False)

master_key = fields.Char(
compute=_compute_master_key,
inverse=_inverse_master_key,
store=False,
)

uuid = fields.Char(default=lambda self: uuid4(), required=True, readonly=True)
name = fields.Char(required=True)
note = fields.Text()

_sql_constraints = [
("uuid_uniq", "UNIQUE(uuid)", _("The UUID must be unique.")),
]

def _log_entry(self, msg, state):
self.ensure_one()
return (
Expand Down
Loading

0 comments on commit ed3c7ab

Please sign in to comment.