comparison sat_pubsub/privilege.py @ 285:a87c155d0fd5

replaced former roster dirty hack by a XEP-0356 first draft implementation, only roster get is implemented so far
author Goffi <goffi@goffi.org>
date Tue, 31 Mar 2015 17:31:56 +0200
parents sat_pubsub/remote_roster.py@002c59dbc23f
children 2f87fa282dfd
comparison
equal deleted inserted replaced
284:dfc47748d8d8 285:a87c155d0fd5
1 #!/usr/bin/python
2 #-*- coding: utf-8 -*-
3 #
4 """
5 Copyright (c) 2003-2011 Ralph Meijer
6 Copyright (c) 2012, 2013, 2014, 2015 Jérôme Poisson
7
8
9 This program is free software: you can redistribute it and/or modify
10 it under the terms of the GNU Affero General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU Affero General Public License for more details.
18
19 You should have received a copy of the GNU Affero General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
21 --
22
23 This program is based on Idavoll (http://idavoll.ik.nu/),
24 originaly written by Ralph Meijer (http://ralphm.net/blog/)
25 It is sublicensed under AGPL v3 (or any later version) as allowed by the original
26 license.
27
28 --
29
30 Here is a copy of the original license:
31
32 Copyright (c) 2003-2011 Ralph Meijer
33
34 Permission is hereby granted, free of charge, to any person obtaining
35 a copy of this software and associated documentation files (the
36 "Software"), to deal in the Software without restriction, including
37 without limitation the rights to use, copy, modify, merge, publish,
38 distribute, sublicense, and/or sell copies of the Software, and to
39 permit persons to whom the Software is furnished to do so, subject to
40 the following conditions:
41
42 The above copyright notice and this permission notice shall be
43 included in all copies or substantial portions of the Software.
44
45 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
46 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
47 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
48 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
49 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
50 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
51 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
52
53 """
54
55 """
56 Remote roster client.
57
58 This module access roster throught a hacked version of
59 remote roster management http://jkaluza.fedorapeople.org/remote-roster.html
60 """
61
62 from wokkel import xmppim
63 from wokkel.compat import IQ
64 from wokkel.subprotocols import XMPPHandler
65 from twisted.python import log
66 from twisted.python import failure
67
68 PRIV_ENT_NS = 'urn:xmpp:privilege:1'
69 PRIV_ENT_ADV_XPATH = '/message/privilege[@xmlns="{}"]'.format(PRIV_ENT_NS)
70 ROSTER_NS = 'jabber:iq:roster'
71 PERM_ROSTER = 'roster'
72 PERM_MESSAGE = 'message'
73 PERM_PRESENCE = 'presence'
74 ALLOWED_ROSTER = ('none', 'get', 'set', 'both')
75 ALLOWED_MESSAGE = ('none', 'outgoing')
76 ALLOWED_PRESENCE = ('none', 'managed_entity', 'roster')
77 TO_CHECK = {PERM_ROSTER:ALLOWED_ROSTER, PERM_MESSAGE:ALLOWED_MESSAGE, PERM_PRESENCE:ALLOWED_PRESENCE}
78
79
80 class InvalidStanza(Exception):
81 pass
82
83 class NotAllowedError(Exception):
84 pass
85
86 class PrivilegesHandler(XMPPHandler):
87 #FIXME: need to manage updates, and database sync
88 #TODO: cache
89
90 def __init__(self):
91 super(PrivilegesHandler, self).__init__()
92 self._permissions = {PERM_ROSTER: 'none',
93 PERM_MESSAGE: 'none',
94 PERM_PRESENCE: 'none'}
95
96 @property
97 def permissions(self):
98 return self._permissions
99
100 def connectionInitialized(self):
101 self.xmlstream.addObserver(PRIV_ENT_ADV_XPATH, self.onAdvertise)
102
103 def onAdvertise(self, message):
104 """Managage the <message/> advertising privileges
105
106 self._permissions will be updated according to advertised privileged
107 """
108 privilege_elt = message.elements(PRIV_ENT_NS, 'privilege').next()
109 for perm_elt in privilege_elt.elements(PRIV_ENT_NS):
110 try:
111 if perm_elt.name != 'perm':
112 raise InvalidStanza(u'unexpected element {}'.format(perm_elt.name))
113 perm_access = perm_elt['access']
114 perm_type = perm_elt['type']
115 try:
116 if perm_type not in TO_CHECK[perm_access]:
117 raise InvalidStanza(u'bad type [{}] for permission {}'.format(perm_type, perm_access))
118 except KeyError:
119 raise InvalidStanza(u'bad permission [{}]'.format(perm_access))
120 except InvalidStanza as e:
121 log.msg("Invalid stanza received ({}), setting permission to none".format(e))
122 for perm in self._permissions:
123 self._permissions[perm] = 'none'
124 break
125
126 self._permissions[perm_access] = perm_type or 'none'
127
128 log.msg('Privileges updated: roster={roster}, message={message}, presence={presence}'.format(**self._permissions))
129
130
131 def getRoster(self, to_jid):
132 """
133 Retrieve contact list.
134
135 @return: Roster as a mapping from L{JID} to L{RosterItem}.
136 @rtype: L{twisted.internet.defer.Deferred}
137 """
138 if self._permissions[PERM_ROSTER] not in ('get', 'both'):
139 log.msg("WARNING: permission not allowed to get roster")
140 raise failure.Failure(NotAllowedError('roster get is not allowed'))
141
142 def processRoster(result):
143 roster = {}
144 for element in result.elements(ROSTER_NS, 'item'):
145 item = xmppim.RosterItem.fromElement(element)
146 roster[item.entity] = item
147
148 return roster
149
150 iq = IQ(self.xmlstream, 'get')
151 iq.addElement((ROSTER_NS, 'query'))
152 iq["to"] = to_jid.userhost()
153 d = iq.send()
154 d.addCallback(processRoster)
155 return d
156
157