249 lines
8 KiB
Python
249 lines
8 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
# These imports will work in an Odoo environment, even if your IDE marks them as not found
|
|
# pylint: disable=import-error
|
|
from odoo import models, fields, api, _
|
|
# pylint: enable=import-error
|
|
|
|
import logging
|
|
from datetime import datetime, timedelta
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
class UnifiAuthSession(models.Model):
|
|
"""Manages authentication sessions for UniFi APIs
|
|
|
|
This model stores authentication tokens, cookies, and session information
|
|
for both the Site Manager API and Controller API. It handles token refresh
|
|
and session validation.
|
|
"""
|
|
_name = 'unifi.auth.session'
|
|
_description = 'UniFi Authentication Session'
|
|
|
|
site_id = fields.Many2one(
|
|
comodel_name='unifi.site',
|
|
string='Site',
|
|
required=True,
|
|
ondelete='cascade',
|
|
help='The site this authentication session belongs to'
|
|
)
|
|
|
|
auth_type = fields.Selection(
|
|
selection=[
|
|
('site_manager', 'Site Manager API'),
|
|
('controller', 'Controller API')
|
|
],
|
|
string='Authentication Type',
|
|
required=True,
|
|
help='Type of authentication'
|
|
)
|
|
|
|
token = fields.Char(
|
|
string='Authentication Token',
|
|
help='Authentication token or cookie'
|
|
)
|
|
|
|
csrf_token = fields.Char(
|
|
string='CSRF Token',
|
|
help='Cross-Site Request Forgery protection token'
|
|
)
|
|
|
|
cookie = fields.Text(
|
|
string='Session Cookie',
|
|
help='Session cookie for Controller API'
|
|
)
|
|
|
|
endpoint = fields.Char(
|
|
string='Authentication Endpoint',
|
|
help='The endpoint used for authentication (e.g. /api/login or /api/auth/login)'
|
|
)
|
|
|
|
is_udm_pro = fields.Boolean(
|
|
string='Is UDM Pro',
|
|
default=False,
|
|
help='Indicates if this session is for a UDM Pro or UDM Pro SE device'
|
|
)
|
|
|
|
expiry = fields.Datetime(
|
|
string='Expiry Date',
|
|
help='Date and time when this session expires'
|
|
)
|
|
|
|
is_valid = fields.Boolean(
|
|
string='Is Valid',
|
|
compute='_compute_is_valid',
|
|
store=False,
|
|
help='Whether this session is currently valid'
|
|
)
|
|
|
|
last_used = fields.Datetime(
|
|
string='Last Used',
|
|
default=fields.Datetime.now,
|
|
help='Date and time when this session was last used'
|
|
)
|
|
|
|
created_at = fields.Datetime(
|
|
string='Created At',
|
|
default=fields.Datetime.now,
|
|
readonly=True,
|
|
help='Date and time when this session was created'
|
|
)
|
|
|
|
@api.depends('expiry')
|
|
def _compute_is_valid(self):
|
|
"""Compute whether the session is valid based on expiry date"""
|
|
now = fields.Datetime.now()
|
|
for session in self:
|
|
if not session.expiry:
|
|
session.is_valid = False
|
|
else:
|
|
session.is_valid = session.expiry > now
|
|
|
|
@api.onchange('auth_type')
|
|
def _onchange_auth_type(self):
|
|
"""Handle changes to auth_type field
|
|
|
|
This method is triggered when the auth_type field is changed.
|
|
It clears fields that are not relevant to the selected authentication type.
|
|
"""
|
|
if self.auth_type == 'site_manager':
|
|
# Clear controller-specific fields
|
|
self.cookie = False
|
|
elif self.auth_type == 'controller':
|
|
# Clear site manager-specific fields
|
|
self.csrf_token = False
|
|
|
|
def validate(self):
|
|
"""Validate the session and refresh if necessary
|
|
|
|
Returns:
|
|
bool: True if the session is valid, False otherwise
|
|
"""
|
|
self.ensure_one()
|
|
|
|
# Check if the session is expired
|
|
if not self.is_valid:
|
|
# Try to refresh the session
|
|
return self.refresh()
|
|
|
|
# Update last used timestamp
|
|
self.write({'last_used': fields.Datetime.now()})
|
|
return True
|
|
|
|
def refresh(self):
|
|
"""Refresh the authentication session
|
|
|
|
Returns:
|
|
bool: True if the session was refreshed successfully, False otherwise
|
|
"""
|
|
self.ensure_one()
|
|
|
|
try:
|
|
if self.auth_type == 'site_manager':
|
|
return self._refresh_site_manager_session()
|
|
elif self.auth_type == 'controller':
|
|
return self._refresh_controller_session()
|
|
return False
|
|
except Exception as e:
|
|
_logger.error('Error refreshing authentication session: %s', str(e))
|
|
return False
|
|
|
|
def _refresh_site_manager_session(self):
|
|
"""Refresh a Site Manager API session
|
|
|
|
Returns:
|
|
bool: True if the session was refreshed successfully, False otherwise
|
|
"""
|
|
# This is a placeholder - will be implemented with actual API calls
|
|
try:
|
|
# Logic to refresh Site Manager API session
|
|
|
|
# Update session data
|
|
self.write({
|
|
'expiry': fields.Datetime.now() + timedelta(hours=1),
|
|
'last_used': fields.Datetime.now()
|
|
})
|
|
|
|
return True
|
|
except (ValueError, TypeError) as e:
|
|
_logger.error('Error refreshing Site Manager session: %s', str(e))
|
|
return False
|
|
except Exception as e:
|
|
_logger.error('Unexpected error refreshing Site Manager session: %s', str(e))
|
|
return False
|
|
|
|
def _refresh_controller_session(self):
|
|
"""Refresh a Controller API session
|
|
|
|
Returns:
|
|
bool: True if the session was refreshed successfully, False otherwise
|
|
"""
|
|
# This is a placeholder - will be implemented with actual API calls
|
|
try:
|
|
# Logic to refresh Controller API session
|
|
|
|
# Update session data
|
|
self.write({
|
|
'expiry': fields.Datetime.now() + timedelta(hours=1),
|
|
'last_used': fields.Datetime.now()
|
|
})
|
|
|
|
return True
|
|
except (ValueError, TypeError) as e:
|
|
_logger.error('Error refreshing Controller session: %s', str(e))
|
|
return False
|
|
except Exception as e:
|
|
_logger.error('Unexpected error refreshing Controller session: %s', str(e))
|
|
return False
|
|
|
|
@api.model
|
|
def create_session(self, site_id, auth_type, token=None, csrf_token=None, cookie=None, expiry=None):
|
|
"""Create a new authentication session
|
|
|
|
Args:
|
|
site_id (int): ID of the site
|
|
auth_type (str): Type of authentication ('site_manager' or 'controller')
|
|
token (str, optional): Authentication token
|
|
csrf_token (str, optional): CSRF token
|
|
cookie (str, optional): Session cookie
|
|
expiry (datetime, optional): Expiry date and time
|
|
|
|
Returns:
|
|
unifi.auth.session: The created session record
|
|
"""
|
|
# Set default expiry if not provided
|
|
if not expiry:
|
|
expiry = fields.Datetime.now() + timedelta(hours=1)
|
|
|
|
# Create the session
|
|
return self.create({
|
|
'site_id': site_id,
|
|
'auth_type': auth_type,
|
|
'token': token,
|
|
'csrf_token': csrf_token,
|
|
'cookie': cookie,
|
|
'expiry': expiry,
|
|
'last_used': fields.Datetime.now(),
|
|
})
|
|
|
|
def invalidate(self):
|
|
"""Invalidate the session
|
|
|
|
Returns:
|
|
bool: True if the session was invalidated successfully, False otherwise
|
|
"""
|
|
self.ensure_one()
|
|
|
|
try:
|
|
# Set expiry to a past date to invalidate the session
|
|
self.write({
|
|
'expiry': fields.Datetime.now() - timedelta(days=1)
|
|
})
|
|
|
|
return True
|
|
except (ValueError, TypeError) as e:
|
|
_logger.error('Error invalidating session: %s', str(e))
|
|
return False
|
|
except Exception as e:
|
|
_logger.error('Unexpected error invalidating session: %s', str(e))
|
|
return False
|