"""Zendesk logic module.""" import json from time import time from uuid import uuid4 import jwt from owsresponse import response from pythonfeatures import pythonfeatures from pythonfeatures.constants import split as split_constants from users import config, constants from users.models import identities class ConfigError(Exception): """Configuration error.""" def _get_branded_config(key: str, brand: str): secrets = getattr(config, key, None) if not secrets: raise ConfigError(f'{key} must be set') try: decoded_secrets = json.loads(secrets) except json.decoder.JSONDecodeError as e: raise ConfigError(f'{key} must be json') from e if brand not in decoded_secrets: raise ConfigError(f'{key} is not defined for brand {brand}') return decoded_secrets[brand] def generate_zendesk_token_v1(identity_id, brand_experience): """Generate zendesk token for Classic Web Widget. Params: identity_id (str): The identity id. Returns: response.Response: containing zendesk jwt token. """ identity_res = identities.get_identity(identity_id) if not identity_res: return identity_res identity_data = identity_res.message brand = brand_experience or identity_data.get('default_brand') if not brand: return response.create_fatal_response('Can not get brand for identity') try: secret = _get_branded_config('ZENDESK_SECRET_KEY', brand) except ConfigError as e: return response.create_fatal_response(str(e)) payload = { 'name': identity_data['name'], 'email': identity_data['email'], 'iat': int(time()), 'jti': str(uuid4()), } encoded = jwt.encode(payload, secret, algorithm='HS256') return response.Response({'value': encoded}) def generate_zendesk_token_v2(identity_id, brand_experience): """Generate zendesk token for Messaging Widget. Params: identity_id (str): The identity id. Returns: response.Response: containing zendesk jwt token. """ identity_res = identities.get_identity(identity_id) if not identity_res: return identity_res identity_data = identity_res.message brand = brand_experience or identity_data.get('default_brand') if not brand: return response.create_fatal_response('Can not get brand for identity') try: secret = _get_branded_config('ZENDESK_MESSAGING_SECRET_KEY', brand) kid = _get_branded_config('ZENDESK_KID', brand) except ConfigError as e: return response.create_fatal_response(str(e)) payload = { 'external_id': identity_data['id'], 'email': identity_data['email'], 'email_verified': True, 'name': identity_data['name'], 'scope': 'user', } exclude_id = ( pythonfeatures.get_single_feature_by_attributes( constants.FEATURE_ZENDESK_WIDGET_NO_EXTERNAL_ID, {'identity_id': identity_id} ).message == split_constants.FEATURE_ENABLED ) if exclude_id: payload.pop('external_id') headers = { 'alg': 'HS256', 'typ': 'JWT', 'kid': kid, } encoded = jwt.encode(payload, secret, headers=headers) return response.Response({'value': encoded}) JWT_GENERATOR_MAP = { constants.ZENDESK_CLASSIC_WEB_WIDGET_TOKEN_VERSION: generate_zendesk_token_v1, constants.ZENDESK_MESSAGING_WIDGET_TOKEN_VERSION: generate_zendesk_token_v2, } def generate_zendesk_token(identity_id, brand_experience, token_version): """Generate zendesk token. Params: identity_id (str): The identity id. brand_experience (str): The current brand. token_version (str): Version of the JWT generator to use. (Classic Web Widget or Web Widget) Returns: response.Response: containing zendesk jwt token. """ if token_version not in JWT_GENERATOR_MAP: return response.create_error_response( code=constants.ERROR_CODE_VALIDATION_ERROR, message=f'"{token_version}" is not supported.', ) return JWT_GENERATOR_MAP[token_version](identity_id, brand_experience)