class RolePermission(models.Model): role = models.ForeignKey("UserRole", on_delete=models.CASCADE) permission_fk = models.ForeignKey("SQLPermission", on_delete=models.CASCADE) permission = ForeignValue(permission_fk) # if True allow access to all items # if False only allow access to listed items allow_all = models.BooleanField(default=True) # current max len in 119 chars allowed_items = ArrayField(models.CharField(max_length=256), blank=True, null=True) class Meta: unique_together = [ ("role", "permission_fk") ] constraints = [ models.CheckConstraint( name="users_rolepermission_valid_allow", check=~models.Q(allow_all=True, allowed_items__len__gt=0) ), ] @staticmethod def from_permission_info(role, info): return RolePermission( role=role, permission=info.name, allow_all=info.allow_all, allowed_items=info.allowed_items ) def as_permission_info(self): from corehq.apps.users.models import PermissionInfo allow = PermissionInfo.ALLOW_ALL if self.allow_all else self.allowed_items return PermissionInfo(self.permission, allow=allow)
class NavigationEventAudit(AuditEvent): """ Audit event to track happenings within the system, ie, view access """ params = TruncatingCharField(max_length=4096, blank=True, default='') view_fk = models.ForeignKey( ViewName, null=True, db_index=False, on_delete=models.PROTECT) view = ForeignValue(view_fk, truncate=True) view_kwargs = NullJsonField(default=dict) headers = NullJsonField(default=dict) status_code = models.SmallIntegerField(default=0) @property def description(self): return self.user or "" @property def request_path(self): return f"{self.path}?{self.params}" @classmethod def audit_view(cls, request, user, view_func, view_kwargs): audit = cls.create_audit(request, user) if request.GET: audit.params = request.META.get("QUERY_STRING", "") audit.view = "%s.%s" % (view_func.__module__, view_func.__name__) for k in STANDARD_HEADER_KEYS: header_item = request.META.get(k, None) if header_item is not None: audit.headers[k] = header_item # it's a bit verbose to go to that extreme, TODO: need to have # targeted fields in the META, but due to server differences, it's # hard to make it universal. audit.view_kwargs = view_kwargs return audit
class AuditEvent(models.Model): id = models.BigAutoField(primary_key=True) user = models.CharField(max_length=255, null=True, blank=True) domain = models.CharField(max_length=126, null=True, blank=True) event_date = models.DateTimeField(default=getdate, db_index=True) path = TruncatingCharField(max_length=255, blank=True, default='') ip_address = models.CharField(max_length=45, blank=True, default='') session_key = models.CharField(max_length=255, blank=True, null=True) user_agent_fk = models.ForeignKey(UserAgent, null=True, db_index=False, on_delete=models.PROTECT) user_agent = ForeignValue(user_agent_fk, truncate=True) couch_id = models.CharField(max_length=126, null=True) @property def doc_type(self): return type(self).__name__ @property def description(self): raise NotImplementedError("abstract property") class Meta: abstract = True indexes = [ models.Index(fields=["user", "event_date"]), models.Index(fields=["domain", "event_date"]), ] def __str__(self): return "[%s] %s" % (self.doc_type, self.description) @classmethod def create_audit(cls, request, user): audit = cls() audit.domain = get_domain(request) audit.path = request.path audit.ip_address = get_ip(request) audit.session_key = request.session.session_key audit.user_agent = request.META.get('HTTP_USER_AGENT') if isinstance(user, AnonymousUser): audit.user = None elif user is None: audit.user = None elif isinstance(user, User): audit.user = user.username else: audit.user = user return audit
class AccessAudit(AuditEvent): access_type = models.CharField(max_length=1, choices=ACCESS_CHOICES.items()) http_accept_fk = models.ForeignKey(HttpAccept, null=True, db_index=False, on_delete=models.PROTECT) http_accept = ForeignValue(http_accept_fk, truncate=True) trace_id = models.CharField(max_length=127, null=True, blank=True) class Meta(AuditEvent.Meta): constraints = [ models.UniqueConstraint(fields=['couch_id'], condition=models.Q(couch_id__isnull=False), name="audit_access_couch_10d1b_idx"), ] # Optional (django-ified) settings.AUDIT_TRACE_ID_HEADER set by AuditcareConfig trace_id_header = None @property def description(self): return f"{ACCESS_CHOICES[self.access_type]}: {self.user or ''}" @classmethod def create_audit(cls, request, user, access_type): '''Creates an instance of a Access log.''' audit = super().create_audit(request, user) audit.http_accept = request.META.get('HTTP_ACCEPT') audit.access_type = access_type if cls.trace_id_header is not None: audit.trace_id = request.META.get(cls.trace_id_header) return audit @classmethod def audit_login(cls, request, user, *args, **kwargs): audit = cls.create_audit(request, user, ACCESS_LOGIN) audit.save() @classmethod def audit_login_failed(cls, request, username, *args, **kwargs): audit = cls.create_audit(request, username, ACCESS_FAILED) audit.save() @classmethod def audit_logout(cls, request, user): audit = cls.create_audit(request, user, ACCESS_LOGOUT) audit.save()
class UserAccessLog(models.Model): TYPE_LOGIN = '******' TYPE_LOGOUT = 'logout' TYPE_FAILURE = 'failure' ACTIONS = ((TYPE_LOGIN, 'Login'), (TYPE_LOGOUT, 'Logout'), (TYPE_FAILURE, 'Login Failure')) id = models.BigAutoField(primary_key=True) user_id = models.CharField(max_length=255, db_index=True) action = models.CharField(max_length=20, choices=ACTIONS) ip = models.GenericIPAddressField(blank=True, null=True) user_agent_fk = models.ForeignKey(UserAgent, null=True, on_delete=models.PROTECT, db_column="user_agent_id") user_agent = ForeignValue(user_agent_fk, truncate=True) path = models.CharField(max_length=255, blank=True) timestamp = models.DateTimeField(default=datetime.utcnow) def __str__(self): return f'{self.timestamp}: {self.user_id} - {self.action}'
def get_foreign_names(model): names = {f.name for f in model._meta.fields if isinstance(f, ForeignKey)} names.update(ForeignValue.get_names(model)) return names