# By: Riasat Ullah
# This file contains all types of user and organization permission codes.
# All designated codes must be a power of 2.

from utils import constants, display_component_names as cnm, errors, roles
import configuration

# permission types
PERMISSION_TYPE_ORG = 1
PERMISSION_TYPE_USER = 2

# organization permissions; the list is comprehensive to accommodate for api key permissions
ORG_SUSPENSION = 4
ORG_SMS_PERMISSION = 8
ORG_VOICE_CALL_PERMISSION = 16
ORG_INCIDENTS_PERMISSION = 32
ORG_INCIDENT_REDACTION_PERMISSION = 64
ORG_PRE_SCHEDULED_ALERTS_PERMISSION = 128
ORG_ADD_RESPONDERS_PERMISSION = 256
ORG_PAST_INCIDENTS_PERMISSION = 512
ORG_INCIDENTS_API_PERMISSION = 1024
ORG_INCIDENT_STATUS_PERMISSION = 2048
ORG_ROUTINES_PERMISSION = 4096
ORG_ESCALATION_POLICIES_PERMISSION = 8192
ORG_SERVICES_PERMISSION = 16384
ORG_BUSINESS_SERVICES_PERMISSION = 32768
ORG_BUSINESS_SERVICES_SUBSCRIPTION_PERMISSION = 65536
ORG_ROUTING_PERMISSION = 131072
ORG_STATUS_DASHBOARDS_PERMISSION = 262144
ORG_STATUS_DASHBOARDS_CREATE_PERMISSION = 524288
ORG_INTEGRATION_PERMISSION = 1048576
ORG_POSTMORTEM_PERMISSION = 2097152
ORG_ANALYTICS_BASIC_PERMISSION = 4194304
ORG_ANALYTICS_SECONDARY_PERMISSION = 8388608
ORG_ANALYTICS_ADVANCED_PERMISSION = 16777216
ORG_INTELLIGENT_GROUPING_PERMISSION = 33554432
ORG_TEAMS_PERMISSION = 67108864
ORG_ADVANCED_PERMISSIONS_PERMISSION = 134217728
ORG_STAKEHOLDER_PERMISSION = 268435456
ORG_OPERATIONS_CONSOLE_PERMISSION = 536870912
ORG_WORKFLOWS_PERMISSION = 1073741824
ORG_CONFERENCE_BRIDGES_PERMISSION = 2147483648
ORG_CONTEXTUAL_SEARCH_PERMISSION = 4294967296
ORG_LIVE_CALL_ROUTING_PERMISSION = 8589934592
ORG_INTEGRATION_SECONDARY_PERMISSION = 17179869184
ORG_LIVE_CALL_ROUTING_SECONDARY_PERMISSION = 34359738368
ORG_SSO_PERMISSION = 68719476736
ORG_CUSTOM_ACTION_PERMISSION = 137438953472
ORG_INTEGRATION_CUSTOMER_SERVICE_PERMISSION = 274877906944
ORG_INTEGRATION_ITSM_PERMISSION = 549755813888
ORG_RECENT_CHANGES_PERMISSION = 1099511627776
ORG_PEOPLE_PERMISSION = 2199023255552
ORG_GROUPS_PERMISSION = 4398046511104
ORG_CHECKS_BASIC_PERMISSION = 8796093022208
ORG_STATUS_PAGES_PERMISSION = 17592186044416
ORG_STATUS_PAGES_PRIVATE_PERMISSION = 35184372088832
ORG_EXTERNAL_SSO_PERMISSION = 70368744177664


# user permissions
USER_GLOBAL_ROLE_PERMISSION = 4
USER_INCIDENTS_CREATE_PERMISSION = 8
USER_INCIDENTS_VIEW_PERMISSION = 16
USER_INCIDENTS_RESPOND_PERMISSION = 32
USER_INCIDENTS_SUBSCRIBE_PERMISSION = 64
USER_INCIDENTS_REDACT_PERMISSION = 128
USER_COMPONENTS_VIEW_PERMISSION = 256
USER_COMPONENTS_INCLUSION_PERMISSION = 512
USER_COMPONENTS_EDIT_PERMISSION = 1024
USER_COMPONENTS_ADVANCED_ROLE_PERMISSION = 2048
USER_STATUS_DASHBOARD_VIEW_PERMISSION = 4096
USER_STATUS_DASHBOARD_EDIT_PERMISSION = 8192
USER_ANALYTICS_VIEW_PERMISSION = 16384
USER_POSTMORTEMS_VIEW_PERMISSION = 32768
USER_POSTMORTEMS_EDIT_PERMISSION = 65536
USER_ORG_MEMBERS_VIEW_PERMISSION = 131072
USER_ORG_MEMBERS_EDIT_PERMISSION = 262144
USER_GLOBAL_REST_API_KEY_EDIT_PERMISSION = 524288
USER_SPECIFIC_REST_API_KEY_EDIT_PERMISSION = 1048576
USER_ORG_ACCOUNT_MANAGE_PERMISSION = 2097152
USER_TEAMS_VIEW_PERMISSION = 4194304
USER_TEAMS_ROLE_PERMISSION = 8388608
USER_TEAMS_EDIT_PERMISSION = 16777216
USER_CONSOLE_PERMISSION = 33554432
USER_PASSWORD_RESET_PERMISSION = 67108864
USER_STATUS_PAGES_PERMISSION = 134217728

# advanced permissions
COMPONENT_ADVANCED_VIEW_PERMISSION = 1
COMPONENT_ADVANCED_RESPOND_PERMISSION = 2
COMPONENT_ADVANCED_EDIT_PERMISSION = 4


####################################
# Organization Permissions
####################################
def create_free_org_permission():
    '''
    Creates the organization permissions for the Free subscription.
    :return: (str) permissions
    '''
    return bin(PERMISSION_TYPE_ORG | ORG_INCIDENTS_PERMISSION | ORG_PRE_SCHEDULED_ALERTS_PERMISSION |
               ORG_INCIDENTS_API_PERMISSION | ORG_ROUTINES_PERMISSION | ORG_ESCALATION_POLICIES_PERMISSION |
               ORG_SERVICES_PERMISSION | ORG_BUSINESS_SERVICES_PERMISSION | ORG_INTEGRATION_PERMISSION |
               ORG_ANALYTICS_BASIC_PERMISSION)


def create_starter_org_permission():
    '''
    Creates the organization permissions for the Starter subscription.
    :return: (str) permissions
    '''
    base_ = int(create_free_org_permission(), 2)
    return bin(base_ + (ORG_SMS_PERMISSION | ORG_VOICE_CALL_PERMISSION |
                        ORG_INCIDENT_REDACTION_PERMISSION | ORG_ROUTING_PERMISSION |
                        ORG_LIVE_CALL_ROUTING_PERMISSION | ORG_INTEGRATION_SECONDARY_PERMISSION |
                        ORG_CHECKS_BASIC_PERMISSION | ORG_STATUS_PAGES_PERMISSION))


def create_business_org_permission():
    '''
    Creates the organization permissions for the Business subscription.
    :return: (str) permissions
    '''
    base_ = int(create_starter_org_permission(), 2)
    return bin(base_ + (ORG_INCIDENT_STATUS_PERMISSION | ORG_ADD_RESPONDERS_PERMISSION |
                        ORG_CONFERENCE_BRIDGES_PERMISSION | ORG_WORKFLOWS_PERMISSION |
                        ORG_TEAMS_PERMISSION | ORG_ADVANCED_PERMISSIONS_PERMISSION |
                        ORG_ANALYTICS_SECONDARY_PERMISSION | ORG_POSTMORTEM_PERMISSION |
                        ORG_BUSINESS_SERVICES_SUBSCRIPTION_PERMISSION | ORG_STATUS_DASHBOARDS_PERMISSION |
                        ORG_CUSTOM_ACTION_PERMISSION | ORG_INTEGRATION_CUSTOMER_SERVICE_PERMISSION |
                        ORG_INTEGRATION_ITSM_PERMISSION)
               )


def create_digital_operations_org_permission():
    '''
    Creates the organization permissions for the Digital Operations subscription.
    :return: (str) permissions
    '''
    base_ = int(create_business_org_permission(), 2)
    return bin(base_ + (ORG_CONTEXTUAL_SEARCH_PERMISSION | ORG_PAST_INCIDENTS_PERMISSION |
                        ORG_STAKEHOLDER_PERMISSION | ORG_INTELLIGENT_GROUPING_PERMISSION |
                        ORG_ANALYTICS_ADVANCED_PERMISSION | ORG_STATUS_DASHBOARDS_CREATE_PERMISSION |
                        ORG_OPERATIONS_CONSOLE_PERMISSION | ORG_LIVE_CALL_ROUTING_SECONDARY_PERMISSION |
                        ORG_SSO_PERMISSION | ORG_RECENT_CHANGES_PERMISSION | ORG_PEOPLE_PERMISSION |
                        ORG_GROUPS_PERMISSION | ORG_STATUS_PAGES_PRIVATE_PERMISSION | ORG_EXTERNAL_SSO_PERMISSION)
               )


def get_subscription_permission(subscription_id):
    '''
    Get the permissions that an organization can enjoy given the subscription id.
    :param subscription_id: subscription id to get the permission for
    :return: (str) permissions
    '''
    assert subscription_id in configuration.allowed_subscription_ids
    if subscription_id == configuration.free_subscription_id:
        return create_free_org_permission()
    elif subscription_id == configuration.starter_subscription_id:
        return create_starter_org_permission()
    elif subscription_id == configuration.business_subscription_id:
        return create_business_org_permission()
    elif subscription_id == configuration.digital_operations_subscription_id:
        return create_digital_operations_org_permission()


def add_trial_add_ons(org_perm):
    '''
    Add all add-ons to the base permission that will be offered during the trial.
    :return: (str) permissions
    '''
    org_perm = add_status_page_add_on_permission(org_perm, with_private=True)
    return org_perm


def create_mobile_app_access_token_permissions(org_perm):
    '''
    Creates the permissions that the access token issued for a mobile app use will have.
    :param org_perm: the actual permissions of the organization; the subset of the mobile permissions
        will be created based on the original set of permissions
    :return: (str) permissions
    '''
    final_bin = bin(PERMISSION_TYPE_ORG | ORG_INCIDENTS_PERMISSION | ORG_PRE_SCHEDULED_ALERTS_PERMISSION |
                    ORG_ROUTINES_PERMISSION | ORG_ESCALATION_POLICIES_PERMISSION)

    if has_org_permission(org_perm, ORG_INCIDENT_STATUS_PERMISSION):
        final_bin = bin(int(final_bin, 2) + ORG_INCIDENT_STATUS_PERMISSION)

    if has_org_permission(org_perm, ORG_ADD_RESPONDERS_PERMISSION):
        final_bin = bin(int(final_bin, 2) + ORG_ADD_RESPONDERS_PERMISSION)

    if has_org_permission(org_perm, ORG_CONFERENCE_BRIDGES_PERMISSION):
        final_bin = bin(int(final_bin, 2) + ORG_CONFERENCE_BRIDGES_PERMISSION)

    if has_org_permission(org_perm, ORG_WORKFLOWS_PERMISSION):
        final_bin = bin(int(final_bin, 2) + ORG_WORKFLOWS_PERMISSION)

    if has_org_permission(org_perm, ORG_TEAMS_PERMISSION):
        final_bin = bin(int(final_bin, 2) + ORG_TEAMS_PERMISSION)

    # Allow services on mobile apps only for accounts with at least the Business subscription plan.
    if has_org_permission(org_perm, ORG_STATUS_DASHBOARDS_PERMISSION):
        final_bin = bin(int(final_bin, 2) + (ORG_STATUS_DASHBOARDS_PERMISSION | ORG_SERVICES_PERMISSION))

    if has_org_permission(org_perm, ORG_INTELLIGENT_GROUPING_PERMISSION):
        final_bin = bin(int(final_bin, 2) + ORG_INTELLIGENT_GROUPING_PERMISSION)

    if has_org_permission(org_perm, ORG_INTEGRATION_SECONDARY_PERMISSION):
        final_bin = bin(int(final_bin, 2) + ORG_INTEGRATION_SECONDARY_PERMISSION)

    if has_org_permission(org_perm, ORG_CUSTOM_ACTION_PERMISSION):
        final_bin = bin(int(final_bin, 2) + ORG_CUSTOM_ACTION_PERMISSION)

    if has_org_permission(org_perm, ORG_LIVE_CALL_ROUTING_SECONDARY_PERMISSION):
        final_bin = bin(int(final_bin, 2) + ORG_LIVE_CALL_ROUTING_SECONDARY_PERMISSION)

    if has_org_permission(org_perm, ORG_SSO_PERMISSION):
        final_bin = bin(int(final_bin, 2) + ORG_SSO_PERMISSION)

    if has_org_permission(org_perm, ORG_CONTEXTUAL_SEARCH_PERMISSION):
        final_bin = bin(int(final_bin, 2) + ORG_CONTEXTUAL_SEARCH_PERMISSION)

    if has_org_permission(org_perm, ORG_RECENT_CHANGES_PERMISSION):
        final_bin = bin(int(final_bin, 2) + ORG_RECENT_CHANGES_PERMISSION)

    return final_bin


def create_advanced_customer_support_access_token_permissions(org_perm):
    '''
    Creates the permissions that the access token issued for a TaskCall Zendesk app will have.
    :param org_perm: the actual permissions of the organization; the subset of the mobile permissions
        will be created based on the original set of permissions
    :return: (str) permissions
    '''
    final_bin = bin(PERMISSION_TYPE_ORG | ORG_INCIDENTS_PERMISSION)

    if has_org_permission(org_perm, ORG_INCIDENT_STATUS_PERMISSION):
        final_bin = bin(int(final_bin, 2) + ORG_INCIDENT_STATUS_PERMISSION)

    if has_org_permission(org_perm, ORG_ADD_RESPONDERS_PERMISSION):
        final_bin = bin(int(final_bin, 2) + ORG_ADD_RESPONDERS_PERMISSION)

    if has_org_permission(org_perm, ORG_WORKFLOWS_PERMISSION):
        final_bin = bin(int(final_bin, 2) + ORG_WORKFLOWS_PERMISSION)

    # Allow services on mobile apps only for accounts with at least the Business subscription plan.
    if has_org_permission(org_perm, ORG_STATUS_DASHBOARDS_PERMISSION):
        final_bin = bin(int(final_bin, 2) + ORG_STATUS_DASHBOARDS_PERMISSION)

    if has_org_permission(org_perm, ORG_INTEGRATION_CUSTOMER_SERVICE_PERMISSION):
        final_bin = bin(int(final_bin, 2) + ORG_INTEGRATION_CUSTOMER_SERVICE_PERMISSION)

    return final_bin


####################################
# User Permissions
####################################
def create_limited_stakeholder_user_permission(org_perm):
    '''
    Creates permissions for a limited stakeholder.
    :param org_perm: the permissions the organization has
    :return: user permissions
    '''
    if has_org_permission(org_perm, ORG_STAKEHOLDER_PERMISSION):
        base_bin = bin(PERMISSION_TYPE_USER | USER_STATUS_DASHBOARD_VIEW_PERMISSION)
        if has_org_permission(org_perm, ORG_INCIDENT_STATUS_PERMISSION):
            return bin(int(base_bin, 2) + USER_INCIDENTS_SUBSCRIBE_PERMISSION)
        else:
            return base_bin
    else:
        raise PermissionError(errors.err_subscription_rights)


def create_full_stakeholder_user_permission(org_perm):
    '''
    Creates permissions for a full stakeholder.
    :param org_perm: the permissions the organization has
    :return: user permissions
    '''
    base_ = int(create_limited_stakeholder_user_permission(org_perm), 2)
    final_bin = bin(base_ + (USER_ORG_MEMBERS_VIEW_PERMISSION | USER_INCIDENTS_VIEW_PERMISSION |
                             USER_COMPONENTS_VIEW_PERMISSION | USER_ANALYTICS_VIEW_PERMISSION))

    if has_org_permission(org_perm, ORG_POSTMORTEM_PERMISSION):
        final_bin = bin(int(final_bin, 2) + USER_POSTMORTEMS_VIEW_PERMISSION)

    if has_org_permission(org_perm, ORG_TEAMS_PERMISSION):
        final_bin = bin(int(final_bin, 2) + USER_TEAMS_VIEW_PERMISSION)

    return final_bin


def create_limited_user_permission(org_perm):
    '''
    Creates permissions for a limited user.
    :param org_perm: the permissions the organization has
    :return: user permissions
    '''

    # We do not start with a base permission from stakeholder permissions because stakeholder permissions
    # ensure that the organization has the permission to have stakeholders. Thus, if we start with a base
    # permission from full_stakeholder, then we will get an error for subscriptions, like "Starter", that
    # do not have stakeholder permissions. Hence, we have to start with a new base.

    final_bin = bin(PERMISSION_TYPE_USER | USER_INCIDENTS_VIEW_PERMISSION |
                    USER_INCIDENTS_CREATE_PERMISSION | USER_INCIDENTS_RESPOND_PERMISSION |
                    USER_COMPONENTS_INCLUSION_PERMISSION | USER_SPECIFIC_REST_API_KEY_EDIT_PERMISSION |
                    USER_ORG_MEMBERS_VIEW_PERMISSION | USER_COMPONENTS_VIEW_PERMISSION |
                    USER_ANALYTICS_VIEW_PERMISSION)

    if has_org_permission(org_perm, ORG_POSTMORTEM_PERMISSION):
        final_bin = bin(int(final_bin, 2) + (USER_POSTMORTEMS_VIEW_PERMISSION | USER_POSTMORTEMS_EDIT_PERMISSION))

    if has_org_permission(org_perm, ORG_STATUS_DASHBOARDS_PERMISSION):
        final_bin = bin(int(final_bin, 2) + USER_STATUS_DASHBOARD_VIEW_PERMISSION)

    if has_org_permission(org_perm, ORG_INCIDENT_STATUS_PERMISSION):
        final_bin = bin(int(final_bin, 2) + USER_INCIDENTS_SUBSCRIBE_PERMISSION)

    return final_bin


def create_full_user_permission(org_perm):
    '''
    Creates permissions for a full user.
    :param org_perm: the permissions the organization has
    :return: user permissions
    '''
    base_ = int(create_limited_user_permission(org_perm), 2)
    final_bin = bin(base_ + USER_COMPONENTS_EDIT_PERMISSION)

    if has_org_permission(org_perm, ORG_TEAMS_PERMISSION):
        final_bin = bin(int(final_bin, 2) + (USER_TEAMS_VIEW_PERMISSION | USER_TEAMS_EDIT_PERMISSION))

    if has_org_permission(org_perm, ORG_ADVANCED_PERMISSIONS_PERMISSION):
        final_bin = bin(int(final_bin, 2) + USER_TEAMS_ROLE_PERMISSION)

    if has_org_permission(org_perm, ORG_STATUS_PAGES_PERMISSION) and\
            not has_user_permission(final_bin, USER_STATUS_PAGES_PERMISSION):
        final_bin = bin(int(final_bin, 2) + USER_STATUS_PAGES_PERMISSION)

    return final_bin


def create_admin_user_permission(org_perm):
    '''
    Creates permissions for an admin role.
    :param org_perm: the permissions the organization has
    :return: user permissions
    '''
    base_ = int(create_full_user_permission(org_perm), 2)
    final_bin = bin(base_ + (USER_ORG_MEMBERS_EDIT_PERMISSION | USER_GLOBAL_REST_API_KEY_EDIT_PERMISSION |
                             USER_GLOBAL_ROLE_PERMISSION))

    if has_org_advanced_roles(org_perm):
        final_bin = bin(int(final_bin, 2) + USER_COMPONENTS_ADVANCED_ROLE_PERMISSION)

    if has_org_permission(org_perm, ORG_STATUS_DASHBOARDS_CREATE_PERMISSION):
        final_bin = bin(int(final_bin, 2) + USER_STATUS_DASHBOARD_EDIT_PERMISSION)

    return final_bin


def create_owner_user_permission(org_perm):
    '''
    Creates permissions for the owner role.
    :param org_perm: the permissions the organization has
    :return: user permissions
    '''
    base_ = int(create_admin_user_permission(org_perm), 2)
    final_bin = bin(base_ + (USER_INCIDENTS_REDACT_PERMISSION | USER_ORG_ACCOUNT_MANAGE_PERMISSION))

    if has_org_permission(org_perm, ORG_OPERATIONS_CONSOLE_PERMISSION):
        return bin(int(final_bin, 2) + USER_CONSOLE_PERMISSION)
    else:
        return final_bin


def create_restricted_access_user_permission(org_perm):
    '''
    Creates permissions for a restricted access user.
    :param org_perm: the permissions the organization has
    :return: user permissions
    '''
    final_bin = bin(PERMISSION_TYPE_USER | USER_ORG_MEMBERS_VIEW_PERMISSION |
                    USER_INCIDENTS_VIEW_PERMISSION | USER_INCIDENTS_RESPOND_PERMISSION |
                    USER_COMPONENTS_INCLUSION_PERMISSION | USER_SPECIFIC_REST_API_KEY_EDIT_PERMISSION |
                    USER_COMPONENTS_ADVANCED_ROLE_PERMISSION | USER_TEAMS_ROLE_PERMISSION)

    if has_org_permission(org_perm, ORG_STATUS_DASHBOARDS_PERMISSION):
        final_bin = bin(int(final_bin, 2) + USER_STATUS_DASHBOARD_VIEW_PERMISSION)

    if has_org_permission(org_perm, ORG_INCIDENT_STATUS_PERMISSION):
        final_bin = bin(int(final_bin, 2) + USER_INCIDENTS_SUBSCRIBE_PERMISSION)

    return final_bin


def create_observer_user_permission(org_perm):
    '''
    Creates permissions for an observer role.
    :param org_perm: the permissions the organization has
    :return: user permissions
    '''
    base_ = int(create_restricted_access_user_permission(org_perm), 2)
    return bin(base_ + (USER_COMPONENTS_VIEW_PERMISSION | USER_TEAMS_VIEW_PERMISSION |
                        USER_ANALYTICS_VIEW_PERMISSION | USER_POSTMORTEMS_VIEW_PERMISSION))


def create_responder_user_permission(org_perm):
    '''
    Creates permissions for a responder role.
    :param org_perm: the permissions the organization has
    :return: user permissions
    '''
    base_ = int(create_observer_user_permission(org_perm), 2)
    final_bin = bin(base_ + (USER_INCIDENTS_CREATE_PERMISSION | USER_POSTMORTEMS_EDIT_PERMISSION))

    if has_org_permission(org_perm, ORG_STATUS_PAGES_PERMISSION) and\
            not has_user_permission(final_bin, USER_STATUS_PAGES_PERMISSION):
        final_bin = bin(int(final_bin, 2) + USER_STATUS_PAGES_PERMISSION)
    return final_bin


def create_manager_user_permission(org_perm):
    '''
    Creates permissions for a manager role.
    :param org_perm: the permissions the organization has
    :return: user permissions
    '''
    base_ = int(create_responder_user_permission(org_perm), 2)
    final_bin = bin(base_ + USER_COMPONENTS_EDIT_PERMISSION)

    if has_org_permission(org_perm, ORG_TEAMS_PERMISSION):
        final_bin = bin(int(final_bin, 2) + USER_TEAMS_EDIT_PERMISSION)
    return final_bin


def get_user_permission(org_perm, role_id):
    '''
    Get the user permissions for a given role.
    :param org_perm: the permission the organization has
    :param role_id: ID of the user's role
    :return: user permission
    '''
    if role_id == roles.limited_stakeholder_role_id:
        return create_limited_stakeholder_user_permission(org_perm)
    elif role_id == roles.stakeholder_role_id:
        return create_full_stakeholder_user_permission(org_perm)
    elif role_id == roles.limited_user_role_id:
        return create_limited_user_permission(org_perm)
    elif role_id == roles.user_role_id:
        return create_full_user_permission(org_perm)
    elif role_id == roles.admin_role_id:
        return create_admin_user_permission(org_perm)
    elif role_id == roles.owner_role_id:
        return create_owner_user_permission(org_perm)
    elif role_id == roles.restricted_access_role_id:
        return create_restricted_access_user_permission(org_perm)
    elif role_id == roles.observer_role_id:
        return create_observer_user_permission(org_perm)
    elif role_id == roles.responder_role_id:
        return create_responder_user_permission(org_perm)
    elif role_id == roles.manager_role_id:
        return create_manager_user_permission(org_perm)


def create_api_key_permission(api_version):
    '''
    Create the permissions this API key has.
    :return: API key permissions
    '''
    if api_version == constants.api_version_incidents_one:
        return bin(PERMISSION_TYPE_USER | USER_COMPONENTS_VIEW_PERMISSION | USER_INCIDENTS_VIEW_PERMISSION |
                   USER_INCIDENTS_CREATE_PERMISSION | USER_INCIDENTS_RESPOND_PERMISSION |
                   USER_ANALYTICS_VIEW_PERMISSION)
    raise ValueError


####################################
# Component Permissions
####################################
def add_live_call_routing_add_on_permission(org_perm):
    '''
    Adds live call routing add-on permissions to a base organization permission.
    :return: (str) permissions
    '''
    final_bin = org_perm
    if not has_org_permission(org_perm, ORG_LIVE_CALL_ROUTING_SECONDARY_PERMISSION):
        final_bin = bin(int(final_bin, 2) + ORG_LIVE_CALL_ROUTING_SECONDARY_PERMISSION)
    if not has_org_permission(org_perm, ORG_PEOPLE_PERMISSION):
        final_bin = bin(int(final_bin, 2) + ORG_PEOPLE_PERMISSION)
    if not has_org_permission(org_perm, ORG_GROUPS_PERMISSION):
        final_bin = bin(int(final_bin, 2) + ORG_GROUPS_PERMISSION)
    return final_bin


def add_status_page_add_on_permission(org_perm, with_private=False):
    '''
    Add status page add-on permissions to a base organization permission.
    Both regular status pages and private status pages add-on permissions can be added through this function.
    :param org_perm: base organization permission
    :param with_private: True if the private status pages permission is needed; False otherwise
    :return: (str) permissions
    '''
    final_bin = org_perm
    if not has_org_permission(org_perm, ORG_STATUS_PAGES_PERMISSION):
        final_bin = bin(int(final_bin, 2) + ORG_STATUS_PAGES_PERMISSION)

    if with_private and not has_org_permission(org_perm, ORG_STATUS_PAGES_PRIVATE_PERMISSION):
        final_bin = bin(int(final_bin, 2) + ORG_STATUS_PAGES_PRIVATE_PERMISSION)

        if not has_org_permission(org_perm, ORG_EXTERNAL_SSO_PERMISSION):
            final_bin = bin(int(final_bin, 2) + ORG_EXTERNAL_SSO_PERMISSION)

    return final_bin


####################################
# Component Permissions
####################################
def create_advanced_component_permission(role_type):
    '''
    Creates advanced permissions for a user. Instead of returning a binary string, this returns an int.
    This is specifically for components or teams as those permissions need to be checked in the database itself.
    :param role_type: (str) the type of role
    :return: (int) 1 -> observer role;  3 -> responder role;  7 -> manager role
    '''
    assert role_type in roles.advanced_component_roles
    if role_type == roles.observer_role:
        return COMPONENT_ADVANCED_VIEW_PERMISSION
    elif role_type == roles.responder_role:
        return COMPONENT_ADVANCED_VIEW_PERMISSION | COMPONENT_ADVANCED_RESPOND_PERMISSION
    elif role_type == roles.manager_role:
        return COMPONENT_ADVANCED_VIEW_PERMISSION | COMPONENT_ADVANCED_RESPOND_PERMISSION | \
               COMPONENT_ADVANCED_EDIT_PERMISSION


####################################
# Permissions Utilities
####################################
def create_password_reset_token_permission():
    '''
    Create the permissions of a password reset token.
    :return: token permissions
    '''
    return bin(PERMISSION_TYPE_USER | USER_PASSWORD_RESET_PERMISSION)


def add_permission(permissions_binary: str, permission_type: int):
    '''
    Add a new permission onto an existing set of permissions.
    :param permissions_binary: (str) the binary permissions combination
    :param permission_type: (int) the type of permission
    :return: (str) new permissions
    '''
    return bin(int(permissions_binary, 2) + permission_type)


def remove_permission(permissions_binary: str, permission_type: int):
    '''
    Remove a permission from an existing set of permissions.
    :param permissions_binary: (str) the binary permissions combination
    :param permission_type: (int) the type of permission
    :return: (str) new permissions
    '''
    return bin(int(permissions_binary, 2) - permission_type)


def add_suspension(permissions_binary: str):
    '''
    Adds suspension to an organization's permissions.
    :param permissions_binary: (str) the binary permission combination
    :return: (str) new permissions
    '''
    if is_org_suspended(permissions_binary):
        return permissions_binary
    return add_permission(permissions_binary, ORG_SUSPENSION)


def remove_suspension(permissions_binary: str):
    '''
    Removes suspension from an organization's permissions.
    :param permissions_binary: (str) the binary permission combination
    :return: (str) new permissions
    '''
    if is_org_suspended(permissions_binary):
        return remove_permission(permissions_binary, ORG_SUSPENSION)
    return permissions_binary


def has_user_permission(permissions_binary: str, permission_type: int):
    '''
    Checks if combination of user permissions has a specific permission or not.
    :param permissions_binary: (str) the binary permission combination
    :param permission_type: (int) the type of permission
    :return: (boolean) True if it does; False otherwise
    '''
    if (int(permissions_binary, 2) & PERMISSION_TYPE_USER) != PERMISSION_TYPE_USER:
        raise SystemError(errors.err_internal_permission_type_unidentified)

    if (int(permissions_binary, 2) & permission_type) == permission_type:
        return True
    return False


def has_org_permission(permissions_binary: str, permission_type: int):
    '''
    Checks if combination of organization permissions has a specific permission or not.
    :param permissions_binary: (str) the binary permission combination
    :param permission_type: (int) the type of permission
    :return: (boolean) True if it does; False otherwise
    '''
    if (int(permissions_binary, 2) & PERMISSION_TYPE_ORG) != PERMISSION_TYPE_ORG:
        raise SystemError(errors.err_internal_permission_type_unidentified)

    if not is_org_suspended(permissions_binary):
        if (int(permissions_binary, 2) & permission_type) == permission_type:
            return True
    return False


def is_org_suspended(permissions_binary: str):
    '''
    Checks if an organization is suspended or not.
    :param permissions_binary: (str) the binary permission combination
    :return: (boolean) True if it is; False otherwise
    '''
    if (int(permissions_binary, 2) & ORG_SUSPENSION) == ORG_SUSPENSION:
        return True
    else:
        return False


def has_org_advanced_roles(permissions_binary: str):
    '''
    Checks if an organization can have advanced roles or not.
    :param permissions_binary: (str) the binary permission combination
    :return: (boolean) True if it is; False otherwise
    '''
    if has_org_permission(permissions_binary, ORG_ADVANCED_PERMISSIONS_PERMISSION):
        return True
    return False


def has_org_paid_plan(permissions_binary: str):
    '''
    Checks if an organization has a paid plan or not. This does not take trial period into account.
    :param permissions_binary: (str) the binary permission combination
    :return: (boolean) True if it has; False otherwise
    '''
    return has_org_permission(permissions_binary, ORG_INCIDENT_REDACTION_PERMISSION)


def is_user_role_allowed(org_perm, role_id):
    '''
    Checks if an organization is allowed to have a particular user role or not.
    :param org_perm: permissions of the organization
    :param role_id: ID of the requested user role
    :return: (boolean) True if allowed; False otherwise
    '''
    if role_id in roles.user_role_maps.keys():
        has_stkhldr_perm = has_org_permission(org_perm, ORG_STAKEHOLDER_PERMISSION)
        if has_org_advanced_roles(org_perm):
            allowed_role_ids = roles.get_advanced_account_role_ids(has_stkhldr_perm)
        else:
            allowed_role_ids = roles.get_standard_account_role_ids(has_stkhldr_perm)

        if role_id in allowed_role_ids:
            return True
        else:
            return False
    else:
        return False


def convert_user_role_and_permission(new_org_perm, old_role_id):
    '''
    Converts the role of a user from one subscription to another
    and gets the new user permission for the new role.
    :param new_org_perm: new permissions (being converted to) of the organization
    :param old_role_id: (int) the previous role ID the user had
    :return: (tuple) -> new role ID, new user permission
    '''
    assert old_role_id in roles.user_role_maps.keys()

    new_role_id = old_role_id

    # do not allow stakeholders to be upgraded
    if old_role_id in roles.get_stakeholder_user_role_ids() and\
            not has_org_permission(new_org_perm, ORG_STAKEHOLDER_PERMISSION):
        raise SystemError(errors.err_internal_stakeholder_upgrade_attempt)
    else:
        # 1. advanced -> standard
        # 2. standard -> advanced
        if has_org_advanced_roles(new_org_perm):
            if old_role_id in roles.get_standard_account_specific_role_ids():
                new_role_id = roles.standard_to_advanced_role_id_maps[old_role_id]
        else:
            if old_role_id in roles.get_advanced_account_specific_role_ids():
                new_role_id = roles.advanced_to_standard_role_id_maps[old_role_id]

    return new_role_id, get_user_permission(new_org_perm, new_role_id)


def is_user_owner(permissions_binary: str):
    '''
    Checks if a user is the owner of the organization or not given their permissions binary.
    :param permissions_binary: (str) the binary permission combination
    :return: (boolean) True if user is admin; False otherwise
    '''
    if has_user_permission(permissions_binary, USER_ORG_ACCOUNT_MANAGE_PERMISSION):
        return True
    return False


def is_user_admin(permissions_binary: str):
    '''
    Checks if a user is an admin or not given their permissions binary.
    :param permissions_binary: (str) the binary permission combination
    :return: (boolean) True if user is admin; False otherwise
    '''
    if has_user_permission(permissions_binary, USER_GLOBAL_ROLE_PERMISSION):
        return True
    return False


def can_user_respond_to_incidents(permissions_binary: str):
    '''
    Checks if a user has the permission to respond to incidents.
    :param permissions_binary: (str) the binary permissions combination
    :return: (boolean) True if user can respond to incidents; False otherwise
    '''
    if has_user_permission(permissions_binary, USER_INCIDENTS_RESPOND_PERMISSION):
        return True
    return False


def can_user_have_component_role(user_perm, org_perm):
    '''
    Checks if a user is allowed to have an advanced component role.
    :param user_perm: user permissions binary
    :param org_perm: organization permissions binary
    :return: (boolean) True if user has the permission; False otherwise
    '''
    if has_org_advanced_roles(org_perm):
        if has_user_permission(user_perm, USER_COMPONENTS_ADVANCED_ROLE_PERMISSION):
            return True
    return False


def can_user_have_team_role(user_perm, org_perm):
    '''
    Checks if a user is allowed to have an advanced team role.
    :param user_perm: user permissions binary
    :param org_perm: organization permissions binary
    :return: (boolean) True if user has the permission; False otherwise
    '''
    if has_org_advanced_roles(org_perm):
        if has_user_permission(user_perm, USER_TEAMS_ROLE_PERMISSION):
            return True
    return False


def can_user_view_components(permissions_binary: str):
    '''
    Checks if a user has the rights to view components or not.
    :param permissions_binary: (str) the binary permissions combination
    :return: (boolean) True if the user can; False otherwise
    '''
    if has_user_permission(permissions_binary, USER_COMPONENTS_VIEW_PERMISSION):
        return True
    return False


def can_user_edit_components(user_perm, org_perm):
    '''
    Checks if a user has the rights to edit components or not.
    :param user_perm: user permissions binary
    :param org_perm: organization permissions binary
    :return: (boolean) True if the user can; False otherwise
    '''
    if has_org_advanced_roles(org_perm):
        if has_user_permission(user_perm, USER_COMPONENTS_ADVANCED_ROLE_PERMISSION):
            return True
    else:
        if has_user_permission(user_perm, USER_COMPONENTS_EDIT_PERMISSION):
            return True
    return False


def get_user_advanced_check_status(user_perm, org_perm):
    '''
    Checks whether advanced roles should be checked for this user, whether the user has advanced component
    roles permission, and whether the user has team roles permission.
    :param user_perm: user permissions binary
    :param org_perm: organization permissions binary
    :return: (tuple) -> (does org have advanced permissions, does user have component role, does user have team role)
    '''
    if is_user_admin(user_perm) or not has_org_advanced_roles(org_perm):
        is_adv_check_needed = False
    else:
        is_adv_check_needed = True
    return is_adv_check_needed, can_user_have_component_role(user_perm, org_perm),\
        can_user_have_team_role(user_perm, org_perm)


def can_user_view_analytics(permissions_binary: str):
    '''
    Checks if a user has the rights to view organization analytics.
    :param permissions_binary: (str) the binary permissions combination
    :return: (boolean) True if the user can; False otherwise
    '''
    if has_user_permission(permissions_binary, USER_ANALYTICS_VIEW_PERMISSION):
        return True
    return False


def get_user_viewable_components(user_perm: str, org_perm: str):
    '''
    Get the components that are viewable by the user.
    :param user_perm: (str) user permissions binary
    :param org_perm: (str) organization permissions binary
    :return: (list) of names of components that the user can view
    '''
    components = []

    # incidents
    if has_org_permission(org_perm, ORG_INCIDENTS_PERMISSION) and\
            has_user_permission(user_perm, USER_INCIDENTS_VIEW_PERMISSION):
        components.append(cnm.dis_com_incidents)
        components.append(cnm.dis_com_alerts)

    # routines, escalation policies, services, business services,
    # conditional routing, conference bridges, workflows
    if can_user_view_components(user_perm):

        if has_org_permission(org_perm, ORG_ROUTINES_PERMISSION):
            components.append(cnm.dis_com_routines)

        if has_org_permission(org_perm, ORG_ESCALATION_POLICIES_PERMISSION):
            components.append(cnm.dis_com_escalation_policies)

        if has_org_permission(org_perm, ORG_SERVICES_PERMISSION):
            components.append(cnm.dis_com_services)

        if has_org_permission(org_perm, ORG_BUSINESS_SERVICES_PERMISSION):
            components.append(cnm.dis_com_business_services)

        if has_org_permission(org_perm, ORG_SERVICES_PERMISSION):
            components.append(cnm.dis_com_dependency_graph)

        if has_org_permission(org_perm, ORG_RECENT_CHANGES_PERMISSION):
            components.append(cnm.dis_com_recent_changes)

        if has_org_permission(org_perm, ORG_CONFERENCE_BRIDGES_PERMISSION):
            components.append(cnm.dis_com_conference_bridges)

        if has_org_permission(org_perm, ORG_CONTEXTUAL_SEARCH_PERMISSION):
            components.append(cnm.dis_com_tags)

        if has_org_permission(org_perm, ORG_ROUTING_PERMISSION):
            components.append(cnm.dis_com_conditional_routing)

        if has_org_permission(org_perm, ORG_WORKFLOWS_PERMISSION):
            components.append(cnm.dis_com_workflows)

        if has_org_permission(org_perm, ORG_PEOPLE_PERMISSION):
            components.append(cnm.dis_com_itsm_people)

        if has_org_permission(org_perm, ORG_GROUPS_PERMISSION):
            components.append(cnm.dis_com_itsm_groups)

        if has_org_permission(org_perm, ORG_CHECKS_BASIC_PERMISSION):
            components.append(cnm.dis_com_heartbeats)

    # organization users
    if has_user_permission(user_perm, USER_ORG_MEMBERS_VIEW_PERMISSION):
        components.append(cnm.dis_com_users)

    # teams
    if has_org_permission(org_perm, ORG_TEAMS_PERMISSION) and\
            has_user_permission(user_perm, USER_TEAMS_VIEW_PERMISSION):
        components.append(cnm.dis_com_teams)

    # API access
    if has_org_permission(org_perm, ORG_INCIDENTS_API_PERMISSION) and\
            has_user_permission(user_perm, USER_GLOBAL_REST_API_KEY_EDIT_PERMISSION):
        components.append(cnm.dis_com_api_access)

    # live call routing
    if has_org_permission(org_perm, ORG_LIVE_CALL_ROUTING_PERMISSION) and is_user_admin(user_perm):
        components.append(cnm.dis_com_live_call_routing)

    # organization account
    if is_user_owner(user_perm):
        components.append(cnm.dis_com_organization)

    # analytics
    if can_user_view_analytics(user_perm):
        if has_org_permission(org_perm, ORG_ANALYTICS_BASIC_PERMISSION):
            components.append(cnm.dis_com_notification_analytics)
            components.append(cnm.dis_com_incident_analytics)

        if has_org_permission(org_perm, ORG_ANALYTICS_SECONDARY_PERMISSION):
            components.append(cnm.dis_com_user_analytics)
            components.append(cnm.dis_com_team_analytics)

        if has_org_permission(org_perm, ORG_ANALYTICS_ADVANCED_PERMISSION):
            components.append(cnm.dis_com_service_analytics)
            components.append(cnm.dis_com_business_impact_analytics)

        if has_org_permission(org_perm, ORG_LIVE_CALL_ROUTING_PERMISSION) and is_user_admin(user_perm):
            components.append(cnm.dis_com_live_call_analytics)

        # This is put here instead of above in order to display "Postmortem"
        # at the bottom of the analytics dropdown on the web
        if has_org_permission(org_perm, ORG_ANALYTICS_SECONDARY_PERMISSION):
            components.append(cnm.dis_com_postmortem)

    # status dashboard
    if has_org_permission(org_perm, ORG_STATUS_DASHBOARDS_PERMISSION) and\
            has_user_permission(user_perm, USER_STATUS_DASHBOARD_VIEW_PERMISSION):
        components.append(cnm.dis_com_status_dashboard)

    # status pages
    if has_org_permission(org_perm, ORG_STATUS_PAGES_PERMISSION) and\
            has_user_permission(user_perm, USER_STATUS_PAGES_PERMISSION):
        components.append(cnm.dis_com_status_pages)

    # external sso
    if has_org_permission(org_perm, ORG_EXTERNAL_SSO_PERMISSION) and is_user_admin(user_perm):
        components.append(cnm.dis_com_itsm_sso)

    # # operations console
    # if has_org_permission(org_perm, ORG_OPERATIONS_CONSOLE_PERMISSION) and is_user_admin(user_perm):
    #     components.append(cnm.dis_com_operations_console)

    return components


def get_user_editable_components(user_perm: str, org_perm: str):
    '''
    Get the components that are editable by the user.
    :param user_perm: (str) user permissions binary
    :param org_perm: (str) organization permissions binary
    :return: (list) of names of components that the user can view
    '''
    components = []

    # incidents
    if has_org_permission(org_perm, ORG_INCIDENTS_PERMISSION) and can_user_respond_to_incidents(user_perm):
        components.append(cnm.dis_com_incidents)
        components.append(cnm.dis_com_alerts)

    # routines, escalation policies, services, business services,
    # conditional routing, conference bridges, workflows
    if has_user_permission(user_perm, USER_COMPONENTS_EDIT_PERMISSION):

        if has_org_permission(org_perm, ORG_ROUTINES_PERMISSION):
            components.append(cnm.dis_com_routines)

        if has_org_permission(org_perm, ORG_ESCALATION_POLICIES_PERMISSION):
            components.append(cnm.dis_com_escalation_policies)

        if has_org_permission(org_perm, ORG_SERVICES_PERMISSION):
            components.append(cnm.dis_com_services)

        if has_org_permission(org_perm, ORG_BUSINESS_SERVICES_PERMISSION):
            components.append(cnm.dis_com_business_services)

        if has_org_permission(org_perm, ORG_SERVICES_PERMISSION):
            components.append(cnm.dis_com_dependency_graph)

        if has_org_permission(org_perm, ORG_RECENT_CHANGES_PERMISSION) and is_user_admin(user_perm):
            components.append(cnm.dis_com_recent_changes)

        if has_org_permission(org_perm, ORG_ROUTING_PERMISSION):
            components.append(cnm.dis_com_conditional_routing)

        if has_org_permission(org_perm, ORG_WORKFLOWS_PERMISSION):
            components.append(cnm.dis_com_workflows)

        if has_org_permission(org_perm, ORG_CONFERENCE_BRIDGES_PERMISSION):
            components.append(cnm.dis_com_conference_bridges)

        if has_org_permission(org_perm, ORG_CONTEXTUAL_SEARCH_PERMISSION):
            components.append(cnm.dis_com_tags)

        if has_org_permission(org_perm, ORG_PEOPLE_PERMISSION):
            components.append(cnm.dis_com_itsm_people)

        if has_org_permission(org_perm, ORG_GROUPS_PERMISSION):
            components.append(cnm.dis_com_itsm_groups)

        if has_org_permission(org_perm, ORG_CHECKS_BASIC_PERMISSION):
            components.append(cnm.dis_com_heartbeats)

    # organization users
    if has_user_permission(user_perm, USER_ORG_MEMBERS_EDIT_PERMISSION):
        components.append(cnm.dis_com_users)

    # API access
    if has_org_permission(org_perm, ORG_INCIDENTS_API_PERMISSION) and\
            has_user_permission(user_perm, USER_GLOBAL_REST_API_KEY_EDIT_PERMISSION):
        components.append(cnm.dis_com_api_access)

    # teams
    if has_org_permission(org_perm, ORG_TEAMS_PERMISSION) and \
            has_user_permission(user_perm, USER_TEAMS_EDIT_PERMISSION):
        components.append(cnm.dis_com_teams)

    # live call routing
    if has_org_permission(org_perm, ORG_LIVE_CALL_ROUTING_PERMISSION) and is_user_admin(user_perm):
        components.append(cnm.dis_com_live_call_routing)

    # organization
    if is_user_owner(user_perm):
        components.append(cnm.dis_com_organization)

    # postmortem
    if can_user_view_analytics(user_perm) and has_user_permission(user_perm, USER_POSTMORTEMS_EDIT_PERMISSION):
        components.append(cnm.dis_com_postmortem)

    # status dashboard
    if has_org_permission(org_perm, ORG_STATUS_DASHBOARDS_PERMISSION) and \
            has_user_permission(user_perm, USER_STATUS_DASHBOARD_EDIT_PERMISSION):
        components.append(cnm.dis_com_status_dashboard)

    # status pages
    if has_org_permission(org_perm, ORG_STATUS_PAGES_PERMISSION) and\
            has_user_permission(user_perm, USER_STATUS_PAGES_PERMISSION):
        components.append(cnm.dis_com_status_pages)

    # external sso
    if has_org_permission(org_perm, ORG_EXTERNAL_SSO_PERMISSION) and is_user_admin(user_perm):
        components.append(cnm.dis_com_itsm_sso)

    return components


def get_user_accessible_component_features(user_perm, org_perm):
    '''
    Gets the component features that are accessible by the user.
    :param user_perm: user permission
    :param org_perm: organization permission
    :return: (list) of features that the user can access
    '''
    features = []

    # incident features
    if has_org_permission(org_perm, ORG_INCIDENTS_PERMISSION) and\
            has_user_permission(user_perm, USER_INCIDENTS_VIEW_PERMISSION):

        if has_org_permission(org_perm, ORG_ADD_RESPONDERS_PERMISSION):
            features.append(cnm.feat_incidents_add_responders)

        if has_user_permission(user_perm, USER_INCIDENTS_CREATE_PERMISSION):
            features.append(cnm.feat_incidents_create)

        if has_org_permission(org_perm, ORG_PAST_INCIDENTS_PERMISSION):
            features.append(cnm.feat_incidents_past)

        if has_org_permission(org_perm, ORG_INTELLIGENT_GROUPING_PERMISSION):
            features.append(cnm.feat_incidents_similar_ongoing)

        if has_org_permission(org_perm, ORG_INCIDENT_REDACTION_PERMISSION) and\
                has_user_permission(user_perm, USER_INCIDENTS_REDACT_PERMISSION):
            features.append(cnm.feat_incidents_redaction)

        if has_org_permission(org_perm, ORG_WORKFLOWS_PERMISSION):
            features.append(cnm.feat_incidents_workflows)

        if has_org_permission(org_perm, ORG_INCIDENT_STATUS_PERMISSION):
            features.append(cnm.feat_incidents_status)

        if has_org_permission(org_perm, ORG_CONFERENCE_BRIDGES_PERMISSION):
            features.append(cnm.feat_incidents_conference)

        if has_org_permission(org_perm, ORG_INTEGRATION_SECONDARY_PERMISSION):
            features.append(cnm.feat_integrations_secondary)

        if has_org_permission(org_perm, ORG_LIVE_CALL_ROUTING_SECONDARY_PERMISSION) and\
                has_user_permission(user_perm, USER_INCIDENTS_RESPOND_PERMISSION):
            features.append(cnm.feat_incidents_call_back)

        if has_org_permission(org_perm, ORG_CONTEXTUAL_SEARCH_PERMISSION) and\
                has_user_permission(user_perm, USER_INCIDENTS_RESPOND_PERMISSION):
            features.append(cnm.feat_incidents_tagging)

        if has_org_permission(org_perm, ORG_CUSTOM_ACTION_PERMISSION):
            features.append(cnm.feat_integrations_custom_action)

        if has_org_permission(org_perm, ORG_INTEGRATION_CUSTOMER_SERVICE_PERMISSION):
            features.append(cnm.feat_integrations_customer_service)

        if has_org_permission(org_perm, ORG_INTEGRATION_ITSM_PERMISSION):
            features.append(cnm.feat_integrations_itsm)

        if has_org_permission(org_perm, ORG_RECENT_CHANGES_PERMISSION):
            features.append(cnm.feat_incidents_recent_changes)

        # user business service subscription features
        if has_org_permission(org_perm, ORG_BUSINESS_SERVICES_SUBSCRIPTION_PERMISSION) and\
            has_user_permission(user_perm, USER_INCIDENTS_SUBSCRIBE_PERMISSION) and\
                has_user_permission(user_perm, USER_COMPONENTS_VIEW_PERMISSION):
            features.append(cnm.feat_users_business_subscriptions)

        # component flexible role permission
        if has_org_advanced_roles(org_perm) and\
                has_user_permission(user_perm, USER_COMPONENTS_ADVANCED_ROLE_PERMISSION):
            features.append(cnm.feat_users_component_flexible_role)

        # organization SSO authentication permission
        if has_org_permission(org_perm, ORG_SSO_PERMISSION):
            features.append(cnm.feat_sso_authentication)

        # user analytics features - this is needed in analytics where filtering type
        # and additional information is dependent on having secondary analytics view permission
        if has_org_permission(org_perm, ORG_ANALYTICS_SECONDARY_PERMISSION) and\
                has_user_permission(user_perm, USER_ANALYTICS_VIEW_PERMISSION):
            features.append(cnm.feat_analytics_secondary)

        # status pages private feature
        if has_org_permission(org_perm, ORG_STATUS_PAGES_PRIVATE_PERMISSION):
            features.append(cnm.feat_status_pages_private)

        return features


def get_org_allowed_user_roles(org_perm):
    '''
    Get the list of user roles that are allowed for a given subscription.
    :param org_perm: permissions combination of the organization
    :return: (list) of user roles
    '''
    with_stakeholder_roles = has_org_permission(org_perm, ORG_STAKEHOLDER_PERMISSION)
    if has_org_advanced_roles(org_perm):
        return roles.get_advanced_account_roles(with_stakeholder_roles)
    else:
        return roles.get_standard_account_roles(with_stakeholder_roles)
