Diff for "Registry/TeamParticipationUsage"

Not logged in - Log In / Register

Differences between revisions 1 and 2
Revision 1 as of 2009-07-31 14:11:57
Size: 10110
Editor: bac
Comment:
Revision 2 as of 2009-07-31 14:14:13
Size: 10142
Editor: bac
Comment:
Deletions are marked like this. Additions are marked like this.
Line 10: Line 10:
This Specification aims to describe correct TeamParticipation usage in accordance with the FOAF approach. This Specification aims to describe correct Team``Participation usage in accordance with the FOAF approach.
Line 16: Line 16:
In general, whenever you are checking if a person has a permission, or is the owner of an object, you need to make sure that your code is team-aware. This means using TeamParticipation! In general, whenever you are checking if a person has a permission, or is the owner of an object, you need to make sure that your code is team-aware. This means using Team``Participation!
Line 30: Line 30:
=== The TeamParticipation table === === The Team``Participation table ===
Line 32: Line 32:
That data structure is the TeamParticipation table, which stores the precise set of people that are members of a particular team. For every person who is a member of a team, or a member of a team which is in turn a member of that team, there will be a participation entry that expresses that persons membership in the team. So TeamParticipation expresses both direct AND indirect membership in a team. Note that TeamParticipation is entirely automatically managed. Only the TeamMembership table is administered, adding and removing members. The results of those operations are exploded out and stored in TeamParticipation. That data structure is the Team``Participation table, which stores the precise set of people that are members of a particular team. For every person who is a member of a team, or a member of a team which is in turn a member of that team, there will be a participation entry that expresses that persons membership in the team. So Team``Participation expresses both direct AND indirect membership in a team. Note that Team``Participation is entirely automatically managed. Only the TeamMembership table is administered, adding and removing members. The results of those operations are exploded out and stored in Team``Participation.
Line 34: Line 34:
TeamParticipation stores 3 important relationships. Consider the following structure of nested teams and persons: Team``Participation stores 3 important relationships. Consider the following structure of nested teams and persons:
Line 44: Line 44:
  * The compiled TeamMembership information about '''direct''' membership. For example, if Team 3 has a member 1 and Team 2 that has a member 4, it stores:   * The compiled Team``Membership information about '''direct''' membership. For example, if Team 3 has a member 1 and Team 2 that has a member 4, it stores:
Line 69: Line 69:
We assume that the following '''Actions''' are aware about the existence of TeamParticipation and maintain the table correctly: We assume that the following '''Actions''' are aware about the existence of Team``Participation and maintain the table correctly:
Line 71: Line 71:
  * !CreatePerson: when a Person is created, probably on PersonSet.createPerson(), we should add a entry in TeamParticipation referring this Person to itself. Each Person is a member of their own Team effectively, so there is a TeamParticipation entry for each person, with themselves as the member.   * !CreatePerson: when a Person is created, probably on PersonSet.createPerson(), we should add a entry in Team``Participation referring this Person to itself. Each Person is a member of their own Team effectively, so there is a TeamParticipation entry for each person, with themselves as the member.
Line 75: Line 75:
  * !ExpireTeamMembership: when the membership entry expires (whatever method we use to perform it) we should call expireMember() method, it'll set its status to EXPIRED and remove the correspondent TeamParticipation entries, IF that person is not also indirectly a team member. Note that a person might be a member of a team both directly and indirectly. Removing their direct membership should not remove their TeamParticipation entry. However, it should probably trigger a warning to the administrator who is doing the removal, because they will want to know that the person will retain membership despite being removed as a direct member.   * !ExpireTeamMembership: when the membership entry expires (whatever method we use to perform it) we should call expireMember() method, it'll set its status to EXPIRED and remove the correspondent Team``Participation entries, IF that person is not also indirectly a team member. Note that a person might be a member of a team both directly and indirectly. Removing their direct membership should not remove their Team``Participation entry. However, it should probably trigger a warning to the administrator who is doing the removal, because they will want to know that the person will retain membership despite being removed as a direct member.
Line 77: Line 77:
  * !DisableTeamMembership: when disabling a member either by rejecting an PROPOSED member or disable a CURRENT member, we should call the method disableMember() that also removes the correspondent entries in TeamParticipation. Note that this should behave in the same way as !ExpireTeamMembership in that it will respect the difference between direct and indirect team membership.   * !DisableTeamMembership: when disabling a member either by rejecting an PROPOSED member or disable a CURRENT member, we should call the method disableMember() that also removes the correspondent entries in Team``Participation. Note that this should behave in the same way as !ExpireTeamMembership in that it will respect the difference between direct and indirect team membership.
Line 122: Line 122:
  * The implementation for the `__contains__` method is simply a '''SELECT''' on the TeamParticipation table, unless a non-SQLCrowd has been added.   * The implementation for the `__contains__` method is simply a '''SELECT''' on the Team``Participation table, unless a non-SQLCrowd has been added.
Line 193: Line 193:
  * It should be possible to maintain the TeamParticipation table using database triggers - this is to be investigated by StuartBishop.   * It should be possible to maintain the Team``Participation table using database triggers - this is to be investigated by StuartBishop.

Team Participation Usage

Author: StuartBishop and CelsoProvidelo
Status: ImplementedSpecification, FoafSpecification
Created: 2005-02-09
Contributors: SteveAlexander

Introduction

This Specification aims to describe correct TeamParticipation usage in accordance with the FOAF approach.

Rationale

Everywhere we use a Person's reference we might be referring to a person or a team. We need to make sure that, when we query the database to see if a the user has a permission, we take into account the fact that the user might indirectly have that permission by virtue of his membership in a team.

In general, whenever you are checking if a person has a permission, or is the owner of an object, you need to make sure that your code is team-aware. This means using TeamParticipation!

Essential Team Participation

As we know the Person table stores both people and teams within the Launchpad System, and they differ only by the extra team-related fields such as teamdescription and teamowner which are filled when the entry refers to a team.

The TeamMembership table includes all the information related to each first level member of this team, including the fields

  • status
  • datecreated
  • dateexpires

These are the direct memberships of a team. There is one entry in TeamMembership for every person who has ever requested to join that team (as discussed in TeamMembership). But since teams can be members of teams, we need a separate data structure to map from a team to the complete set of people who are current members of a team.

The Team``Participation table

That data structure is the TeamParticipation table, which stores the precise set of people that are members of a particular team. For every person who is a member of a team, or a member of a team which is in turn a member of that team, there will be a participation entry that expresses that persons membership in the team. So TeamParticipation expresses both direct AND indirect membership in a team. Note that TeamParticipation is entirely automatically managed. Only the TeamMembership table is administered, adding and removing members. The results of those operations are exploded out and stored in TeamParticipation.

TeamParticipation stores 3 important relationships. Consider the following structure of nested teams and persons:

T2(P4, T3(P1))
Team 2 contains both Person 4 and Team 3 (which contains Person 1).

P4
Person 4 exists, and is not in any Team.
  • The compiled TeamMembership information about direct membership. For example, if Team 3 has a member 1 and Team 2 that has a member 4, it stores:

   | id | team | person | 
   |  1 |   2  |    4   | -> compiled team 4 membership
   |  2 |   3  |    1   | -> compiled team 3 membership 

  • exploded relationships among Teams, ex: If Team 3 is a Member of Team 2, it stores that Team 2 contains Person 1. This is because (Team 2 contains Team 3) and (Team 3 contains Person 1), so Team 2 transitively contains Person 1.

   | id | team | person | 
   |  3 |   2  |    1   | -> exploded team 3 member of team 4

  • The information relating a Person (not a Team) to itself, is:

   | id | team | person | 
   |  4 |   1  |    1   | -> refers to person 1
   |  5 |   4  |    4   | -> refers to person 4

This behaviour allows us to optimize permission checks to use a single quick database query.

Assumptions

We assume that the following Actions are aware about the existence of TeamParticipation and maintain the table correctly:

  • CreatePerson: when a Person is created, probably on PersonSet.createPerson(), we should add a entry in TeamParticipation referring this Person to itself. Each Person is a member of their own Team effectively, so there is a TeamParticipation entry for each person, with themselves as the member.

  • ApproveTeamMembership: when Approving a Membership entry (status PROPOSED to CURRENT) by have an available method approveMember(), that creates the correspondent entries in TeamParticipation. Note that memberships with status ADMIN also imply membership, so there is no change to teamparticipation in moving the status between CURRENT and ADMIN.

  • ExpireTeamMembership: when the membership entry expires (whatever method we use to perform it) we should call expireMember() method, it'll set its status to EXPIRED and remove the correspondent TeamParticipation entries, IF that person is not also indirectly a team member. Note that a person might be a member of a team both directly and indirectly. Removing their direct membership should not remove their TeamParticipation entry. However, it should probably trigger a warning to the administrator who is doing the removal, because they will want to know that the person will retain membership despite being removed as a direct member.

  • DisableTeamMembership: when disabling a member either by rejecting an PROPOSED member or disable a CURRENT member, we should call the method disableMember() that also removes the correspondent entries in TeamParticipation. Note that this should behave in the same way as ExpireTeamMembership in that it will respect the difference between direct and indirect team membership.

An immediate member of a team is one that is directly in that team. So, if Team B is a member of Team A, and Person C is a member of Team B, then Person C is in Team A, but is not an immediate member of team A.

There are no use cases in the application code for knowing if a person is an immediate member of a particular team.

Note that when checking if a person is an admin of a given team, we are concerned only whether they (or the team they are in) are the immediate admin of that given team. It does not matter whether they are the admin of another team, where that other team is a member of the given team.

Use Cases

The most probable use case is Ownership handling, like:

  • Owner of Distribution
  • Owner of Product
  • Maintainership in general
  • Security Officer
  • Almost every other foreign key relationship to the Person table

In all of these cases the security machinery will need to be able to check if the currently authenticated user is a member of the given role (as per TeamsAndRoles).

Implementation

Suggested Security Implementation:

  • Define ICrowd

Toggle line numbers
   1     #!python
   2     class ICrowd(Interface):
   3 
   4         def __contains__(person_or_team):
   5             """Return True if the given person_or_team is in the crowd."""
   6 
   7         def __add__(crowd):
   8             """Return a new ICrowd that is this crowd added to the given crowd.
   9 
  10             The returned crowd contains the person or teams in
  11             both this crowd and the given crowd.
  12             """
  • Create an adapter from IPerson -> ICrowd. Note that this adapter also works for ITeam, which inherits from IPerson.

  • The implementation for the __contains__ method is simply a SELECT on the TeamParticipation table, unless a non-SQLCrowd has been added.

  • Implement non-SQLCrowd classes for absolutely everybody (celebs.absolutelyanyone), for any Person known to Launchpad (celebs.people), and nobody at all (celebs.nobody). See LaunchpadCelebrities.

  • Passing celebs.anonymous crowd into contains means you want to know whether the "unknown Person" is in the given crowd. The "unknown Person" is the person that represents the unauthenticated user.

  • A probable implementation is given below. This needs to be confirmed by doctests, which should live in lib/canonical/launchpad/database/person.py

Toggle line numbers
   1 class SQLCrowd:
   2 
   3     implements(ICrowd)
   4 
   5     def __init__(self, person_or_team):
   6         if person is not None:
   7             person = IPerson(person_or_team)
   8             self.person_ids = sets.Set([person.id])
   9         self.crowds = []  # list because crowds may be not hashable.
  10 
  11     def __contains__(self, person_or_team):
  12         # Look in the non-SQLCrowds before hitting the database.
  13         for crowd in self.crowds:
  14             if person_or_team in crowd:
  15                 return True
  16         # If we have None passed in, the return False at this stage.
  17         if not IPerson.providedBy(person_or_team):
  18             raise TypeError, person_or_team
  19         string_ids = [str(person_id) for person_id in self.person_ids]
  20         sql = 'SELECT COUNT(*) FROM TeamParticipation WHERE person=%d and team in (%s)' % (
  21             person_or_team.id, ', '.join(string_ids)
  22             )
  23         rv = execute(sql)
  24         return rv[0][0] >= 1
  25 
  26     def _copy(self):
  27         new_crowd = SQLCrowd(None)
  28         new_crowd.persons = sets.Set(self.persons)
  29         new_crowd.crowds = list(self.crowds)
  30         return new_crowd
  31 
  32     def __add__(self, crowd):
  33         new_crowd = self._copy()
  34         if isinstance(crowd, SQLCrowd):
  35             new_crowd.persons |= crowd.persons
  36             new_crowd.crowds += crowd.crowds
  37         else:
  38             new_crowd.crowds.append(crowd)
  39         return new_crowd

  • We may want an inefficient ICrowd that just loops through the teams and persons for use in non-SQL based tests. In its __contains__ method, it would compare persons by == and look for team membership using ITeam.isMember().

Usage example

Toggle line numbers
   1     class EditByOwnerOrAssignee(AuthorizationBase):
   2         permission = 'launchpad.Edit'
   3         usedfor = IBugTask
   4 
   5         def getAllowedCrowd(self):
   6             # Note that this is using the new API from CrowdControl.
   7             return ICrowd(self.obj.owner) + ICrowd(self.obj.assignee)

Unresolved Issues

  • It should be possible to maintain the TeamParticipation table using database triggers - this is to be investigated by StuartBishop.

  • The ICrowd interface was implemented but has not been used.

Registry/TeamParticipationUsage (last edited 2009-07-31 14:14:13 by bac)