-
Notifications
You must be signed in to change notification settings - Fork 12
/
drupal_services.py
executable file
·166 lines (133 loc) · 5.97 KB
/
drupal_services.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
#!/usr/bin/env python
#
# (c) 2009 Kasper Souren
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the Affero GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the Affro GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
drupal_services is a module to call Drupal Services.
Check /admin/build/services/settings on your Drupal install.
DrupalServices can be passed a configuration dict. Based on that it
will instantiate the proper class. Using Drupal Services with keys
but without a session is currently not supported.
"""
import xmlrpclib, time, random, string, hmac, hashlib, pprint
class BasicServices(xmlrpclib.Server):
"""Drupal Services without keys or sessions, not very secure."""
def __init__(self, url):
xmlrpclib.Server.__init__(self, url)
self.connection = self.system.connect()
self.sessid = self.connection['sessid']
def call(self, method_name, *args):
return getattr(self, method_name)(self._build_eval_list(method_name, args))
def _build_eval_list(self, method_name, args):
# method_name is used in ServicesSessidKey
return args
def __eval(self, to_eval):
print to_eval
try:
return eval(to_eval)
except xmlrpclib.Fault, err:
print "Oh oh. An xmlrpc fault occurred."
print "Fault code: %d" % err.faultCode
print "Fault string: %s" % err.faultString
class ServicesSessid(BasicServices):
"""Drupal Services with sessid."""
def __init__(self, url, username, password):
BasicServices.__init__(self, url)
self.session = self.user.login(self.sessid, username, password)
def _build_eval_list(self, args):
return ([self.sessid] +
map(None, args)) # Python refuses to concatenate list and tuple
class ServicesSessidKey(ServicesSessid):
"""Drupal Services with sessid and keys."""
def __init__(self, url, username, password, domain, key):
BasicServices.__init__(self, url)
self.domain = domain
self.key = key
self.session = self.call('user.login', username, password)
self.sessid = self.session['sessid']
def _build_eval_list(self, method_name, args):
hash, timestamp, nonce = self._token(method_name)
return ([hash,
self.domain, timestamp,
nonce, self.sessid] +
map(None, args))
def _token(self, api_function):
timestamp = str(int(time.mktime(time.localtime())))
nonce = "".join(random.sample(string.letters+string.digits, 10))
return (hmac.new(self.key, "%s;%s;%s;%s" %
(timestamp, self.domain, nonce, api_function),
hashlib.sha256).hexdigest(),
timestamp,
nonce)
class ServicesKey(BasicServices):
"""Drupal Services with keys."""
def __init__(self, url, domain, key):
BasicServices.__init__(self, url)
self.domain = domain
self.key = key
def _build_eval_list(self, method_name, args):
hash, timestamp, nonce = self._token(method_name)
return ([hash,
self.domain,
timestamp,
nonce] +
map(None, args))
def _token(self, api_function):
timestamp = str(int(time.mktime(time.localtime())))
nonce = "".join(random.sample(string.letters+string.digits, 10))
return (hmac.new(self.key, "%s;%s;%s;%s" %
(timestamp, self.domain, nonce, api_function),
hashlib.sha256).hexdigest(),
timestamp,
nonce)
class DrupalServices:
"""Drupal services class.
config is a nice way to deal with configuration files."""
def __init__(self, config):
self.config = config
if (config.has_key('username') and config.has_key('key')):
self.server = ServicesSessidKey(config['url'],
config['username'], config['password'],
config['domain'], config['key'])
elif (config.has_key('username')):
self.server = ServicesSessid(config['url'],
config['username'], config['password'])
elif (config.has_key('key')):
self.server = ServicesKey(config['url'],
config['domain'], config['key'])
else:
self.server = BasicServices(config['url'])
def call(self, method_name, *args):
# It would be neat to add a smart __getattr__ but that would
# only go one level deep, e.g. server.node, not
# server.node.save.
return self.server.call(method_name, *args)
def listMethods(self):
return self.server.system.listMethods()
def getInfo(self, method_name):
print method_name
print self.server.system.methodHelp(fName)
print self.server.system.methodSignature(fName)
if __name__ == "__main__":
from config import config
drupal = DrupalServices(config)
new_node = { 'type': 'page',
'title': 'Just a little test',
'body': '''Ordenar bibliotecas es ejercer de un modo silencioso el arte de la critica.
-- Jorge Luis Borges. (1899-1986) Escritor argentino.''',
}
new_node_id = drupal.call('node.save', new_node)
print 'New node id: %s' % new_node_id
print drupal.call('node.get', int(new_node_id), ['body'])