Browse Source

Aug 17: [ADD] Initital Commits 'restrict_logins'

pull/332/merge
Cybrosys Technologies 8 months ago
parent
commit
a7a38c9be6
  1. 50
      restrict_logins/README.rst
  2. 23
      restrict_logins/__init__.py
  3. 46
      restrict_logins/__manifest__.py
  4. 23
      restrict_logins/controllers/__init__.py
  5. 100
      restrict_logins/controllers/home.py
  6. 104
      restrict_logins/controllers/session.py
  7. 17
      restrict_logins/data/ir_cron_data.xml
  8. 5
      restrict_logins/doc/RELEASE_NOTES.md
  9. 24
      restrict_logins/models/__init__.py
  10. 101
      restrict_logins/models/ir_http.py
  11. 123
      restrict_logins/models/res_users.py
  12. BIN
      restrict_logins/static/description/assets/icons/check.png
  13. BIN
      restrict_logins/static/description/assets/icons/chevron.png
  14. BIN
      restrict_logins/static/description/assets/icons/cogs.png
  15. BIN
      restrict_logins/static/description/assets/icons/consultation.png
  16. BIN
      restrict_logins/static/description/assets/icons/ecom-black.png
  17. BIN
      restrict_logins/static/description/assets/icons/education-black.png
  18. BIN
      restrict_logins/static/description/assets/icons/hotel-black.png
  19. BIN
      restrict_logins/static/description/assets/icons/license.png
  20. BIN
      restrict_logins/static/description/assets/icons/lifebuoy.png
  21. BIN
      restrict_logins/static/description/assets/icons/logo.png
  22. BIN
      restrict_logins/static/description/assets/icons/manufacturing-black.png
  23. BIN
      restrict_logins/static/description/assets/icons/pos-black.png
  24. BIN
      restrict_logins/static/description/assets/icons/puzzle.png
  25. BIN
      restrict_logins/static/description/assets/icons/restaurant-black.png
  26. BIN
      restrict_logins/static/description/assets/icons/service-black.png
  27. BIN
      restrict_logins/static/description/assets/icons/trading-black.png
  28. BIN
      restrict_logins/static/description/assets/icons/training.png
  29. BIN
      restrict_logins/static/description/assets/icons/update.png
  30. BIN
      restrict_logins/static/description/assets/icons/user.png
  31. BIN
      restrict_logins/static/description/assets/icons/wrench.png
  32. BIN
      restrict_logins/static/description/assets/misc/categories.png
  33. BIN
      restrict_logins/static/description/assets/misc/check-box.png
  34. BIN
      restrict_logins/static/description/assets/misc/compass.png
  35. BIN
      restrict_logins/static/description/assets/misc/corporate.png
  36. BIN
      restrict_logins/static/description/assets/misc/customer-support.png
  37. BIN
      restrict_logins/static/description/assets/misc/cybrosys-logo.png
  38. BIN
      restrict_logins/static/description/assets/misc/features.png
  39. BIN
      restrict_logins/static/description/assets/misc/logo.png
  40. BIN
      restrict_logins/static/description/assets/misc/pictures.png
  41. BIN
      restrict_logins/static/description/assets/misc/pie-chart.png
  42. BIN
      restrict_logins/static/description/assets/misc/right-arrow.png
  43. BIN
      restrict_logins/static/description/assets/misc/star.png
  44. BIN
      restrict_logins/static/description/assets/misc/support.png
  45. BIN
      restrict_logins/static/description/assets/misc/whatsapp.png
  46. BIN
      restrict_logins/static/description/assets/modules/customer_product_qrcode.png
  47. BIN
      restrict_logins/static/description/assets/modules/customized_barcode_generator.png
  48. BIN
      restrict_logins/static/description/assets/modules/employee_dynamic_fields.png
  49. BIN
      restrict_logins/static/description/assets/modules/list_view_sticky_header.png
  50. BIN
      restrict_logins/static/description/assets/modules/odoo_website_helpdesk.png
  51. BIN
      restrict_logins/static/description/assets/modules/sm01.png
  52. BIN
      restrict_logins/static/description/assets/modules/sm02.jpg
  53. BIN
      restrict_logins/static/description/assets/modules/sm03.png
  54. BIN
      restrict_logins/static/description/assets/modules/sm04.png
  55. BIN
      restrict_logins/static/description/assets/modules/sm05.jpg
  56. BIN
      restrict_logins/static/description/assets/modules/todo_list.png
  57. BIN
      restrict_logins/static/description/assets/screenshots/hero.gif
  58. BIN
      restrict_logins/static/description/assets/screenshots/login_1.png
  59. BIN
      restrict_logins/static/description/assets/screenshots/login_2.png
  60. BIN
      restrict_logins/static/description/assets/screenshots/login_3.png
  61. BIN
      restrict_logins/static/description/banner.jpg
  62. BIN
      restrict_logins/static/description/icon.png
  63. 607
      restrict_logins/static/description/index.html
  64. 15
      restrict_logins/views/login_clear_session_template.xml
  65. 25
      restrict_logins/views/res_users_views.xml

50
restrict_logins/README.rst

@ -0,0 +1,50 @@
.. image:: https://img.shields.io/badge/license-LGPL--3-green.svg
:target: https://www.gnu.org/licenses/lgpl-3.0-standalone.html
:alt: License: LGPL-3
Restrict Concurrent User Login
==============================
This module ensures restricted concurrent sessions, enforces user force logout, and automates session expiry for enhanced security and user management.
Configuration
=============
No additional configurations needed.
Company
-------
* `Cybrosys Techno Solutions <https://cybrosys.com/>`__
License
-------
Lesser General Public License, Version 3 (LGPL v3)
(https://www.gnu.org/licenses/lgpl-3.0-standalone.html)
Credits
-------
* Developers : (V13) Milind Mohan,
(V14) Aysha Shalin,
(V15) Aysha Shalin,
(V16) Jumana Jabin MK
Contact: odoo@cybrosys.com
Contacts
--------
* Mail Contact : odoo@cybrosys.com
* Website : https://cybrosys.com
Bug Tracker
-----------
Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported.
Maintainer
==========
.. image:: https://cybrosys.com/images/logo.png
:target: https://cybrosys.com
This module is maintained by Cybrosys Technologies.
For support and more information, please visit `Our Website <https://cybrosys.com/>`__
Further information
===================
HTML Description: `<static/description/index.html>`__

23
restrict_logins/__init__.py

@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
###############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Cybrosys Technologies (odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU LESSER
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
#
# 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 (LGPL v3) for more details.
#
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
# (LGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
###############################################################################
from . import controllers
from . import models

46
restrict_logins/__manifest__.py

@ -0,0 +1,46 @@
# -*- coding: utf-8 -*-
###############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Cybrosys Technologies (odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU LESSER
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
#
# 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 (LGPL v3) for more details.
#
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
# (LGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
###############################################################################
{
'name': 'Restrict Concurrent User Login',
'version': '16.0.1.0.0',
'category': 'Extra Tools',
'summary': """Ensures restricted concurrent sessions, enforces user force
logout, and automates session expiry for enhanced security.""",
'description': """This module ensures security by restricting concurrent
user sessions and provides the option for forced logout. It includes
automatic session expiry after a set duration, managing user logins
efficiently.""",
'author': 'Cybrosys Techno Solutions',
'company': 'Cybrosys Techno Solutions',
'maintainer': 'Cybrosys Techno Solutions',
'website': 'https://www.cybrosys.com',
'data': [
'data/ir_cron_data.xml',
'views/res_users_views.xml',
'views/login_clear_session_template.xml',
],
'images': ['static/description/banner.jpg'],
'license': 'LGPL-3',
'installable': True,
'auto_install': False,
'application': False,
}

23
restrict_logins/controllers/__init__.py

@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
###############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Cybrosys Technologies (odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU LESSER
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
#
# 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 (LGPL v3) for more details.
#
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
# (LGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
###############################################################################
from . import home
from . import session

100
restrict_logins/controllers/home.py

@ -0,0 +1,100 @@
###############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Cybrosys Technologies (odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU LESSER
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
#
# 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 (LGPL v3) for more details.
#
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
# (LGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
###############################################################################
from odoo.addons.web.controllers.home import Home as WebHome
from odoo.addons.web.controllers.utils import ensure_db
import odoo
import odoo.modules.registry
from odoo import SUPERUSER_ID
from odoo import http
from odoo.http import request
from odoo.tools.translate import _
# Shared parameters for all login/signup flows
SIGN_UP_REQUEST_PARAMS = {'db', 'login', 'debug', 'token', 'message', 'error', 'scope', 'mode',
'redirect', 'redirect_hostname', 'email', 'name', 'partner_id',
'password', 'confirm_password', 'city', 'country_id', 'lang', 'signup_email'}
class Home(WebHome):
""" This class includes methods related to user authentication and login."""
@http.route('/web/login', type='http', auth="none")
def web_login(self, redirect=None, **kw):
ensure_db()
request.params['login_success'] = False
if request.httprequest.method == 'GET' and redirect and request.session.uid:
return request.redirect(redirect)
# simulate hybrid auth=user/auth=public, despite using auth=none to be able
# to redirect users when no db is selected - cfr ensure_db()
if request.env.uid is None:
if request.session.uid is None:
# no user -> auth=public with specific website public user
request.env["ir.http"]._auth_method_public()
else:
# auth=user
request.update_env(user=request.session.uid)
values = {k: v for k, v in request.params.items() if k in SIGN_UP_REQUEST_PARAMS}
try:
values['databases'] = http.db_list()
except odoo.exceptions.AccessDenied:
values['databases'] = None
if request.httprequest.method == 'POST':
old_uid = request.uid
try:
uid = request.session.authenticate(request.db,
request.params['login'],
request.params['password'])
request.params['login_success'] = True
return request.redirect(
self._login_redirect(uid, redirect=redirect))
except odoo.exceptions.AccessDenied as e:
failed_uid = request.env.uid
request.env.uid = old_uid
if e.args == odoo.exceptions.AccessDenied().args:
values['error'] = _("Wrong login/password")
# already logged_in user exception
elif e.args[0] == "already_logged_in":
values['error'] = "User already logged in. Log out from " \
"other devices and try again."
values['logout_all'] = True
values['failed_uid'] = failed_uid if (
failed_uid != SUPERUSER_ID) else False
else:
values['error'] = e.args[0]
else:
if 'error' in request.params and request.params.get(
'error') == 'access':
values['error'] = _(
'Only employees can access this database. Please contact the administrator.')
if 'login' not in values and request.session.get('auth_login'):
values['login'] = request.session.get('auth_login')
if not odoo.tools.config['list_db']:
values['disable_database_manager'] = True
response = request.render('web.login', values)
response.headers['X-Frame-Options'] = 'SAMEORIGIN'
response.headers['Content-Security-Policy'] = "frame-ancestors 'self'"
return response

104
restrict_logins/controllers/session.py

@ -0,0 +1,104 @@
# -*- coding: utf-8 -*-
###############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Cybrosys Technologies (odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU LESSER
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
#
# 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 (LGPL v3) for more details.
#
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
# (LGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
###############################################################################
import os
from odoo.addons.web.controllers.session import Session as WebSession
import odoo
import odoo.modules.registry
from odoo import http
from odoo.http import request
from odoo.tools._vendor import sessions
# Shared parameters for all login/signup flows
SIGN_UP_REQUEST_PARAMS = {'db', 'login', 'debug', 'token', 'message', 'error', 'scope', 'mode',
'redirect', 'redirect_hostname', 'email', 'name', 'partner_id',
'password', 'confirm_password', 'city', 'country_id', 'lang', 'signup_email'}
LOGIN_SUCCESSFUL_PARAMS = set()
def clear_session_history(u_sid, f_uid=False):
""" Clear all the user session histories of a particular user """
path = odoo.tools.config.session_dir
store = odoo.http.FilesystemSessionStore(
path, session_class=odoo.http.Session, renew_missing=True)
session_path = store.get_session_filename(u_sid)
try:
os.remove(session_path)
return True
except OSError as e:
pass
return False
def super_clear_all():
""" Clear all the user session histories """
path = odoo.tools.config.session_dir
store = sessions.FilesystemSessionStore(
path, session_class=odoo.http.Session, renew_missing=True)
for fname in os.listdir(store.path):
path = os.path.join(store.path, fname)
try:
os.unlink(path)
except OSError:
pass
return True
class Session(WebSession):
"""
This class includes methods to handle user logouts, clear individual user
sessions, and perform a force logout, which logs out from all devices.
"""
@http.route('/web/session/logout', type='http', auth="none")
def logout(self, redirect='/web'):
""" Logs out the current user by clearing their session. """
user = request.env['res.users'].with_user(1).search(
[('id', '=', request.session.uid)])
user._clear_session() # Clear user session
request.session.logout(keep_db=True)
return request.redirect(redirect, 303)
@http.route('/clear_all_sessions', type='http', auth="none")
def logout_all(self, redirect='/web', f_uid=False):
""" Log out from all the sessions of the current user """
if f_uid:
user = request.env['res.users'].with_user(1).browse(int(f_uid))
if user:
# Clear session file for the user
session_cleared = clear_session_history(user.sid, f_uid)
if session_cleared:
# Clear user session
user._clear_session()
request.session.logout(keep_db=True)
return request.redirect(redirect, 303)
@http.route('/super/logout_all', type='http', auth="none")
def super_logout_all(self, redirect='/web'):
""" Log out from all the sessions of all users. """
users = request.env['res.users'].with_user(1).search([])
for user in users:
# Clear session file for the user
session_cleared = super_clear_all()
if session_cleared:
user._clear_session() # Clear user session
request.session.logout(keep_db=True)
return request.redirect(redirect, 303)

17
restrict_logins/data/ir_cron_data.xml

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Scheduled action to validate user sessions. -->
<data>
<record model="ir.cron" id="validate_user_sessions">
<field name="name">User: Validate User Sessions</field>
<field name="model_id" ref="model_res_users"/>
<field name="state">code</field>
<field name="code">model._validate_sessions()</field>
<field name="interval_number">1</field>
<field name="interval_type">minutes</field>
<field name="numbercall">-1</field>
<field name="doall" eval="False"/>
<field name="active" eval="True"/>
</record>
</data>
</odoo>

5
restrict_logins/doc/RELEASE_NOTES.md

@ -0,0 +1,5 @@
## Module <restrict_logins>
#### 05.08.2024
#### Version 16.0.1.0.0
##### ADD
- Initial Commit for Restrict Concurrent User Login

24
restrict_logins/models/__init__.py

@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
###############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Cybrosys Technologies (odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU LESSER
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
#
# 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 (LGPL v3) for more details.
#
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
# (LGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
###############################################################################
from . import ir_http
from . import res_users

101
restrict_logins/models/ir_http.py

@ -0,0 +1,101 @@
# -*- coding: utf-8 -*-
###############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Cybrosys Technologies (odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU LESSER
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
#
# 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 (LGPL v3) for more details.
#
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
# (LGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
###############################################################################
import logging
from datetime import datetime, timedelta
import werkzeug
import werkzeug.exceptions
import werkzeug.routing
from odoo import api, http, models, SUPERUSER_ID
from odoo.exceptions import AccessDenied
from odoo.http import request
from odoo.service import security
_logger = logging.getLogger(__name__)
class IrHttp(models.AbstractModel):
""" Extends the Odoo abstract model 'ir.http' for custom HTTP handling."""
_inherit = 'ir.http'
@classmethod
def _authenticate(cls, endpoint):
""" This method updates user session details, checks for session
mismatches and performs necessary updates."""
auth_method = 'none' if http.is_cors_preflight(request, endpoint) else endpoint.routing['auth']
try:
if request.session.uid:
uid = request.session.uid
user_pool = request.env['res.users'].with_user(
SUPERUSER_ID).browse(uid)
# updating session details
def _update_user(u_sid, u_now, u_exp_date, u_uid):
""" Function for updating session details for the
corresponding user."""
if u_uid and u_exp_date and u_sid and u_now:
query = """update res_users set sid = '%s',
last_update = '%s',exp_date = '%s',
logged_in = 'TRUE' where id = %s
""" % (u_sid, u_now, u_exp_date, u_uid)
execute = request.env.cr.execute(query)
sid = request.session.sid
last_update = user_pool.last_update
now = datetime.now()
exp_date = datetime.now() + timedelta(minutes=45)
# Check that the authentication contains bus_inactivity
# request_params = request.params.copy()
# if 'options' in request_params and 'bus_inactivity' in \
# request_params['options']:
# Update session if there is sid mismatch
if uid and user_pool.sid and sid != user_pool.sid:
_update_user(sid, now, exp_date, uid)
else:
# Update if there is no session data and user is active
if not user_pool.last_update and not user_pool.sid and \
not user_pool.logged_in:
_update_user(sid, now, exp_date, uid)
# Update sid and date if last update is above 0.5 min
if last_update:
update_diff = (datetime.now() -
last_update).total_seconds() / 60.0
if uid and (update_diff > 0.5 or sid != user_pool.sid):
_update_user(sid, now, exp_date, uid)
except Exception as e:
_logger.info(
"Exception during updating user session...%s", e)
pass
try:
if request.session.uid is not None:
if not security.check_session(request.session, request.env):
request.session.logout(keep_db=True)
request.env = api.Environment(request.env.cr, None,
request.session.context)
getattr(cls, "_auth_method_%s" % auth_method)()
except (AccessDenied, http.SessionExpiredException,
werkzeug.exceptions.HTTPException):
raise
except Exception:
_logger.info("Exception during request Authentication.",
exc_info=True)
raise AccessDenied()
return auth_method

123
restrict_logins/models/res_users.py

@ -0,0 +1,123 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Cybrosys Technologies (odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU LESSER
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
#
# 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 (LGPL v3) for more details.
#
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
# (LGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
#############################################################################
import logging
from datetime import datetime, timedelta
import pytz
from odoo import SUPERUSER_ID
from odoo import api, fields, models
from odoo.exceptions import AccessDenied
from odoo.http import request
from ..controllers.session import clear_session_history
_logger = logging.getLogger(__name__)
class ResUsers(models.Model):
""" Inheriting 'res.users' for adding fields related session."""
_inherit = 'res.users'
sid = fields.Char(string='Session ID', help="ID of session")
exp_date = fields.Datetime(string='Current Session Expiration',
help="Time of expiring of current session")
logged_in = fields.Boolean(string='Logged In',
help="User is currently logged in")
last_update = fields.Datetime(string="Last Connection",
help="Last log in")
@classmethod
def _login(cls, db, login, password, user_agent_env):
"""
Handles user login authentication.
Notify user if they are already logged in and attempt to log in from
other device.
"""
if not password:
raise AccessDenied()
ip = request.httprequest.environ['REMOTE_ADDR'] if request else 'n/a'
try:
with cls.pool.cursor() as cr:
self = api.Environment(cr, SUPERUSER_ID, {})[cls._name]
with self._assert_can_auth(user=login):
user = self.search(self._get_login_domain(login),
order=self._get_login_order(), limit=1)
if not user:
raise AccessDenied()
user = user.with_user(user)
user._check_credentials(password, user_agent_env)
tz = request.httprequest.cookies.get(
'tz') if request else None
if tz in pytz.all_timezones and (
not user.tz or not user.login_date):
user.tz = tz
# Check sid and exp date
if user.exp_date and user.sid and user.logged_in:
_logger.warning("User %s is already logged in "
"into the system!. Multiple "
"sessions are not allowed for "
"security reasons!" % user.name)
request.env.uid = user.id
raise AccessDenied("already_logged_in")
# Save user session detail if login is successful
user._save_session()
user._update_last_login()
except AccessDenied:
_logger.info("Login failed for db:%s login:%s from %s", db, login,
ip)
raise
_logger.info("Login successful for db:%s login:%s from %s", db, login,
ip)
return user.id
def _clear_session(self):
""" Function for clearing the session details of user."""
self.write({
'sid': False,
'exp_date': False,
'logged_in': False,
'last_update': datetime.now()
})
def _save_session(self):
""" Function for saving session details of the corresponding user."""
exp_date = datetime.utcnow() + timedelta(minutes=45)
sid = request.session.sid
self.with_user(SUPERUSER_ID).write({
'sid': sid,
'exp_date': exp_date,
'logged_in': True,
'last_update': datetime.now()
})
def _validate_sessions(self):
""" Function for validating user sessions."""
users = self.search([('exp_date', '!=', False)])
for user in users:
if user.exp_date < datetime.utcnow():
# Clear session file for the user
session_cleared = clear_session_history(user.sid)
if session_cleared:
user._clear_session() # Clear user session
_logger.info("Cron _validate_session: "
"cleared session user: %s" % (user.name))
else:
_logger.info("Cron _validate_session: failed to "
"clear session user: %s" % (user.name))

BIN
restrict_logins/static/description/assets/icons/check.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

BIN
restrict_logins/static/description/assets/icons/chevron.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 310 B

BIN
restrict_logins/static/description/assets/icons/cogs.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
restrict_logins/static/description/assets/icons/consultation.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
restrict_logins/static/description/assets/icons/ecom-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 576 B

BIN
restrict_logins/static/description/assets/icons/education-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 733 B

BIN
restrict_logins/static/description/assets/icons/hotel-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 911 B

BIN
restrict_logins/static/description/assets/icons/license.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
restrict_logins/static/description/assets/icons/lifebuoy.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
restrict_logins/static/description/assets/icons/logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
restrict_logins/static/description/assets/icons/manufacturing-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 673 B

BIN
restrict_logins/static/description/assets/icons/pos-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 878 B

BIN
restrict_logins/static/description/assets/icons/puzzle.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 653 B

BIN
restrict_logins/static/description/assets/icons/restaurant-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 905 B

BIN
restrict_logins/static/description/assets/icons/service-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 839 B

BIN
restrict_logins/static/description/assets/icons/trading-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 427 B

BIN
restrict_logins/static/description/assets/icons/training.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 627 B

BIN
restrict_logins/static/description/assets/icons/update.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
restrict_logins/static/description/assets/icons/user.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 988 B

BIN
restrict_logins/static/description/assets/icons/wrench.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
restrict_logins/static/description/assets/misc/categories.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
restrict_logins/static/description/assets/misc/check-box.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
restrict_logins/static/description/assets/misc/compass.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
restrict_logins/static/description/assets/misc/corporate.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
restrict_logins/static/description/assets/misc/customer-support.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
restrict_logins/static/description/assets/misc/cybrosys-logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

BIN
restrict_logins/static/description/assets/misc/features.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 589 B

BIN
restrict_logins/static/description/assets/misc/logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
restrict_logins/static/description/assets/misc/pictures.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
restrict_logins/static/description/assets/misc/pie-chart.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
restrict_logins/static/description/assets/misc/right-arrow.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 967 B

BIN
restrict_logins/static/description/assets/misc/star.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
restrict_logins/static/description/assets/misc/support.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
restrict_logins/static/description/assets/misc/whatsapp.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

BIN
restrict_logins/static/description/assets/modules/customer_product_qrcode.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

BIN
restrict_logins/static/description/assets/modules/customized_barcode_generator.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

BIN
restrict_logins/static/description/assets/modules/employee_dynamic_fields.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

BIN
restrict_logins/static/description/assets/modules/list_view_sticky_header.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

BIN
restrict_logins/static/description/assets/modules/odoo_website_helpdesk.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

BIN
restrict_logins/static/description/assets/modules/sm01.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

BIN
restrict_logins/static/description/assets/modules/sm02.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

BIN
restrict_logins/static/description/assets/modules/sm03.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

BIN
restrict_logins/static/description/assets/modules/sm04.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

BIN
restrict_logins/static/description/assets/modules/sm05.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

BIN
restrict_logins/static/description/assets/modules/todo_list.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

BIN
restrict_logins/static/description/assets/screenshots/hero.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 KiB

BIN
restrict_logins/static/description/assets/screenshots/login_1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 926 KiB

BIN
restrict_logins/static/description/assets/screenshots/login_2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 932 KiB

BIN
restrict_logins/static/description/assets/screenshots/login_3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 931 KiB

BIN
restrict_logins/static/description/banner.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

BIN
restrict_logins/static/description/icon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

607
restrict_logins/static/description/index.html

@ -0,0 +1,607 @@
<div style="background-color: #714B67; min-height: 600px; width: 100%; padding: 15px; position: relative;">
<!-- TITLE BAR -->
<div class="d-flex align-items-center justify-content-between"
style="border-bottom: 1px solid #875A7B; padding: 15px; display: flex; justify-content: space-between; align-items: center;">
<img src="assets/misc/cybrosys-logo.png" width="42" height="42"
style="width: 42px; height: 42px;"/>
<div>
<div style="color: #7C7BAD; font-size: 14px; font-family: 'Montserrat', sans-serif; font-weight: bold; background-color: white; display: inline-block; padding: 3px 10px; border-radius: 50px;"
class="mr-2">
<i class="fa fa-check mr-1"></i>Community
</div>
<div style="color: #875A7B; font-size: 14px; font-family: 'Montserrat', sans-serif; font-weight: bold; background-color: white; display: inline-block; padding: 3px 10px; border-radius: 50px;"
class="mr-2">
<i class="fa fa-check mr-1"></i>Enterprise
</div>
<div style="color: #875A7B; font-size: 14px; font-family: 'Montserrat', sans-serif; font-weight: bold; background-color: white; display: inline-block; padding: 3px 10px; border-radius: 50px;"
class="mr-2">
<i class="fa fa-check mr-1"></i>Odoo.sh
</div>
</div>
</div>
<!-- END OF TITLE BAR -->
<div class="container">
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-12">
<!-- APP HERO -->
<h1 style="color: #FFFFFF; font-weight: bolder; font-size: 50px; text-align: center; margin-top: 50px;">
Restrict Concurrent User Login</h1>
<p style="color:#FFFFFF; padding: 8px 15px; text-align: center; font-size: 24px;">
Restrict Concurrent Sessions for Users</p>
<!-- END OF APP HERO -->
<img src="assets/screenshots/hero.gif"
style="width: 75%; height: auto; position: absolute; margin-left: auto; margin-right: auto; top: 45%; left: 12%; right: auto;"/>
</div>
</div>
</div>
</div>
<!-- NAVIGATION SECTION -->
<div class="d-flex align-items-center"
style="border-bottom: 2px solid #714B67; padding: 15px 0px; margin-top: 300px;">
<div class="d-flex justify-content-center align-items-center mr-2"
style="background-color: #F5F5F5; border-radius: 0px; width: 40px; height: 40px;">
<img src="assets/misc/compass.png"/>
</div>
<h2 class="mt-2"
style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;">
Explore This Module</h2>
</div>
<div class="row my-4" style="font-family: 'Montserrat', sans-serif;">
<div class="col-sm-12 col-md-6 my-3">
<a href="#overview">
<div class="d-flex justify-content-between align-items-center"
style="background-color: #f5f5f5; padding: 30px; width: 100%;">
<div>
<span style="color: #714B67; font-size: 24px; font-weight: 500; display: block;">Overview</span>
<span
style="color: #714B67; font-size: 16px; font-weight: 400; color:#282F33; display: block;">Learn more about this module</span>
</div>
<img src="assets/misc/right-arrow.png" width="36" height="36"/>
</div>
</a>
</div>
<div class="col-sm-12 col-md-6 my-3">
<a href="#features">
<div class="d-flex justify-content-between align-items-center"
style="background-color: #f5f5f5; padding: 30px; width: 100%;">
<div>
<span style="color: #714B67; font-size: 24px; font-weight: 500; display: block;">Features</span>
<span
style="color: #714B67; font-size: 16px; font-weight: 400; color:#282F33; display: block;">View features of this module</span>
</div>
<img src="assets/misc/right-arrow.png" width="36" height="36"/>
</div>
</a>
</div>
<div class="col-sm-12 col-md-6 my-3">
<a href="#screenshots">
<div class="d-flex justify-content-between align-items-center"
style="background-color: #f5f5f5; padding: 30px; width: 100%;">
<div>
<span style="color: #714B67; font-size: 24px; font-weight: 500; display: block;">Screenshots</span>
<span
style="color: #714B67; font-size: 16px; font-weight: 400; color:#282F33; display: block;">See key screenshots of this module</span>
</div>
<img src="assets/misc/right-arrow.png" width="36" height="36"/>
</div>
</a>
</div>
</div>
<!-- END OF NAVIGATION SECTION -->
<!-- OVERVIEW SECTION -->
<div class="d-flex align-items-center"
style="border-bottom: 2px solid #714B67; padding: 15px 0px;" id="overview">
<div class="d-flex justify-content-center align-items-center mr-2"
style="background-color: #F5F5F5; border-radius: 0px; width: 40px; height: 40px;">
<img src="assets/misc/pie-chart.png"/>
</div>
<h2 class="mt-2"
style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;">
Overview
</h2>
</div>
<div class="row"
style="font-family: 'Montserrat', sans-serif; font-weight: 400; font-size: 14px; line-height: 200%;">
<div class="col-sm-12 py-4">
This module is designed to enhance security by restricting users from
having
multiple concurrent logins. If a user is already logged in on one
device,
they will be prevented from logging in on another. Additionally, the
module
offers the capability to force logout for users. Inactive sessions will
automatically expire after 45 minutes, further enhancing security
measures.
</div>
</div>
<!-- END OF OVERVIEW SECTION -->
<!-- FEATURES SECTION -->
<div class="d-flex align-items-center"
style="border-bottom: 2px solid #714B67; padding: 15px 0px;" id="features">
<div class="d-flex justify-content-center align-items-center mr-2"
style="background-color: #F5F5F5; border-radius: 0px; width: 40px; height: 40px;">
<img src="assets/misc/features.png"/>
</div>
<h2 class="mt-2"
style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;">
Features
</h2>
</div>
<div class="row"
style="font-family: 'Montserrat', sans-serif; font-weight: 400; font-size: 14px; line-height: 200%;">
<div class="col-sm-12 col-md-6">
<div class="d-flex align-items-center"
style="margin-top: 40px; margin-bottom: 40px">
<img src="assets/misc/check-box.png" class="mr-2"/>
<span style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Restrict Concurrent Sessions for Users</span>
<p style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important;
position:absolute; left: 58px; bottom: 123px;">
Restrict Concurrent Sessions to enhance security.</p>
</div>
<div class="d-flex align-items-center"
style="margin-top: 30px; margin-bottom: 30px">
<img src="assets/misc/check-box.png" class="mr-2"/>
<span style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Force User Logout</span>
<p style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important;
position:absolute; left: 58px; bottom: 52px;">
Option to log out from all logged in devices.</p>
</div>
<div class="d-flex align-items-center"
style="margin-top: 30px; margin-bottom: 30px">
<img src="assets/misc/check-box.png" class="mr-2"/>
<span style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Automatic Session Expiration</span>
<p style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important;
position:absolute; left: 58px; bottom: -10px;">
Automatic Session Expiration After 45 Minutes.</p>
</div>
</div>
</div>
<!-- END OF FEATURES SECTION -->
<!-- SCREENSHOTS SECTION -->
<div class="d-flex align-items-center"
style="border-bottom: 2px solid #714B67; padding: 15px 0px;"
id="screenshots">
<div class="d-flex justify-content-center align-items-center mr-2"
style="background-color: #F5F5F5; border-radius: 0px; width: 40px; height: 40px;">
<img src="assets/misc/pictures.png"/>
</div>
<h2 class="mt-2"
style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;">
Screenshots
</h2>
</div>
<div class="row">
<div class="col-sm-12">
<div style="display: block; margin: 30px auto;">
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">
Log in to Odoo</h3>
<p style="font-weight: 400; font-family: 'Montserrat', sans-serif; font-size: 14px;">
After installation, Log in to Odoo.</p>
<img src="assets/screenshots/login_1.png" class="img-thumbnail">
</div>
<div style="display: block; margin: 30px auto;">
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">
Alert When the User is Logged in from Additional Devices.</h3>
<p style="font-weight: 400; font-family: 'Montserrat', sans-serif; font-size: 14px;">
When attempting to Log in, a notification will appear if the
user is already logged in.</p>
<img src="assets/screenshots/login_2.png" class="img-thumbnail">
</div>
<div style="display: block; margin: 30px auto;">
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">
Option for Forced Logout.</h3>
<p style="font-weight: 400; font-family: 'Montserrat', sans-serif; font-size: 14px;">
Enable the ability to force logout from devices that are already
logged in.</p>
<img src="assets/screenshots/login_3.png" class="img-thumbnail">
</div>
</div>
</div>
<!-- END OF SCREENSHOTS SECTION -->
<!-- RELATED PRODUCTS -->
<div class="d-flex align-items-center" style="border-bottom: 2px solid #714B67; padding: 15px 0px;">
<div class="d-flex justify-content-center align-items-center mr-2"
style="background-color: #F5F5F5; border-radius: 0px; width: 40px; height: 40px;">
<img src="assets/misc/categories.png" />
</div>
<h2 class="mt-2" style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;">Related
Products
</h2>
</div>
<div class="row">
<div class="col-sm-12">
<div id="demo1" class="row carousel slide" data-ride="carousel">
<!-- The slideshow -->
<div class="carousel-inner" style="padding: 30px;">
<div class="carousel-item" style="min-height: 198.656px;">
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left">
<a href="https://apps.odoo.com/apps/modules/16.0/advanced_chatter_view/" target="_blank">
<div style="border-radius:10px">
<img class="img img-responsive center-block" style="border-radius: 0px;"
src="assets/modules/sm01.png">
</div>
</a>
</div>
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left">
<a href="https://apps.odoo.com/apps/modules/16.0/hide_chatter/#" target="_blank">
<div style="border-radius:10px">
<img class="img img-responsive center-block" style="border-radius: 0px;"
src="assets/modules/sm02.jpg">
</div>
</a>
</div>
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left">
<a href="https://apps.odoo.com/apps/modules/16.0/email_id_validation/#" target="_blank">
<div style="border-radius:10px">
<img class="img img-responsive center-block" style="border-radius: 0px;"
src="assets/modules/sm03.png">
</div>
</a>
</div>
</div>
<div class="carousel-item active" style="min-height: 198.656px;">
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left">
<a href="https://apps.odoo.com/apps/modules/16.0/activity_reminder/" target="_blank">
<div style="border-radius:10px">
<img class="img img-responsive center-block" style="border-radius: 0px;"
src="assets/modules/sm04.png">
</div>
</a>
</div>
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left">
<a href="https://apps.odoo.com/apps/modules/16.0/autosuggestion_in_discuss/" target="_blank">
<div style="border-radius:10px">
<img class="img img-responsive center-block" style="border-radius: 0px;"
src="assets/modules/sm05.png">
</div>
</a>
</div>
</div>
</div>
<!-- Left and right controls -->
<a class="carousel-control-prev" href="#demo1" data-slide="prev" style="width:35px; color:#000"> <span
class="carousel-control-prev-icon"><i class="fa fa-chevron-left" style="font-size:24px"></i></span>
</a> <a class="carousel-control-next" href="#demo1" data-slide="next" style="width:35px; color:#000">
<span class="carousel-control-next-icon"><i class="fa fa-chevron-right"
style="font-size:24px"></i></span>
</a>
</div>
</div>
</div>
<!-- END OF RELATED PRODUCTS -->
<!-- OUR SERVICES -->
<div class="d-flex align-items-center"
style="border-bottom: 2px solid #714B67; padding: 15px 0px;">
<div class="d-flex justify-content-center align-items-center mr-2"
style="background-color: #F5F5F5; border-radius: 0px; width: 40px; height: 40px;">
<img src="assets/misc/star.png"/>
</div>
<h2 class="mt-2"
style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;">
Our Services
</h2>
</div>
<div class="container my-5">
<div class="row">
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4">
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
style="background-color: #1dd1a1 !important; border-radius: 15px !important; height: 80px; width: 80px;">
<img src="assets/icons/cogs.png" class="img-responsive"
height="48px" width="48px">
</div>
<h6 class="text-center"
style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;">
Odoo
Customization</h6>
</div>
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4">
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
style="background-color: #ff6b6b !important; border-radius: 15px !important; height: 80px; width: 80px;">
<img src="assets/icons/wrench.png" class="img-responsive"
height="48px" width="48px">
</div>
<h6 class="text-center"
style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;">
Odoo
Implementation</h6>
</div>
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4">
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
style="background-color: #6462CD !important; border-radius: 15px !important; height: 80px; width: 80px;">
<img src="assets/icons/lifebuoy.png" class="img-responsive"
height="48px" width="48px">
</div>
<h6 class="text-center"
style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;">
Odoo
Support</h6>
</div>
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4">
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
style="background-color: #ffa801 !important; border-radius: 15px !important; height: 80px; width: 80px;">
<img src="assets/icons/user.png" class="img-responsive"
height="48px" width="48px">
</div>
<h6 class="text-center"
style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;">
Hire
Odoo
Developer</h6>
</div>
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4">
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
style="background-color: #54a0ff !important; border-radius: 15px !important; height: 80px; width: 80px;">
<img src="assets/icons/puzzle.png" class="img-responsive"
height="48px" width="48px">
</div>
<h6 class="text-center"
style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;">
Odoo
Integration</h6>
</div>
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4">
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
style="background-color: #6d7680 !important; border-radius: 15px !important; height: 80px; width: 80px;">
<img src="assets/icons/update.png" class="img-responsive"
height="48px" width="48px">
</div>
<h6 class="text-center"
style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;">
Odoo
Migration</h6>
</div>
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4">
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
style="background-color: #786fa6 !important; border-radius: 15px !important; height: 80px; width: 80px;">
<img src="assets/icons/consultation.png" class="img-responsive"
height="48px" width="48px">
</div>
<h6 class="text-center"
style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;">
Odoo
Consultancy</h6>
</div>
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4">
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
style="background-color: #f8a5c2 !important; border-radius: 15px !important; height: 80px; width: 80px;">
<img src="assets/icons/training.png" class="img-responsive"
height="48px" width="48px">
</div>
<h6 class="text-center"
style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;">
Odoo
Implementation</h6>
</div>
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4">
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
style="background-color: #e6be26 !important; border-radius: 15px !important; height: 80px; width: 80px;">
<img src="assets/icons/license.png" class="img-responsive"
height="48px" width="48px">
</div>
<h6 class="text-center"
style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;">
Odoo
Licensing Consultancy</h6>
</div>
</div>
</div>
<!--END OF OUR SERVICES -->
<!-- OUR INDUSTRIES -->
<div class="d-flex align-items-center"
style="border-bottom: 2px solid #714B67; padding: 15px 0px;">
<div class="d-flex justify-content-center align-items-center mr-2"
style="background-color: #F5F5F5; border-radius: 0px; width: 40px; height: 40px;">
<img src="assets/misc/corporate.png"/>
</div>
<h2 class="mt-2"
style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;">
Our
Industries
</h2>
</div>
<div class="container my-5">
<div class="row">
<div class="col-lg-3">
<div class="my-4 d-flex flex-column justify-content-center"
style="background-color: #f6f8f9 !important; border-radius: 0px; padding: 2rem !important; height: 250px !important;">
<img src="assets/icons/trading-black.png"
class="img-responsive mb-3" height="48px" width="48px">
<h5 style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;">
Trading
</h5>
<p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;">
Easily procure
and
sell your products</p>
</div>
</div>
<div class="col-lg-3">
<div class="my-4 d-flex flex-column justify-content-center"
style="background-color: #f6f8f9 !important; border-radius: 0px; padding: 2rem !important; height: 250px !important;">
<img src="assets/icons/pos-black.png"
class="img-responsive mb-3" height="48px" width="48px">
<h5 style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;">
POS
</h5>
<p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;">
Easy
configuration
and convivial experience</p>
</div>
</div>
<div class="col-lg-3">
<div class="my-4 d-flex flex-column justify-content-center"
style="background-color: #f6f8f9 !important; border-radius: 0px; padding: 2rem !important; height: 250px !important;">
<img src="assets/icons/education-black.png"
class="img-responsive mb-3" height="48px" width="48px">
<h5 style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;">
Education
</h5>
<p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;">
A platform for
educational management</p>
</div>
</div>
<div class="col-lg-3">
<div class="my-4 d-flex flex-column justify-content-center"
style="background-color: #f6f8f9 !important; border-radius: 0px; padding: 2rem !important; height: 250px !important;">
<img src="assets/icons/manufacturing-black.png"
class="img-responsive mb-3" height="48px" width="48px">
<h5 style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;">
Manufacturing
</h5>
<p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;">
Plan, track and
schedule your operations</p>
</div>
</div>
<div class="col-lg-3">
<div class="my-4 d-flex flex-column justify-content-center"
style="background-color: #f6f8f9 !important; border-radius: 0px; padding: 2rem !important; height: 250px !important;">
<img src="assets/icons/ecom-black.png"
class="img-responsive mb-3" height="48px" width="48px">
<h5 style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;">
E-commerce &amp; Website
</h5>
<p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;">
Mobile
friendly,
awe-inspiring product pages</p>
</div>
</div>
<div class="col-lg-3">
<div class="my-4 d-flex flex-column justify-content-center"
style="background-color: #f6f8f9 !important; border-radius: 0px; padding: 2rem !important; height: 250px !important;">
<img src="assets/icons/service-black.png"
class="img-responsive mb-3" height="48px" width="48px">
<h5 style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;">
Service Management
</h5>
<p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;">
Keep track of
services and invoice</p>
</div>
</div>
<div class="col-lg-3">
<div class="my-4 d-flex flex-column justify-content-center"
style="background-color: #f6f8f9 !important; border-radius: 0px; padding: 2rem !important; height: 250px !important;">
<img src="assets/icons/restaurant-black.png"
class="img-responsive mb-3" height="48px" width="48px">
<h5 style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;">
Restaurant
</h5>
<p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;">
Run your bar or
restaurant methodically</p>
</div>
</div>
<div class="col-lg-3">
<div class="my-4 d-flex flex-column justify-content-center"
style="background-color: #f6f8f9 !important; border-radius: 0px; padding: 2rem !important; height: 250px !important;">
<img src="assets/icons/hotel-black.png"
class="img-responsive mb-3" height="48px" width="48px">
<h5 style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;">
Hotel Management
</h5>
<p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;">
An
all-inclusive
hotel management application</p>
</div>
</div>
</div>
</div>
<!--END OF OUR INDUSTRIES -->
<!-- SUPPORT -->
<div class="d-flex align-items-center"
style="border-bottom: 2px solid #714B67; padding: 15px 0px;">
<div class="d-flex justify-content-center align-items-center mr-2"
style="background-color: #F5F5F5; border-radius: 0px; width: 40px; height: 40px;">
<img src="assets/misc/customer-support.png"/>
</div>
<h2 class="mt-2"
style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;">
Support
</h2>
</div>
<div class="container mt-5">
<div class="row">
<div class="col-sm-12 col-md-6">
<div style="background-color: #F6F8F9; padding: 30px; display: flex; align-items: center;">
<div class="mr-4 d-flex justify-content-center align-items-center"
style="background-color: #714B67; display: inline-block; height: 70px; width: 70px; display: flex; align-items: center; justify-content: center;">
<img src="assets/misc/support.png" height="48" width="48"
style="width: 42px; height: 42px;"/>
</div>
<div>
<h4>Need Help?</h4>
<p style="line-height: 100%;">Got questions or need help?
Get in touch.</p>
<a href="mailto:odoo@cybrosys.com">
<p style="font-weight: 400; font-size: 28px; line-height: 80%; color: #714B67;">
odoo@cybrosys.com</p>
</a>
</div>
</div>
</div>
<div class="col-sm-12 col-md-6">
<div style="background-color: #F6F8F9; padding: 30px; display: flex; align-items: center;">
<div class="mr-4 d-flex justify-content-center align-items-center"
style="background-color: #2AC44D; display: inline-block; height: 70px; width: 70px; display: flex; align-items: center; justify-content: center;">
<img src="assets/misc/whatsapp.png" height="52" width="52"
style="width: 52px; height: 52px;"/>
</div>
<div>
<h4>WhatsApp</h4>
<p style="line-height: 100%;">Say hi to us on WhatsApp!</p>
<a href="https://api.whatsapp.com/send?phone=918606827707">
<p style="font-weight: 400; font-size: 28px; line-height: 80%; color: #714B67;">
+91 86068
27707</p>
</a>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-12 my-5 d-flex justify-content-center align-items-center">
<img src="assets/misc/logo.png" width="144" height="31"
style="width:144px; height: 31px; margin-top: 40px;"/>
</div>
</div>
</div>
<!-- END OF SUPPORT -->

15
restrict_logins/views/login_clear_session_template.xml

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Extends the template to display a link for users to log out from other devices positioned after an alert message in the 'web.login' template. -->
<template id="login_clear_session" name="Clear User Sessions" inherit_id="web.login">
<xpath expr="//p[hasclass('alert-danger')]" position="after">
<t t-if="logout_all">
<div class="justify-content-between mt-2 d-flex small">
<a style="margin-left: auto; margin-right: auto;" t-att-href="'/clear_all_sessions?f_uid='+str(failed_uid)">
Logout from other devices.
</a>
</div>
</t>
</xpath>
</template>
</odoo>

25
restrict_logins/views/res_users_views.xml

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Adding fields related to session in 'res.users' tree view. -->
<record id="view_users_tree" model="ir.ui.view">
<field name="name">res.users.view.tree.inherit.restrict.logins</field>
<field name="model">res.users</field>
<field name="inherit_id" ref="base.view_users_tree"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='login_date']" position="attributes">
<attribute name="string">Last Login</attribute>
<attribute name="readonly">1</attribute>
</xpath>
<xpath expr="//field[@name='login_date']" position="after">
<field name="last_update" readonly="1"/>
</xpath>
<xpath expr="//field[@name='login_date']" position="before">
<field name="logged_in" string="Logged In" readonly="1"/>
<field name="exp_date" readonly="1"/>
</xpath>
</field>
</record>
</odoo>
Loading…
Cancel
Save