Skip to content
This repository

Added the teams extension (https://dev.launchpad.net/OpenIDTeams) #57

Closed
wants to merge 2 commits into from

1 participant

Patrick Uiterwijk
Patrick Uiterwijk

I have created openid.extensions.teams to implement the OpenID teams extension specified at https://dev.launchpad.net/OpenIDTeams

Patrick Uiterwijk

Since nobody has responded to this request since I opened it, I have moved this into a separate module (python-openid-teams).

Patrick Uiterwijk puiterwijk closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
2  openid/extensions/__init__.py
... ...
@@ -1,5 +1,5 @@
1 1
 """OpenID Extension modules."""
2 2
 
3  
-__all__ = ['ax', 'pape', 'sreg']
  3
+__all__ = ['ax', 'pape', 'sreg', 'teams']
4 4
 
5 5
 from openid.extensions.draft import pape5 as pape
286  openid/extensions/teams.py
... ...
@@ -0,0 +1,286 @@
  1
+"""Teams extension request and response parsing and object representation
  2
+
  3
+This module contains objects representing team extension requests 
  4
+and responses that can be used with both OpenID relying parties and
  5
+OpenID providers.
  6
+
  7
+    1. The relying party creates a request object and adds it to the
  8
+       C{L{AuthRequest<openid.consumer.consumer.AuthRequest>}} object
  9
+       before making the C{checkid_} request to the OpenID provider::
  10
+
  11
+        auth_request.addExtension(TeamsRequest(requested=['team']))
  12
+
  13
+    2. The OpenID provider extracts the teams extension request from
  14
+       the OpenID request using C{L{TeamsRequest.fromOpenIDRequest}},
  15
+       gets the user's approval and team membership, creates a C{L{TeamsResponse}}
  16
+       object and adds it to the C{id_res} response::
  17
+
  18
+        teams_req = TeamsRequest.fromOpenIDRequest(checkid_request)
  19
+        # [ get the user's approval and team membership, informing the user that
  20
+        #   the groups in teams_response were requested and accepted ]
  21
+        teams_resp = TeamsResponse.extractResponse(teams_req, group_memberships)
  22
+        teams_resp.toMessage(openid_response.fields)
  23
+
  24
+    3. The relying party uses C{L{TeamsResponse.fromSuccessResponse}} to
  25
+       exxtract the data from the OpenID response::
  26
+
  27
+        teams_resp = TeamsResponse.fromSuccessResponse(success_response)
  28
+
  29
+@var teams_uri: The URI used for the teams extension namespace and XRD Type Value
  30
+"""
  31
+
  32
+from openid.message import registerNamespaceAlias, \
  33
+     NamespaceAliasRegistrationError
  34
+from openid.extension import Extension
  35
+import logging
  36
+
  37
+try:
  38
+    basestring #pylint:disable-msg=W0104
  39
+except NameError:
  40
+    # For Python 2.2
  41
+    basestring = (str, unicode) #pylint:disable-msg=W0622
  42
+
  43
+__all__ = [
  44
+    'TeamsRequest',
  45
+    'TeamsResponse',
  46
+    'teams_uri',
  47
+    'supportsTeams',
  48
+    ]
  49
+
  50
+# The namespace for this extension
  51
+teams_uri = 'http://ns.launchpad.net/2007/openid-teams'
  52
+
  53
+try:
  54
+    registerNamespaceAlias(teams_uri, 'lp')
  55
+except NamespaceAliasRegistrationError, e:
  56
+    logging.exception('registerNamespaceAlias(%r, %r) failed: %s' % (teams_uri,
  57
+                                                               'teams', str(e),))
  58
+
  59
+def supportsTeams(endpoint):
  60
+    """Does the given endpoint advertise support for team extension?
  61
+
  62
+    @param endpoint: The endpoint object as returned by OpenID discovery
  63
+    @type endpoint: openid.consumer.discover.OpenIDEndpoint
  64
+
  65
+    @returns: Whether an teams extension type was advertised by the endpoint
  66
+    @rtype: bool
  67
+    """
  68
+    return endpoint.usesExtension(teams_uri)
  69
+
  70
+class TeamsRequest(Extension):
  71
+    """An object to hold the state of a teams extension request.
  72
+
  73
+    @ivar requested: A list of team names in this teams extension request
  74
+    @type required: [str]
  75
+
  76
+    @group Consumer: requestField, requestFields, getExtensionArgs, addToOpenIDRequest
  77
+    @group Server: fromOpenIDRequest, parseExtensionArgs
  78
+    """
  79
+    ns_uri = 'http://ns.launchpad.net/2007/openid-teams'
  80
+    ns_alias = 'lp'
  81
+
  82
+    def __init__(self, requested=None):
  83
+        """Initialize an empty teams extension request"""
  84
+        Extension.__init__(self)
  85
+        self.requested = []
  86
+
  87
+        if requested:
  88
+            self.requestTeams(requested)
  89
+
  90
+    def requestedTeams(self):
  91
+        return self.requested
  92
+
  93
+    def fromOpenIDRequest(cls, request):
  94
+        """Create a simple teams extension request that contains the
  95
+        team names that were requested in the OpenID request with the
  96
+        given arguments
  97
+
  98
+        @param request: The OpenID request
  99
+        @type request: openid.server.CheckIDRequest
  100
+
  101
+        @returns: The newly created teams extension request
  102
+        @rtype: C{L{TeamsRequest}}
  103
+        """
  104
+        self = cls()
  105
+
  106
+        # Since we're going to mess with namespace URI mapping, don't
  107
+        # mutate the object that was passed in.
  108
+        message = request.message.copy()
  109
+
  110
+        args = message.getArgs(self.ns_uri)
  111
+        self.parseExtensionArgs(args)
  112
+
  113
+        return self
  114
+
  115
+    fromOpenIDRequest = classmethod(fromOpenIDRequest)
  116
+
  117
+    def parseExtensionArgs(self, args):
  118
+        """Parse the unqualified teams extension request
  119
+        parameters and add them to this object.
  120
+
  121
+        This method is essentially the inverse of
  122
+        C{L{getExtensionArgs}}. This method restores the serialized teams
  123
+        extension team names.
  124
+
  125
+        If you are extracting arguments from a standard OpenID
  126
+        checkid_* request, you probably want to use C{L{fromOpenIDRequest}},
  127
+        which will extract the teams extension namespace and arguments from the
  128
+        OpenID request. This method is intended for cases where the
  129
+        OpenID server needs more control over how the arguments are
  130
+        parsed than that method provides.
  131
+
  132
+        >>> args = message.getArgs(teams_uri)
  133
+        >>> request.parseExtensionArgs(args)
  134
+
  135
+        @param args: The unqualified teams extension arguments
  136
+        @type args: {str:str}
  137
+
  138
+        @returns: None; updates this object
  139
+        """
  140
+        items = args.get('query_membership')
  141
+        if items:
  142
+            for team_name in items.split(','):
  143
+                self.requestTeam(team_name)
  144
+
  145
+    def wereTeamsRequested(self):
  146
+        """Have any teams been requested?
  147
+
  148
+        @rtype: bool
  149
+        """
  150
+        return bool(self.requested)
  151
+
  152
+    def __requests__(self, team_name):
  153
+        """Was this team in the request team names?"""
  154
+        return team_name in self.requested
  155
+
  156
+    def requestTeam(self, team_name):
  157
+        """Request the specified team membership from the OpenID user
  158
+
  159
+        @param team_name: the unqualified team name
  160
+        @type team_name: str
  161
+        """
  162
+        if not team_name in self.requested:
  163
+            self.requested.append(team_name)
  164
+
  165
+    def requestTeams(self, team_names):
  166
+        """Add the given list of team names to the request.
  167
+
  168
+        @param team_names: The team names to request
  169
+        @type team_names: [str]
  170
+        """
  171
+        if isinstance(team_names, basestring):
  172
+            raise TypeError('Teams should be passed as a list of '
  173
+                            'strings (not %r)' % (type(field_names),))
  174
+
  175
+        for team_name in team_names:
  176
+            self.requestTeam(team_name)
  177
+
  178
+    def getExtensionArgs(self):
  179
+        """Get a dictionary of unqualified team extension
  180
+        arguments representing this request.
  181
+
  182
+        This method is essentially the inverse of
  183
+        C{L{parseExtensionArgs}}. This method serializes the team
  184
+        extension request fields.
  185
+
  186
+        @rtype: {str:str}
  187
+        """
  188
+        args = {}
  189
+
  190
+        if self.requested:
  191
+            args['query_membership'] = ','.join(self.requested)
  192
+
  193
+        return args
  194
+
  195
+class TeamsResponse(Extension):
  196
+    """Represents the data returned in a simple registration response
  197
+    inside of an OpenID C{id_res} response. This object will be
  198
+    created by the OpenID server, added to the C{id_res} response
  199
+    object, and then extracted from the C{id_res} message by the
  200
+    Consumer.
  201
+
  202
+    @ivar data: The simple registration data, keyed by the unqualified
  203
+        simple registration name of the field (i.e. nickname is keyed
  204
+        by C{'nickname'})
  205
+
  206
+    @ivar ns_uri: The URI under which the simple registration data was
  207
+        stored in the response message.
  208
+
  209
+    @group Server: extractResponse
  210
+    @group Consumer: fromSuccessResponse
  211
+    @group Read-only dictionary interface: keys, iterkeys, items, iteritems,
  212
+        __iter__, get, __getitem__, keys, has_key
  213
+    """
  214
+
  215
+    ns_uri = 'http://ns.launchpad.net/2007/openid-teams'
  216
+    ns_alias = 'lp'
  217
+
  218
+    def __init__(self, teams=None):
  219
+        Extension.__init__(self)
  220
+        if teams is None:
  221
+            self.teams = []
  222
+        else:
  223
+            self.teams = teams
  224
+
  225
+    def extractResponse(cls, request, teams):
  226
+        """Take a C{L{TeamsRequest}} and a list of groups
  227
+        the user is member of and create a C{L{TeamsResponse}}
  228
+        object containing the list of team names that are both
  229
+        requested and in the membership list of the user.
  230
+
  231
+        @param request: The teams extension request object
  232
+        @type request: TeamsRequest
  233
+
  234
+        @param teams: The list of teams the user is a member of
  235
+        @type teams: [str]
  236
+
  237
+        @returns: a teams extension response object
  238
+        @rtype: TeamsResponse
  239
+        """
  240
+        self = cls()
  241
+        for team in request.requestedTeams():
  242
+            if team in teams:
  243
+                self.teams.append(team)
  244
+        return self
  245
+
  246
+    extractResponse = classmethod(extractResponse)
  247
+
  248
+    def fromSuccessResponse(cls, success_response, signed_only=True):
  249
+        """Create a C{L{TeamsResponse}} object from a successful OpenID
  250
+        library response
  251
+        (C{L{openid.consumer.consumer.SuccessResponse}}) response
  252
+        message
  253
+
  254
+        @param success_response: A SuccessResponse from consumer.complete()
  255
+        @type success_response: C{L{openid.consumer.consumer.SuccessResponse}}
  256
+
  257
+        @param signed_only: Whether to process only data that was
  258
+            signed in the id_res message from the server.
  259
+        @type signed_only: bool
  260
+
  261
+        @rtype: TeamsResponse
  262
+        @returns: A teams extension response with the teams the OpenID
  263
+            provider provided.
  264
+        """
  265
+        self = cls()
  266
+        if signed_only:
  267
+            args = success_response.getSignedNS(self.ns_uri)
  268
+        else:
  269
+            args = success_response.message.getArgs(self.ns_uri)
  270
+
  271
+        if not args:
  272
+            return None
  273
+
  274
+        self.teams = args['is_member'].split(',')
  275
+
  276
+        return self
  277
+
  278
+    fromSuccessResponse = classmethod(fromSuccessResponse)
  279
+
  280
+    def getExtensionArgs(self):
  281
+        """Get the fields to put in the teams extension namespace
  282
+        when adding them to an id_res message.
  283
+
  284
+        @see: openid.extension
  285
+        """
  286
+        return {'is_member': ','.join(self.teams)}
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.