"""Model layer for graph based identity queries.""" from connector_neo4j import get_session from flask import g from owsresponse import response from notifications.utils.neo4j import strip_query def has_vendor_star_access_settings(identity_id: str) -> bool: """Check if an identity has Vendor * access. Args: identity_id (str): ID of identity to check. Returns: bool: whether or not the identity has access. """ session = get_session() query = """ MATCH (admin:Identity)-[:HAS_PROFILE]->(sp:Profile) -[:HAS_ADMIN_ACCESS_TO]->(r:Vendor) WHERE admin.id = $identity_id AND sp.profileType = 'SettingsProfile' AND r.id = '*' RETURN admin as identity """ ident_result = session.run(query, identity_id=identity_id).single() return ident_result and bool(ident_result.get('identity')) def get_identity_for_label_profile(label_profile_id: int) -> response.Response: """Get identity for label_profile_id. Args: label_profile_id (int): label_profile id or vend_contact id. Returns: Response: identity object. """ session = get_session() query = strip_query( """MATCH (i:Identity)-[:HAS_PROFILE]->(p:Profile {profileType: "LabelProfile"}) WHERE p.profileId = $profileId RETURN i as identity""" ) result = session.run(query, profileId=label_profile_id).single() if not result: return response.create_not_found_response( f'No Identity found with LabelProfile {label_profile_id}.' ) return response.Response(dict(result['identity'])) def get_resource_by_id_type(entity_type: str, entity_id: int) -> response.Response: """Get Resource node based on type and id. Args: entity_type (str): type of node. eg: Vendor or Subaccount. entity_id (int): id for the node. Returns: Response: Resource object. """ session = get_session() query = strip_query( f"""MATCH (r:{entity_type}) WHERE r.id = $id RETURN r as resource LIMIT 1""" ) result = session.run(query, id=entity_id).single() if not result: return response.create_not_found_response(f'No {entity_type} found with id {entity_id}.') return response.Response(dict(result['resource'])) def get_brand_for_identity(identity_id: str) -> response.Response: """Get brand for identity. Args: identity_id (str): id for mention identity Returns: Response: defaultBrand. """ session = get_session() query = strip_query( f"""MATCH (i:Identity) WHERE i.id = '{identity_id}' RETURN i.defaultBrand""" ) result = session.run(query, identity_id=identity_id).single() try: resp = response.Response(result['i.defaultBrand']) except TypeError: g.log.warning( "defaultBrand field doesn`t exist for that identity. Set i.defaultBrand = 'orchard'" ) res = 'orchard' return response.Response(res) return response.Response(resp) def can_administer_profile(admin_id: str, other_id: str, profile_types: list[str]) -> bool: """Check if an admin can administer another identity's profile. Args: admin_id (str): id for admin identity other_id (str): id for other identity profile_types (List[str]): list of allowable profile types Returns: Response (bool): If the admin can administer the other identity. """ profile_types_filter = 'AND op.profileType IN $profile_types' if len(profile_types) > 0 else '' if admin_id == other_id or has_vendor_star_access_settings(admin_id): # If identity has v* access, just verify the profile exists query = f""" MATCH (op:Profile)<-[:HAS_PROFILE]-(other:Identity) WHERE other.id = $other_id {profile_types_filter} WITH collect(other) as rows RETURN size(rows) > 0 """ else: # Else check if the identity has admin access to the profile query = f""" MATCH (admin:Identity)-[:HAS_PROFILE]->(:Profile)-[:HAS_ADMIN_ACCESS_TO]->(r)- [*0..1]->(x)<-[:HAS_ACCESS_TO]-(op:Profile)<-[:HAS_PROFILE]-(other:Identity) WHERE admin.id = $admin_id AND other.id = $other_id {profile_types_filter} WITH collect(other) as rows RETURN size(rows) > 0 """ result = ( get_session() .run(query, admin_id=admin_id, other_id=other_id, profile_types=(profile_types or [])) .single() ) return result and result[0]