Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Authorization in social networking website

I need to accomplish the following related to privileges:

I have 3 users:

- User A
- User B
- User C

Each of the users has the following documents with associated access settings:

- 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 has User B as a contact but is not a contact of User C (User B and User C are contacts).

Thus, User A would be able to view the following:

- 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)

I am interested to learn how these privileges would be handled. I am also seeking any documentation or articles that would help me hit the ground running.

like image 616
David542 Avatar asked Jun 13 '11 09:06

David542


2 Answers

Unfortunately Django's authorization system does not allow you to assign permissions per object, only per class. Here I assume that each of your "Document" is an instance of a model class.

There are, however, reusable apps that greatly simplify this task. Have a look at django-guardian or other packages that work on object (or row) level.

like image 174
FelipeFG Avatar answered Oct 20 '22 16:10

FelipeFG


A general answer is to find the distance between the document owner and a given contact. In Computer Science terms, this is a directed graph.

There's a good article with some SQL queries that covers this topic at http://techportal.inviqa.com/2009/09/07/graphs-in-the-database-sql-meets-social-networks/. Rather than trying to summarize the entire article, here's how to conceptualize the problem:

  • Start with a blank piece of paper.
  • Draw a dot somewhere on the page for each person (in this case, Users A, B, and C). In CS terms, this is a "node".
  • Draw an arrow from a user to all of their contacts. In CS terms, this is a "directed edge", or an "arc".
    • This isn't explicit in the question, but it looks like User C must be a contact of User B, or a contact another of User A's other contacts (since User A can read C2 and C4).
    • So in this case, you would draw from User A -> User B, and User B -> User C.

As an aside, if being a "contact" is mutual, you can draw a line segment (or bidirectional arrow) instead of an arrow. In CS terms, this would be an "undirected" vs. a "directed" graph. Facebook relationships are an undirected relationship; if someone is my friend, then I am also their friend. By contrast, if someone is in my Outlook address book, I'm not necessarily in theirs. So this is a directed relationship.

As more users are added to the drawing, you'll notice that a user's contacts are one step away, and their contacts-of-contacts are two steps away. But you can only travel in the direction of the arrow.

So the problem for contacts is, "How do I find all nodes whose graph distance is one?" And the question for contacts-of-contacts is, "How do I find all nodes whose graph distance is two?". Although "two or less" is probably more appropriate, since you'd expect direct contacts to have access to all of the "contacts-of-contacts" content.

For the general case, there are some SQL queries described in the article that might provide some insight. But for your specific need, I'd consider just using some joins.

Let's consider a Users table, with primary key id along with its other fields, and a HasContact table which has only two columns: userId and contactId. We'll assume that User A has id 1, User B is 2, and User C is 3. HasContact has rows (1, 2) and (2, 3) to represent the relationships described above.

A pretty simple set of SQL joins can produce a list of all friends, or all friends-of-friends.

The following query would return all IDs of a User's contacts:

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

If you know the user IDs, an authorization query could be quite simple:

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

If the query returns 0, then we know that the current user is not one of the document owner's contacts.

We can update that second query to indicate whether a user is a contact-of-a-contact:

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

This should return nonzero, as long as there are entries in the Relationships table such that ($document_owner_user_id, X) and (X, $id_of_current_user) both exist. Otherwise, it will return zero.

I know this is a long and somewhat indirect answer, so please comment if you have any questions.

like image 34
GargantuChet Avatar answered Oct 20 '22 14:10

GargantuChet