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.
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.
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:
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With