43
43
import cgi
44
44
import cgitb
45
45
import codecs
46
+ from collections import defaultdict , namedtuple
46
47
from flask import Flask , request , Response
47
48
from functools import partial , update_wrapper
48
49
import os
56
57
from wsgiref .simple_server import WSGIRequestHandler
57
58
58
59
from mig .shared .accountstate import check_account_accessible
59
- from mig .shared .base import client_dir_id , client_id_dir , cert_field_map
60
+ from mig .shared .base import canonical_user , client_dir_id , client_id_dir , cert_field_map
60
61
from mig .shared .conf import get_configuration_object
61
62
from mig .shared .compat import PY2
62
63
from mig .shared .griddaemons .openid import default_max_user_hits , \
72
73
valid_complex_url , InputException
73
74
from mig .shared .tlsserver import hardened_ssl_context
74
75
from mig .shared .url import urlparse , urlencode , parse_qsl
75
- from mig .shared .useradm import create_user , get_any_oid_user_dn , check_password_scramble , \
76
+ from mig .shared .useradm import get_any_oid_user_dn , check_password_scramble , \
76
77
check_hash
77
78
from mig .shared .userdb import default_db_path
78
79
from mig .shared .validstring import possible_user_id , is_valid_email_address
80
+ from mig .server .createuser import _main as createuser
79
81
80
82
# Update with extra fields
81
83
cert_field_map .update ({'role' : 'ROLE' , 'timezone' : 'TZ' , 'nickname' : 'NICK' ,
@@ -114,8 +116,8 @@ def _ensure_encoded_string(chunk):
114
116
exc .code : exc for exc in httpexceptions .__dict__ .values () if hasattr (exc , 'code' )}
115
117
116
118
117
- def http_error_from_status_code (http_status_code , http_url ):
118
- return httpexceptions_by_code [http_status_code ]()
119
+ def http_error_from_status_code (http_status_code , http_url , description = None ):
120
+ return httpexceptions_by_code [http_status_code ](description )
119
121
120
122
121
123
def quoteattr (val ):
@@ -156,7 +158,66 @@ def invalid_argument(arg):
156
158
raise ValueError ("Unexpected query variable: %s" % quoteattr (arg ))
157
159
158
160
159
- def _create_and_expose_server (configuration ):
161
+ # requests
162
+
163
+ class ValidationReport (RuntimeError ):
164
+ def __init__ (self , errors_by_field ):
165
+ self .errors_by_field = errors_by_field
166
+
167
+ def serialize (self , output_format = 'text' ):
168
+ if output_format == 'json' :
169
+ return dict (errors = self .errors_by_field )
170
+ else :
171
+ lines = ["- %s: required %s" % (k , v ) for k , v in self .errors_by_field .items ()]
172
+ lines .insert (0 , '' )
173
+ return 'payload failed to validate:%s' % ('\n ' .join (lines ),)
174
+
175
+
176
+ def _is_not_none (value ):
177
+ """value is not None"""
178
+ return value is not None
179
+
180
+
181
+ def _is_string_and_non_empty (value ):
182
+ """value is a non-empty string"""
183
+ return isinstance (value , str ) and len (value ) > 0
184
+
185
+
186
+ _REQUEST_ARGS_POST_USER = namedtuple ('PostUserArgs' , [
187
+ 'full_name' ,
188
+ 'organization' ,
189
+ 'state' ,
190
+ 'country' ,
191
+ 'email' ,
192
+ 'comment' ,
193
+ 'password' ,
194
+ ])
195
+
196
+
197
+ _REQUEST_ARGS_POST_USER ._validators = defaultdict (lambda : _is_not_none , dict (
198
+ full_name = _is_string_and_non_empty ,
199
+ organization = _is_string_and_non_empty ,
200
+ state = _is_string_and_non_empty ,
201
+ country = _is_string_and_non_empty ,
202
+ email = _is_string_and_non_empty ,
203
+ comment = _is_string_and_non_empty ,
204
+ password = _is_string_and_non_empty ,
205
+ ))
206
+
207
+ def validate_payload (definition , payload ):
208
+ args = definition (* [payload .get (field , None ) for field in definition ._fields ])
209
+
210
+ errors_by_field = {}
211
+ for field_name , field_value in args ._asdict ().items ():
212
+ validator_fn = definition ._validators [field_name ]
213
+ if not validator_fn (field_value ):
214
+ errors_by_field [field_name ] = validator_fn .__doc__
215
+ if errors_by_field :
216
+ raise ValidationReport (errors_by_field )
217
+ else :
218
+ return args
219
+
220
+ def _create_and_expose_server (server , configuration ):
160
221
app = Flask ('coreapi' )
161
222
162
223
@app .get ('/openid/user' )
@@ -171,9 +232,28 @@ def GET_user_username(username):
171
232
def POST_user ():
172
233
payload = request .get_json ()
173
234
174
- greeting = payload .get ('greeting' , '<none>' )
175
- if greeting == 'provocation' :
176
- raise http_error_from_status_code (422 , None )
235
+ # unpack the payload to a series of arguments
236
+ try :
237
+ validated = validate_payload (_REQUEST_ARGS_POST_USER , payload )
238
+ except ValidationReport as vr :
239
+ return http_error_from_status_code (400 , None , vr .serialize ())
240
+
241
+ args = list (validated )
242
+
243
+ try :
244
+
245
+ # user_dict = canonical_user(configuration, raw_user, raw_user.keys())
246
+ # except (AttributeError, IndexError, KeyError) as e:
247
+ # raise http_error_from_status_code(400, None)
248
+ # except Exception as e:
249
+ # pass
250
+
251
+ # try:
252
+ createuser (configuration , args )
253
+ except Exception as e :
254
+ pass
255
+
256
+ greeting = 'hello client!'
177
257
return Response (greeting , 201 )
178
258
179
259
return app
0 commit comments