Авторизация на сайте социальных сетей

Мне нужно выполнить следующие действия, связанные с привилегиями:

У меня 3 пользователя:

- User A
- User B
- User C

Каждый из пользователей имеет следующие документы со связанными параметрами доступа:

- User A
    - Document A1, only allow contacts to view
    - Document A2, allow everyone to view
    - Document A3, allow no one to view except myself
    - Document A4, allow contacts, and contacts of contacts to view
- User B
    - Documents B1, B2, B3, B4 with similar privileges
- User C
    - Documents C1, C2, C3, C4 with similar privileges

User A имеет User B как контакт, но не является контактом User C (User B и User C являются контактами).

Таким образом, User A сможет просмотреть следующее:

- Document B1 (contacts can view)
- Document B2 (everyone can view) 
- Document B4 (contacts of contacts)
- Document C2 (everyone can view)
- Document C4 (contacts of contacts)

Может кто-нибудь объяснить, как будут обрабатываться эти привилегии. И если вы могли бы связать меня с любой документацией или статьями, которые помогут мне попасть в землю. Спасибо.

Ответ 1

Общий ответ - найти расстояние между владельцем документа и данным контактом. В терминах компьютерных наук это ориентированный график.

Там есть хорошая статья с некоторыми SQL-запросами, которая охватывает этот раздел http://techportal.inviqa.com/2009/09/07/graphs-in-the-database-sql-meets-social-networks/. Вместо того, чтобы пытаться обобщить всю статью, вот как концептуализировать проблему:

  • Начните с чистого листа.
  • Нарисуйте точку где-нибудь на странице для каждого человека (в данном случае - пользователей A, B и C). В терминах CS это "node".
  • Нарисуйте стрелку от пользователя ко всем своим контактам. В терминах CS это "направленный край" или "дуга".
    • Это не является явным в вопросе, но похоже, что пользователь C должен быть контактом пользователя B или связаться с другим пользователем. Другие контакты (поскольку пользователь A может читать C2 и C4).
    • Итак, в этом случае вы будете рисовать из User A → User B и User B → User C.

В стороне, если "контакт" взаимен, вы можете нарисовать сегмент линии (или двунаправленную стрелку) вместо стрелки. В терминах CS это будет "неориентированный" против "направленного" графа. Отношения Facebook - это ненаправленная связь; если кто-то мой друг, то я тоже их друг. В отличие от этого, если кто-то находится в моей адресной книге Outlook, я не обязательно в них. Итак, это направленная связь.

По мере добавления большего количества пользователей на чертеж вы заметите, что контакты пользователя находятся в одном шаге, а их контакты-контакты находятся в двух шагах. Но вы можете двигаться только в направлении стрелки.

Таким образом, проблема для контактов заключается в следующем: "Как найти все узлы, расстояние между графиками которых равно единице?" И вопрос о контактах-контактах: "Как найти все узлы, расстояние между графиками которых равно двум?". Хотя "два или менее", вероятно, более уместны, поскольку вы ожидаете, что прямые контакты будут иметь доступ ко всем контентам "контакты-контакты".

В общем случае в статье описаны некоторые SQL-запросы, которые могут дать некоторое представление. Но для вашей конкретной потребности я бы подумал, просто используя некоторые соединения.

Рассмотрим таблицу Users с первичным ключом id вместе со своими другими полями и таблицей HasContact, которая имеет только два столбца: userId и contactId. Предположим, что пользователь A имеет идентификатор 1, пользователь B - 2, а пользователь C - 3. HasContact имеет строки (1, 2) и (2, 3) для представления отношений, описанных выше.

Довольно простой набор соединений SQL может содержать список всех друзей или всех друзей друзей.

Следующий запрос возвращает все идентификаторы контактов пользователя:

SELECT contact.id
  FROM Users "user"
    LEFT JOIN Relationships "rel"
      ON user.id = rel.userid
    LEFT JOIN Users "contact"
      ON rel.contactId = contact.id
  WHERE user.id = $id_of_current_user

Если вы знаете идентификаторы пользователей, запрос авторизации может быть довольно простым:

SELECT count(*)
  FROM Relationships "rel"
  WHERE rel.userid = $document_owner_user_id
    AND rel.contactid = $id_of_current_user

Если запрос возвращает 0, мы знаем, что текущий пользователь не является одним из контактов владельца документа.

Мы можем обновить этот второй запрос, чтобы указать, является ли пользователь контактным контактом:

SELECT count(*)
  FROM Relationships "rel_1"
    INNER JOIN Relationships "rel_2"
      ON rel_1.contactId = rel_2.userId
  WHERE rel_1.userid = $document_owner_user_id
    AND rel_2.contactid = $id_of_current_user

Это должно возвращать ненулевое значение, если в таблице отношений есть записи, что существуют ($document_owner_user_id, X) и (X, $id_of_current_user). В противном случае он вернет нуль.

Я знаю, что это длинный и несколько косвенный ответ, поэтому, пожалуйста, прокомментируйте, если у вас есть какие-либо вопросы.

Ответ 2

К сожалению, система авторизации Django не позволяет вам назначать разрешения для каждого объекта, только для каждого класса. Здесь я предполагаю, что каждый из ваших "Документов" является экземпляром класса модели.

Существуют, однако, многоразовые приложения, которые значительно упрощают эту задачу. Посмотрите django-guardian или другие пакеты, которые работают на уровне объекта (или строки).

Ответ 3

В основном вам нужно Ограничить доступ к зарегистрированным пользователям, которые проходят тест. Но часть с "контактами контактов" может привести к очень сложным sql-запросам. И я предлагаю вам пересмотреть это требование. (У меня есть много хороших друзей, которым я люблю и доверяю, но у них есть все виды странных людей, как друзья...)

Ответ 4

Что вам нужно, это список контроля доступа (ACL), который изменится в соответствии с каждой сетью пользователей. ACL, а также объектные разрешения недоступны в стандартном модуле django.contrib.auth. Я фактически реализовал ACL в Django, но на основе класса не основан на объектах. В контексте вашего приложения он будет работать, как User A может просматривать любые документы, если он находится в определенной уполномоченной группе (например, Admins group). Но его можно заставить работать, например, User A может просматривать документы User B Если User A находится в группе Contacts для User B.

Я могу объяснить, как это делается с точки зрения структуры модели для настроенного приложения auth. Модели User и Permission будут такими же, как и стандартное приложение Django auth (вы можете скопировать его из models.py). Затем вам нужна модель для представления разных уровней разрешений (Everyone, ContactsOfContact, Contact, Myself). Выполнение этого просто, как добавление другого поля в стандартную модель django auth groups. Это поле будет внешним ключом к той же модели, которая будет представлять группу, на которую унаследует каждая группа. Теперь модель выглядит так:

class SocialGroup(models.Model):
  name = models.CharField(unique=True, max_length=150)
  parent = models.ForeignKey('self')

Теперь вы можете добавлять такие отношения, как Contacts, группа может просматривать все, что может просматривать группа Everyone, установив группу Everyone в качестве родительской группы Contacts. Обратите внимание, что нет отношения внешнего ключа с моделью Permission. Далее нам нужен способ указать отношения между пользователями и группами.

class Relationship(models.Model):
  user  = models.ForeignKey(User)
  related_user = models.ForeignKey(User)
  related_by = models.ForeignKey(SocialGroup)

С помощью этой модели мы можем сказать, что User A (related_user) связан с User B (пользователем), находясь в группе User B Contacts (related_by). Затем нам нужен способ указать разрешение для каждого документа.

class DocumentPermissions(models.Model):
  document = models.ForeignKey(Document)
  group = models.ForeignKey(SocialGroup)
  Permission = models.ForeignKey(Permission)

Теперь мы можем сказать, что Contacts у группы есть can_view разрешение для Document B1. Обратите внимание, что эта модель должна идти туда, где находится модель документа, а не auth models.py в нашем новом приложении auth. Это все, теперь все, что нам нужно сделать, это написать логику, чтобы найти разрешения для данного пользователя для данного документа. Итак, что нам нужно делать, когда User B пытается просмотреть Document A1,

  • Сначала проверьте, кому принадлежит документ (User A)
  • Затем найдите группу отношений между владельцем и попыткой доступа к документу из модели отношений (Контакты).
  • Затем проверьте DocumentPermissions, чтобы увидеть, имеет ли эта группа или какая-либо группа, наследующая ее, разрешения для просмотра документа.

Эта логика может быть реализована в новом интерфейсе аутентификации (который будет использовать наши новые модели auth) для Django, который может войти в новое приложение auth. Вы можете проверить функцию has_perm в стандартном бэкэнде модели Django. Это все, что вам нужно. Все декораторы и все еще будут работать.

Ответ 5

Вы можете включить поле друзей друзей. Это был бы очень большой стол (скажем, 200 * 200 * N = 40 000 * N... или действительно огромный, если у вас нет ограничений для друзей, а кто-то дружит с миллионом человек - 1 миллион человек с 1 миллионом FoF), но это было бы проще, чем ударить базу данных 200 раз за просмотр.