A comprehensive Customer Relationship Management software built on Django with extensive customization capabilities
Comprehensive user account management, role-based permissions, team organization, and security configuration with fine-grained access control for all CRM entities and operations.
Extended Django user model with CRM-specific functionality.
class CremeUser(AbstractUser):
"""
Extended user model for Creme CRM with additional fields and CRM-specific functionality.
Attributes:
- displayed_name: str, display name shown in UI
- language: str, preferred language code
- time_zone: str, user's timezone
- role: UserRole, assigned permission role
- is_team: bool, indicates team/shared account
- theme: str, UI theme preference
- json_settings: JSONField, user preferences and configuration
Methods:
- get_teams(): Get teams this user belongs to
- has_perm_to_access(app_label): Check application access permission
- has_perm_to_view(entity): Check entity view permission
- has_perm_to_change(entity): Check entity modification permission
- has_perm_to_delete(entity): Check entity deletion permission
- has_perm_to_link(entity): Check entity relationship permission
- has_perm_to_unlink(entity): Check relationship removal permission
- get_role(): Get user's role with fallback to default
- is_superuser(): Check if user has administrator privileges
"""Hierarchical role system for organizing user permissions.
class UserRole(models.Model):
"""
Role-based permission system defining what users can access and do.
Attributes:
- name: str, role name (e.g., "Sales Manager", "Administrator")
- allowed_apps: ManyToMany, applications this role can access
- admin_4_apps: ManyToMany, applications with administrative rights
- creatable_ctypes: ManyToMany, entity types this role can create
- exportable_ctypes: ManyToMany, entity types this role can export
Methods:
- can_access(app_label): Check if role allows access to application
- can_admin(app_label): Check if role has admin rights for application
- can_create(content_type): Check if role can create entity type
- can_export(content_type): Check if role can export entity type
- get_allowed_apps(): Get list of accessible applications
- get_creatable_models(): Get list of creatable entity types
"""Fine-grained permissions for individual entities and entity types.
class EntityCredentials(models.Model):
"""
Entity-level access permissions that can be granted or forbidden.
Attributes:
- role: UserRole, role these credentials apply to
- value: int, permission bitmask (VIEW|CHANGE|DELETE|LINK|UNLINK)
- set_type: int, whether permissions are ESET_ALL/ESET_OWN/ESET_FILTER
- ctype: ContentType, entity type these permissions apply to
- efilter: EntityFilter, optional filter limiting scope
- forbidden: bool, whether these permissions are forbidden
Permission Constants:
- VIEW = 1: Permission to view entities
- CHANGE = 2: Permission to modify entities
- DELETE = 4: Permission to delete entities
- LINK = 8: Permission to create relationships with entities
- UNLINK = 16: Permission to remove relationships from entities
Set Types:
- ESET_ALL = 1: Apply to all entities of this type
- ESET_OWN = 2: Apply only to entities owned by user
- ESET_FILTER = 3: Apply to entities matching filter
Methods:
- can_view(user, entity): Check if user can view entity
- can_change(user, entity): Check if user can modify entity
- can_delete(user, entity): Check if user can delete entity
- can_link(user, entity): Check if user can create relationships
- can_unlink(user, entity): Check if user can remove relationships
"""
class SetCredentials(models.Model):
"""
Permission sets for bulk credential management.
Attributes:
- role: UserRole, target role for these credentials
- value: int, permission bitmask
- set_type: int, scope of permission application
- ctype: ContentType, entity type
- efilter: EntityFilter, optional entity filter
- forbidden: bool, forbidden permission flag
Methods:
- apply_to_role(role): Apply credential set to role
- get_permissions(): Get list of granted permissions
- matches_entity(entity): Check if entity matches credential scope
"""Team organization and shared permissions.
class Team(CremeUser):
"""
Team entity for organizing users and shared permissions.
Inherits from CremeUser with is_team=True.
Additional Methods:
- get_teammates(): Get users belonging to this team
- add_teammate(user): Add user to team
- remove_teammate(user): Remove user from team
- get_team_permissions(): Get team-specific permissions
- share_entity_with_team(entity): Share entity access with team
"""User data isolation and sandboxing.
class Sandbox(models.Model):
"""
User sandboxing system for data isolation and security.
Attributes:
- uuid: str, unique sandbox identifier
- user: CremeUser, sandbox owner
- type: ContentType, entity type this sandbox applies to
- role: UserRole, role for sandbox-specific permissions
Methods:
- check_perm(user, perm, entity): Check permission within sandbox
- isolate_queryset(queryset, user): Filter queryset for sandbox
- get_isolated_entities(user): Get entities accessible to user
- is_isolated(entity, user): Check if entity is in user's sandbox
"""from creme.creme_core.models import CremeUser, UserRole
from django.contrib.contenttypes.models import ContentType
# Create a role
sales_role = UserRole.objects.create(name="Sales Representative")
# Add application permissions
from creme.persons import get_contact_model
from creme.opportunities import get_opportunity_model
ContactModel = get_contact_model()
OpportunityModel = get_opportunity_model()
# Allow access to persons and opportunities apps
sales_role.allowed_apps.add('persons', 'opportunities', 'activities')
# Allow creating contacts and opportunities
contact_ct = ContentType.objects.get_for_model(ContactModel)
opportunity_ct = ContentType.objects.get_for_model(OpportunityModel)
sales_role.creatable_ctypes.add(contact_ct, opportunity_ct)
# Create user with role
user = CremeUser.objects.create_user(
username='john.sales',
email='john@company.com',
first_name='John',
last_name='Smith',
role=sales_role
)
user.set_password('secure_password123')
user.save()from creme.creme_core.models import EntityCredentials, SetCredentials
# Grant view and change permissions for contacts
EntityCredentials.objects.create(
role=sales_role,
value=EntityCredentials.VIEW | EntityCredentials.CHANGE,
set_type=EntityCredentials.ESET_ALL,
ctype=contact_ct
)
# Grant full permissions for own opportunities only
EntityCredentials.objects.create(
role=sales_role,
value=EntityCredentials.VIEW | EntityCredentials.CHANGE | EntityCredentials.DELETE,
set_type=EntityCredentials.ESET_OWN,
ctype=opportunity_ct
)
# Forbid deleting contacts
EntityCredentials.objects.create(
role=sales_role,
value=EntityCredentials.DELETE,
set_type=EntityCredentials.ESET_ALL,
ctype=contact_ct,
forbidden=True
)# Check user permissions
user = CremeUser.objects.get(username='john.sales')
contact = ContactModel.objects.get(id=1)
# Check various permissions
can_view = user.has_perm_to_view(contact)
can_edit = user.has_perm_to_change(contact)
can_delete = user.has_perm_to_delete(contact)
can_link = user.has_perm_to_link(contact)
print(f"User can view contact: {can_view}")
print(f"User can edit contact: {can_edit}")
print(f"User can delete contact: {can_delete}")
# Check application access
can_access_billing = user.has_perm_to_access('billing')
print(f"User can access billing: {can_access_billing}")# Create team
sales_team = CremeUser.objects.create(
username='sales_team',
email='sales@company.com',
is_team=True,
role=sales_role
)
# Add users to team
sales_users = CremeUser.objects.filter(role=sales_role, is_team=False)
for user in sales_users:
# Create relationship between user and team
from creme.creme_core.models import Relation, RelationType
team_rel = RelationType.objects.get(pk='creme_core-subject_has_team')
Relation.objects.create(
user=user,
subject_entity=user,
object_entity=sales_team,
type=team_rel
)
# Share entity with team
opportunity = OpportunityModel.objects.get(id=1)
share_rel = RelationType.objects.get(pk='creme_core-subject_share')
Relation.objects.create(
user=opportunity.user,
subject_entity=opportunity,
object_entity=sales_team,
type=share_rel
)from creme.creme_core.models import EntityFilter, EntityFilterCondition
# Create manager role with filtered permissions
manager_role = UserRole.objects.create(name="Sales Manager")
# Create filter for high-value opportunities
high_value_filter = EntityFilter.objects.create(
id='high_value_opportunities',
name='High Value Opportunities',
entity_type=opportunity_ct,
filter_type=EntityFilter.EF_REGULAR
)
EntityFilterCondition.objects.create(
filter=high_value_filter,
type=EntityFilterCondition.EFC_FIELD,
name='estimated_sales__gte',
value='10000'
)
# Grant special permissions for high-value opportunities only
EntityCredentials.objects.create(
role=manager_role,
value=EntityCredentials.VIEW | EntityCredentials.CHANGE | EntityCredentials.DELETE,
set_type=EntityCredentials.ESET_FILTER,
ctype=opportunity_ct,
efilter=high_value_filter
)# Set user preferences
user.json_settings = {
'theme': 'dark',
'list_view_page_size': 50,
'default_currency': 'USD',
'email_notifications': True,
'dashboard_config': {
'show_calendar': True,
'show_recent_activities': True,
'show_statistics': False
}
}
user.save()
# Get user preference
def get_user_preference(user, key, default=None):
"""Get user preference with fallback to default."""
return user.json_settings.get(key, default)
# Set user preference
def set_user_preference(user, key, value):
"""Set user preference and save."""
if user.json_settings is None:
user.json_settings = {}
user.json_settings[key] = value
user.save()
# Usage
page_size = get_user_preference(user, 'list_view_page_size', 25)
set_user_preference(user, 'theme', 'light')# Create multiple users from CSV
import csv
def create_users_from_csv(csv_file_path, default_role):
"""Create users in bulk from CSV file."""
created_users = []
with open(csv_file_path, 'r') as file:
reader = csv.DictReader(file)
for row in reader:
user = CremeUser.objects.create_user(
username=row['username'],
email=row['email'],
first_name=row['first_name'],
last_name=row['last_name'],
role=default_role
)
user.set_password(row['password'])
user.save()
created_users.append(user)
return created_users
# Bulk permission assignment
def assign_bulk_permissions(role, app_permissions, entity_permissions):
"""Assign permissions to role in bulk."""
# Add app permissions
role.allowed_apps.add(*app_permissions['allowed'])
if 'admin' in app_permissions:
role.admin_4_apps.add(*app_permissions['admin'])
# Add entity creation permissions
if 'creatable' in entity_permissions:
role.creatable_ctypes.add(*entity_permissions['creatable'])
# Add entity export permissions
if 'exportable' in entity_permissions:
role.exportable_ctypes.add(*entity_permissions['exportable'])
# Usage
sales_permissions = {
'allowed': ['persons', 'opportunities', 'activities', 'commercial'],
'admin': []
}
entity_permissions = {
'creatable': [contact_ct, opportunity_ct],
'exportable': [contact_ct]
}
assign_bulk_permissions(sales_role, sales_permissions, entity_permissions)# Audit user permissions
def audit_user_permissions(user):
"""Generate permission audit report for user."""
report = {
'user': user.username,
'role': user.role.name if user.role else 'No Role',
'is_superuser': user.is_superuser,
'allowed_apps': list(user.role.allowed_apps.values_list('name', flat=True)) if user.role else [],
'admin_apps': list(user.role.admin_4_apps.values_list('name', flat=True)) if user.role else [],
'entity_permissions': []
}
if user.role:
credentials = EntityCredentials.objects.filter(role=user.role)
for cred in credentials:
report['entity_permissions'].append({
'entity_type': cred.ctype.name,
'permissions': cred.get_permissions(),
'scope': cred.get_set_type_display(),
'forbidden': cred.forbidden
})
return report
# Check for security issues
def check_security_issues():
"""Check for common security configuration issues."""
issues = []
# Check for users without roles
users_without_roles = CremeUser.objects.filter(role__isnull=True, is_superuser=False)
if users_without_roles.exists():
issues.append(f"Found {users_without_roles.count()} users without assigned roles")
# Check for overprivileged roles
overprivileged_roles = UserRole.objects.filter(
admin_4_apps__isnull=False
).distinct()
for role in overprivileged_roles:
admin_apps = role.admin_4_apps.count()
if admin_apps > 5: # Arbitrary threshold
issues.append(f"Role '{role.name}' has admin access to {admin_apps} applications")
return issuesThe user management system provides comprehensive security and access control that integrates with all CRM modules while maintaining flexibility for different organizational structures and security requirements.
Install with Tessl CLI
npx tessl i tessl/pypi-creme-crmdocs