[Solved]-Authorization in social networking website

5đź‘Ť

âś…

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.

👤GargantuChet

7đź‘Ť

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.

👤FelipeFG

3đź‘Ť

What you basically need is to Limit access to logged-in users that pass a test. But the part with “contacts of contacts” can lead to very complicated sql-queries. And I suggest you to rethink that requirement. (I have lots of good friends whom I like and trust. But they have all kinds of weird people as friends …)

👤jammon

3đź‘Ť

What you need is an access control list(ACL) which will change according to each users network. ACL as well as object based permissions is not available in standard django.contrib.auth module. I’ve actually implemented an ACL in Django, But it’s class based not object based. In the context of your application it’d work like User A can view anyones documents as long as he is in a certain authorized group(say Admins group). But it can be made to work like User A can view User Bs documents If User A is in the Contacts group for User B.

I can explain how do it in terms of model structure for a customized auth app. The User and Permission model will be the same as standard Django auth app(You can copy it from models.py). Then you need a model to represent different levels of permissions(Everyone, ContactsOfContact, Contact, Myself). Doing this is simple as adding another field to the standard django auth groups model. This field will be a foreign key to the same model, Which will represent the group each group will inherit from. Now the model looks like this,

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

Now you can add relationships such as Contacts group can view everything that Everyone group can view by setting Everyone group as the parent to Contacts group. Note that there’s no foreign key relationship with Permission model. Next we need a way to specify the relationships between users and groups.

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

With this model we can say User A(related_user) is related to User B(user) by being in User Bs Contacts(related_by) group. Then we need a way to specify permission for each document.

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

Now we can say Contacts group has can_view permission for Document B1. Note that this model should go to wherever the Document model is located NOT to auth models.py in our new auth app. That’s it, Now all we have to do is to write the logic to find permissions for a given user for a given document. So here’s what we need to do when a User B tries to view Document A1,

  • First check which person the document belongs to(User A)
  • Then find the relationship group between owner and the one trying access the document from Relationship model(Contacts).
  • Then check DocumentPermissions to see whether that group or any group that it inherits from has permissions to view the document.

This logic can be implemented in a new authentication backend(which will use our new auth models) for Django which can go into the new auth app. You can check has_perm function in standard Django model backend. That’s all you need to do. All the decorators and stuff will still work.

👤vim

0đź‘Ť

You could include a “friends of friends” field. It would be a very big table (say, 200*200*N = 40,000*N … or really huge if you have no limit to friends, and someone is friends with a million people – that’s 1 million people with 1 million FoF) but it would be easier than hitting the database 200 times per view.

👤wisty

Leave a comment