Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • coopdevs/comunitats-energetiques/odoo-ce
1 result
Show changes
Commits on Source (9)
...@@ -71,6 +71,7 @@ ...@@ -71,6 +71,7 @@
'data/mail_template_data.xml', 'data/mail_template_data.xml',
'data/mail_template_update_data.xml', 'data/mail_template_update_data.xml',
'wizards/multicompany_easy_creation.xml', 'wizards/multicompany_easy_creation.xml',
'wizards/assign_admin_wizard.xml',
], ],
'installable': True, 'installable': True,
'application': True, 'application': True,
......
...@@ -8,6 +8,7 @@ URL_AUTH = "{root_endpoint}realms/{realm_name}/protocol/openid-connect/auth" ...@@ -8,6 +8,7 @@ URL_AUTH = "{root_endpoint}realms/{realm_name}/protocol/openid-connect/auth"
URL_VALIDATION = "{root_endpoint}realms/{realm_name}/protocol/openid-connect/userinfo" URL_VALIDATION = "{root_endpoint}realms/{realm_name}/protocol/openid-connect/userinfo"
URL_TOKEN = "{root_endpoint}realms/{realm_name}/protocol/openid-connect/token" URL_TOKEN = "{root_endpoint}realms/{realm_name}/protocol/openid-connect/token"
URL_JWKS = "{root_endpoint}realms/{realm_name}/protocol/openid-connect/certs" URL_JWKS = "{root_endpoint}realms/{realm_name}/protocol/openid-connect/certs"
URL_RESET_PASSWORD = "{root_endpoint}admin/realms/{realm_name}/users/{kc_uid}/execute-actions-email?redirect_uri={odoo_url}&client_id={cliend_id}"
class OAuthProvider(models.Model): class OAuthProvider(models.Model):
...@@ -22,6 +23,8 @@ class OAuthProvider(models.Model): ...@@ -22,6 +23,8 @@ class OAuthProvider(models.Model):
admin_user_endpoint = fields.Char(string='User admin URL', required=True) admin_user_endpoint = fields.Char(string='User admin URL', required=True)
root_endpoint = fields.Char(string='Root URL', required=True, default='http://keycloak-ccee.local:8080/auth/') root_endpoint = fields.Char(string='Root URL', required=True, default='http://keycloak-ccee.local:8080/auth/')
realm_name = fields.Char(string='Realm name', required=True, default='0') realm_name = fields.Char(string='Realm name', required=True, default='0')
reset_password_endpoint = fields.Char(string='Reset password URL')
redirect_admin_url = fields.Char(string='Redirect Link after update password')
def validate_admin_provider(self): def validate_admin_provider(self):
if not self.client_secret: if not self.client_secret:
...@@ -31,6 +34,7 @@ class OAuthProvider(models.Model): ...@@ -31,6 +34,7 @@ class OAuthProvider(models.Model):
@api.onchange('root_endpoint') @api.onchange('root_endpoint')
def _onchange_root_endpoint(self): def _onchange_root_endpoint(self):
# TODO: Duplicated code? 🤔
if self.is_keycloak_provider and self.root_endpoint and self.realm_name: if self.is_keycloak_provider and self.root_endpoint and self.realm_name:
self.admin_user_endpoint = URL_ADMIN_USERS.format(**{'root_endpoint': self.root_endpoint, self.admin_user_endpoint = URL_ADMIN_USERS.format(**{'root_endpoint': self.root_endpoint,
'realm_name': self.realm_name}) 'realm_name': self.realm_name})
...@@ -42,9 +46,17 @@ class OAuthProvider(models.Model): ...@@ -42,9 +46,17 @@ class OAuthProvider(models.Model):
'realm_name': self.realm_name}) 'realm_name': self.realm_name})
self.jwks_uri = URL_JWKS.format(**{'root_endpoint': self.root_endpoint, self.jwks_uri = URL_JWKS.format(**{'root_endpoint': self.root_endpoint,
'realm_name': self.realm_name}) 'realm_name': self.realm_name})
self.reset_password_endpoint = URL_RESET_PASSWORD.format(
root_endpoint=self.root_endpoint,
realm_name=self.realm_name,
kc_uid='{kc_uid}',
odoo_url=self.redirect_admin_url,
cliend_id=self.client_id,
)
@api.onchange('realm_name') @api.onchange('realm_name')
def _onchange_realm_name(self): def _onchange_realm_name(self):
# TODO: Duplicated code? 🤔
if self.is_keycloak_provider and self.root_endpoint and self.realm_name: if self.is_keycloak_provider and self.root_endpoint and self.realm_name:
self.admin_user_endpoint = URL_ADMIN_USERS.format(**{'root_endpoint': self.root_endpoint, self.admin_user_endpoint = URL_ADMIN_USERS.format(**{'root_endpoint': self.root_endpoint,
'realm_name': self.realm_name}) 'realm_name': self.realm_name})
...@@ -56,6 +68,13 @@ class OAuthProvider(models.Model): ...@@ -56,6 +68,13 @@ class OAuthProvider(models.Model):
'realm_name': self.realm_name}) 'realm_name': self.realm_name})
self.jwks_uri = URL_JWKS.format(**{'root_endpoint': self.root_endpoint, self.jwks_uri = URL_JWKS.format(**{'root_endpoint': self.root_endpoint,
'realm_name': self.realm_name}) 'realm_name': self.realm_name})
self.reset_password_endpoint = URL_RESET_PASSWORD.format(
root_endpoint=self.root_endpoint,
realm_name=self.realm_name,
kc_uid='{kc_uid}',
odoo_url=self.redirect_admin_url,
cliend_id=self.client_id,
)
def get_auth_link(self): def get_auth_link(self):
self.ensure_one() self.ensure_one()
......
...@@ -71,6 +71,13 @@ class ResCompany(models.Model): ...@@ -71,6 +71,13 @@ class ResCompany(models.Model):
) )
wordpress_base_url = fields.Char( wordpress_base_url = fields.Char(
string=_("Wordpress Base URL (JWT auth)") string=_("Wordpress Base URL (JWT auth)")
admins = fields.One2many(
'res.users',
string=_("Community admins"),
compute='_get_admins',
readonly=True,
store=False
) )
@api.constrains('hierarchy_level', 'parent_id') @api.constrains('hierarchy_level', 'parent_id')
...@@ -128,6 +135,55 @@ class ResCompany(models.Model): ...@@ -128,6 +135,55 @@ class ResCompany(models.Model):
admins_user_ids.append(role_line.user_id.id) admins_user_ids.append(role_line.user_id.id)
return any([user in admins_user_ids for user in company_user_ids]) return any([user in admins_user_ids for user in company_user_ids])
def _get_admin_role_name(self):
if self.hierarchy_level == 'community':
return "role_ce_admin"
elif self.hierarchy_level == 'coordinator':
return "role_coordination"
elif self.hierarchy_level == 'instance':
return "role_platform_admin"
def _get_admins(self):
role_name = self._get_admin_role_name()
for rec in self:
role_lines = self.env["res.users.role.line"].sudo().search([
("company_id.id", "=", self.id),
("active", "=", True),
("role_id.code", "=", role_name)
])
rec.admins = role_lines.mapped("user_id")
def add_ce_admin(self, user):
role_name = self._get_admin_role_name()
role = self.env["res.users.role"].sudo().search([(
"code", "=", role_name
)])
current_role = self.env["res.users.role.line"].sudo().search([
("user_id", "=", user.id),
("active", "=", True),
("company_id", "=", self.id)
])
if current_role and len(current_role) > 1:
raise UserError(_("Error: This user have multiple roles for this company"))
if current_role and len(current_role.company_ids) > 1:
raise UserError(_("Error: One role line can't have multiple users"))
if current_role and current_role.role.code != "role_ce_member":
raise UserError(_("Error: You can't remove {} role").format(current_role.role.code))
if current_role:
current_role.write({"role_id": role})
else:
user = self.env["res.users"].sudo().browse(user.id)
user.write({
"company_ids": [(4, self.id)]
})
self.env["res.users.role.line"].sudo().create({
"user_id": user.id,
"active": True,
"role_id": role.id,
"company_id": self.id,
})
def get_ce_members(self, domain_key="in_kc_and_active"): def get_ce_members(self, domain_key="in_kc_and_active"):
domains_dict = { domains_dict = {
"in_kc_and_active": [ "in_kc_and_active": [
...@@ -236,6 +292,15 @@ class ResCompany(models.Model): ...@@ -236,6 +292,15 @@ class ResCompany(models.Model):
self.landing_page_id.write( self.landing_page_id.write(
{"wp_landing_page_id": landing_page['id']}) {"wp_landing_page_id": landing_page['id']})
def action_open_assign_admin_wizard(self):
return {
'name': 'Example Wizard',
'type': 'ir.actions.act_window',
'res_model': 'assign.admin.wizard',
'view_mode': 'form',
'target': 'new',
}
def get_landing_page_form(self): def get_landing_page_form(self):
return { return {
"type": "ir.actions.act_window", "type": "ir.actions.act_window",
......
...@@ -135,7 +135,8 @@ class ResUsers(models.Model): ...@@ -135,7 +135,8 @@ class ResUsers(models.Model):
values = { values = {
'username': odoo_user.login, 'username': odoo_user.login,
'email': odoo_user.partner_id.email, 'email': odoo_user.partner_id.email,
'attributes': {'lang': [odoo_user.lang]} 'attributes': {'lang': [odoo_user.lang]},
'enabled': True,
} }
if 'firstname' in odoo_user.partner_id: if 'firstname' in odoo_user.partner_id:
...@@ -181,3 +182,32 @@ class ResUsers(models.Model): ...@@ -181,3 +182,32 @@ class ResUsers(models.Model):
def get_role_codes(self): def get_role_codes(self):
#TODO Map all code to company and enable (We should update the API schema too) #TODO Map all code to company and enable (We should update the API schema too)
return self.role_line_ids[0].role_id.code return self.role_line_ids[0].role_id.code
def get_administrared_ce(self):
communities = []
role_lines = self.env["res.users.role.line"].sudo().search([
("user_id.id", "=", self.id),
("active", "=", True),
("role_id.code", "=", "role_ce_admin")
])
for role_line in role_lines:
for company in role_line.allowed_company_ids:
communities.append(company.id)
return communities
def send_reset_password_mail(self):
provider_id = self.env.ref('energy_communities.keycloak_admin_provider')
provider_id.validate_admin_provider()
headers = {
'Authorization': 'Bearer %s' % self._get_admin_token(provider_id)
}
headers['Content-Type'] = "application/json"
endpoint = provider_id.reset_password_endpoint.format(
kc_uid = self.oauth_uid
)
response = requests.put(endpoint, headers=headers, data='["UPDATE_PASSWORD", "VERIFY_EMAIL"]')
if response.status_code != 204:
raise exceptions.UserError(
_('Something went wrong. Mail can not be sended. More details: {}').format(response.json())
)
...@@ -16,6 +16,10 @@ ...@@ -16,6 +16,10 @@
('is_keycloak_provider', '=', False)]}"/> ('is_keycloak_provider', '=', False)]}"/>
<field name="admin_user_endpoint" attrs="{'invisible': ['|', ('is_admin_provider','=',False), <field name="admin_user_endpoint" attrs="{'invisible': ['|', ('is_admin_provider','=',False),
('is_keycloak_provider', '=', False)]}"/> ('is_keycloak_provider', '=', False)]}"/>
<field name="redirect_admin_url" attrs="{'invisible': ['|', ('is_admin_provider','=',False),
('is_keycloak_provider', '=', False)]}"/>
<field name="reset_password_endpoint" attrs="{'invisible': ['|', ('is_admin_provider','=',False),
('is_keycloak_provider', '=', False)]}"/>
</xpath> </xpath>
</field> </field>
</record> </record>
......
...@@ -11,25 +11,15 @@ ...@@ -11,25 +11,15 @@
<xpath expr="//sheet" position="before"> <xpath expr="//sheet" position="before">
<header> <header>
<field name="landing_page_id" invisible="1"/> <field name="landing_page_id" invisible="1"/>
<button <button name="create_landing" type="object" string="Create landing page" attrs="{'invisible': [('landing_page_id','!=',False)]}" />
name="create_landing" <button name="action_open_assign_admin_wizard" type="object" string="Assing administrator" groups="['group_admin','group_platform_manager','group_coordinator']"/>
type="object"
string="Create landing page"
attrs="{'invisible': [('landing_page_id','!=',False)]}"
/>
</header> </header>
</xpath> </xpath>
<xpath expr="//field[@name='logo']" position="before"> <xpath expr="//field[@name='logo']" position="before">
<div class='oe_button_box'> <div class='oe_button_box'>
<button <button name="get_landing_page_form" type="object" string="My landing page" class="oe_stat_button" icon="fa-globe" attrs="{'invisible': [('landing_page_id','=',False)]}" />
name="get_landing_page_form"
type="object"
string="My landing page"
class="oe_stat_button"
icon="fa-globe"
attrs="{'invisible': [('landing_page_id','=',False)]}"
/>
</div> </div>
<field name="admins" invisible="True"></field>
</xpath> </xpath>
<xpath expr="//notebook" position="inside"> <xpath expr="//notebook" position="inside">
<page string="Energy Communities"> <page string="Energy Communities">
...@@ -39,6 +29,7 @@ ...@@ -39,6 +29,7 @@
<field name="parent_id" domain="[('id', 'in', parent_id_filtered_ids)]"></field> <field name="parent_id" domain="[('id', 'in', parent_id_filtered_ids)]"></field>
<field name="voluntary_share_id"></field> <field name="voluntary_share_id"></field>
<field name="ce_tag_ids"/> <field name="ce_tag_ids"/>
<field name="admins"/>
</group> </group>
</page> </page>
</xpath> </xpath>
......
from . import multicompany_easy_creation from . import multicompany_easy_creation
from . import assign_admin_wizard
\ No newline at end of file
from odoo import models, fields, api
from odoo.tools.translate import _
from odoo.exceptions import ValidationError
class AssignAdminWizard(models.TransientModel):
_name = 'assign.admin.wizard'
_description = 'Assign admin Wizard'
is_new_admin = fields.Boolean(
string=_("Is a new admin?")
)
first_name = fields.Char(string=_("First name"))
last_name = fields.Char(string=_("Last name"))
vat = fields.Char(string=_("VAT"))
email = fields.Char(string=_("Email"))
lang = fields.Many2one(
'res.lang',
string=_("Language")
)
def process_data(self):
company_ids = self.env.context.get('active_ids')
if not company_ids:
raise ValidationError(_('Company not found'))
company_id = company_ids[0]
if self.is_new_admin:
user = self.create_user(company_id)
else:
user = self.env['res.users'].search([('login', '=', self.vat)])
company = self.env['res.company'].browse(company_id)
company.add_ce_admin(user)
return True
# TODO: Move this method inside res_users
def create_user(self, company_id):
vals = {
"login": self.vat,
"firstname": self.first_name,
"lastname": self.last_name,
"company_id": company_id,
"company_ids": [company_id],
"lang": self.lang.code,
"email": self.email,
}
user = self.env["res.users"].create(vals)
user.create_users_on_keycloak()
user.send_reset_password_mail()
return user
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="view_assign_admin_wizard_form" model="ir.ui.view">
<field name="name">assign.admin.wizard.form</field>
<field name="model">assign.admin.wizard</field>
<field name="arch" type="xml">
<form string="Assign Admin Wizard">
<sheet>
<group>
<field name="is_new_admin"/>
</group>
<group>
<field name="vat"/>
<field name="first_name" attrs="{'invisible':[('is_new_admin', '=', False)]}"/>
<field name="last_name" attrs="{'invisible':[('is_new_admin', '=', False)]}"/>
<field name="email" attrs="{'invisible':[('is_new_admin', '=', False)]}"/>
<field name="lang" attrs="{'invisible':[('is_new_admin', '=', False)]}"/>
</group>
</sheet>
<footer>
<button name="process_data" string="Procesar" type="object" class="btn-primary"/>
<button string="Cancelar" class="btn-secondary" special="cancel"/>
</footer>
</form>
</field>
</record>
</data>
</odoo>
\ No newline at end of file