Skip to content

Commit b9c6c14

Browse files
committed
Implement finding users by email address.
1 parent 2aba4f6 commit b9c6c14

File tree

2 files changed

+127
-1
lines changed

2 files changed

+127
-1
lines changed

mig/services/coreapi/server.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@
7474
from mig.shared.tlsserver import hardened_ssl_context
7575
from mig.shared.url import urlparse, urlencode, parse_qsl
7676
from mig.shared.useradm import get_any_oid_user_dn, check_password_scramble, \
77-
check_hash
77+
check_hash, search_users as useradm_search_users
7878
from mig.shared.userdb import default_db_path
7979
from mig.shared.validstring import possible_user_id, is_valid_email_address
8080
from mig.server.createuser import _main as createuser
@@ -203,6 +203,13 @@ def _is_string_and_non_empty(value):
203203
))
204204

205205

206+
def search_users(configuration, search_filter):
207+
conf_path = configuration.config_file
208+
db_path = default_db_path(configuration)
209+
_, hits = useradm_search_users(search_filter, conf_path, db_path)
210+
return list((obj for _, obj in hits))
211+
212+
206213
def validate_payload(definition, payload):
207214
args = definition(*[payload.get(field, None) for field in definition._fields])
208215

@@ -228,6 +235,19 @@ def GET_user():
228235
def GET_user_username(username):
229236
return 'FOOBAR'
230237

238+
@app.get('/user/find')
239+
def GET_user_find():
240+
query_params = request.args
241+
242+
objects = search_users(configuration, {
243+
'email': query_params['email']
244+
})
245+
246+
if len(objects) != 1:
247+
raise http_error_from_status_code(404, None)
248+
249+
return dict(objects=objects)
250+
231251
@app.post('/user')
232252
def POST_user():
233253
payload = request.get_json()

tests/test_mig_services_coreapi.py

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import shutil
77
import sys
88
import unittest
9+
import urllib.parse
910
from threading import Thread
1011
from unittest import skip
1112

@@ -15,6 +16,7 @@
1516

1617
from mig.services.coreapi import ThreadedApiHttpServer, \
1718
_create_and_expose_server, _extend_configuration
19+
from mig.server.createuser import _main as _createuser
1820
from mig.shared.conf import get_configuration_object
1921
from mig.shared.useradm import _USERADM_CONFIG_DIR_KEYS
2022

@@ -257,5 +259,109 @@ def _on_instance(server):
257259
return server_thread
258260

259261

262+
class MigServerGrid_openid__existing_user(MigTestCase, HtmlAssertMixin):
263+
def before_each(self):
264+
self.server_addr = None
265+
self.server_thread = None
266+
267+
for config_key in _USERADM_CONFIG_DIR_KEYS:
268+
dir_path = getattr(self.configuration, config_key)[0:-1]
269+
try:
270+
shutil.rmtree(dir_path)
271+
except OSError as exc:
272+
if exc.errno != errno.ENOENT: # FileNotFoundError
273+
pass
274+
275+
_createuser(self.configuration, [
276+
"Test User",
277+
"Test Org",
278+
"NA",
279+
"DK",
280+
"user@example.com",
281+
"This is the create comment",
282+
"password"
283+
], default_renew=True)
284+
pass
285+
286+
def _provide_configuration(self):
287+
return 'testconfig'
288+
289+
def after_each(self):
290+
if self.server_thread:
291+
self.server_thread.stop()
292+
293+
def issue_request(self, request_path):
294+
return self.issue_GET(request_path)
295+
296+
def issue_GET(self, request_path, query_dict=None, response_encoding='textual'):
297+
assert isinstance(request_path, str) and request_path.startswith('/'), "require http path starting with /"
298+
request_url = ''.join(('http://', self.server_addr[0], ':', str(self.server_addr[1]), request_path))
299+
300+
if query_dict is not None:
301+
query_string = urllib.parse.urlencode(query_dict)
302+
request_url = ''.join((request_url, '?', query_string))
303+
304+
status = 0
305+
data = None
306+
307+
try:
308+
response = urlopen(request_url, None, timeout=2000)
309+
310+
status = response.getcode()
311+
data = response.read()
312+
except HTTPError as httpexc:
313+
status = httpexc.code
314+
data = None
315+
316+
content = attempt_to_decode_response_data(data, response_encoding)
317+
return (status, content)
318+
319+
def test_GET_openid_user_find(self):
320+
flask_app = None
321+
322+
self.server_addr = ('localhost', 4567)
323+
configuration = self._make_configuration(self.logger, self.server_addr)
324+
self.server_thread = self._make_server(configuration)
325+
#flask_app = _create_and_bind_flask_app_to_server(self.server_thread, configuration)
326+
self.server_thread.start_wait_until_ready()
327+
328+
status, content = self.issue_GET('/user/find', {
329+
'email': 'user@example.com'
330+
})
331+
332+
self.assertEqual(status, 200)
333+
334+
self.assertIsInstance(content, dict)
335+
self.assertIn('objects', content)
336+
self.assertIsInstance(content['objects'], list)
337+
338+
user = content['objects'][0]
339+
# check we received the correct user
340+
self.assertEqual(user['full_name'], 'Test User')
341+
342+
def _make_configuration(self, test_logger, server_addr, overrides=None):
343+
configuration = self.configuration
344+
_extend_configuration(
345+
configuration,
346+
server_addr[0],
347+
server_addr[1],
348+
logger=test_logger,
349+
expandusername=False,
350+
host_rsa_key='',
351+
nossl=True,
352+
show_address=False,
353+
show_port=False,
354+
)
355+
return configuration
356+
357+
@staticmethod
358+
def _make_server(configuration):
359+
def _on_instance(server):
360+
server.server_app = _create_and_expose_server(server, server.configuration)
361+
362+
server_thread = make_wrapped_server(ThreadedApiHttpServer, configuration, on_instance=_on_instance)
363+
return server_thread
364+
365+
260366
if __name__ == '__main__':
261367
testmain()

0 commit comments

Comments
 (0)