saml: always retry user creation when detecting duplicates on sso (#75777)
gitea/wcs/pipeline/head This commit looks good
Details
gitea/wcs/pipeline/head This commit looks good
Details
This commit is contained in:
parent
1247b35500
commit
b0feb59521
|
@ -2,6 +2,7 @@ import json
|
|||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
import threading
|
||||
import uuid
|
||||
|
||||
import pytest
|
||||
|
@ -12,6 +13,7 @@ from wcs.ctl.management.commands.hobo_notify import Command as HoboNotifyCommand
|
|||
from wcs.qommon import force_str
|
||||
from wcs.qommon.afterjobs import AfterJob
|
||||
from wcs.qommon.http_request import HTTPRequest
|
||||
from wcs.sql import cleanup_connection
|
||||
from wcs.sql_criterias import NotNull
|
||||
|
||||
from .utilities import create_temporary_pub, get_app
|
||||
|
@ -996,3 +998,37 @@ def test_hobo_notify_call_command(pub, alt_tempdir):
|
|||
role.store()
|
||||
call_command('hobo_notify', os.path.join(alt_tempdir, 'message.json'))
|
||||
assert pub.role_class.count() == 0
|
||||
|
||||
|
||||
def test_provisionning_concurrency(pub):
|
||||
concurrency = 10
|
||||
user_data = {
|
||||
'uuid': 'a' * 32,
|
||||
'first_name': 'John',
|
||||
'last_name': 'Doé',
|
||||
'email': 'john.doe@example.net',
|
||||
'zipcode': '13400',
|
||||
'is_superuser': False,
|
||||
'is_active': True,
|
||||
'roles': [],
|
||||
}
|
||||
|
||||
for i in range(10):
|
||||
pub.user_class.wipe()
|
||||
b = threading.Barrier(concurrency)
|
||||
|
||||
def thread_function(b):
|
||||
b.wait()
|
||||
try:
|
||||
HoboNotifyCommand.create_or_update_user(pub, user_data)
|
||||
except Exception:
|
||||
pass
|
||||
cleanup_connection()
|
||||
|
||||
threads = [threading.Thread(target=thread_function, args=[b]) for i in range(concurrency)]
|
||||
for thread in threads:
|
||||
thread.start()
|
||||
for thread in threads:
|
||||
thread.join()
|
||||
|
||||
assert pub.user_class.count() == 1
|
||||
|
|
|
@ -3,6 +3,7 @@ import http.cookies
|
|||
import io
|
||||
import os
|
||||
import shutil
|
||||
import threading
|
||||
import urllib.parse
|
||||
import uuid
|
||||
|
||||
|
@ -24,6 +25,7 @@ from wcs.qommon.http_request import HTTPRequest
|
|||
from wcs.qommon.ident.idp import MethodAdminDirectory
|
||||
from wcs.qommon.misc import get_lasso_server
|
||||
from wcs.qommon.saml2 import Saml2Directory, SOAPException
|
||||
from wcs.sql import cleanup_connection
|
||||
|
||||
from .test_fc_auth import get_session
|
||||
from .test_hobo_notify import PROFILE
|
||||
|
@ -829,3 +831,28 @@ def test_opened_session_backoffice_url(pub):
|
|||
resp = app.get('/backoffice/studio/')
|
||||
assert resp.status_int == 302
|
||||
assert urllib.parse.parse_qs(urllib.parse.urlparse(resp.location).query).get('IsPassive')
|
||||
|
||||
|
||||
def test_sso_provisionning_concurrency(pub):
|
||||
concurrency = 10
|
||||
|
||||
directory = Saml2Directory()
|
||||
login = mock.Mock()
|
||||
login.identity.dump.return_value = ''
|
||||
|
||||
for i in range(3):
|
||||
pub.user_class.wipe()
|
||||
b = threading.Barrier(concurrency)
|
||||
|
||||
def thread_function(b):
|
||||
b.wait()
|
||||
directory.get_or_create_user_by_name_id(login, 'abcd')
|
||||
cleanup_connection()
|
||||
|
||||
threads = [threading.Thread(target=thread_function, args=[b]) for i in range(concurrency)]
|
||||
for thread in threads:
|
||||
thread.start()
|
||||
for thread in threads:
|
||||
thread.join()
|
||||
|
||||
assert pub.user_class.count() == 1
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
# along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import os
|
||||
import random
|
||||
import time
|
||||
import urllib.parse
|
||||
from xml.sax.saxutils import escape
|
||||
|
@ -539,6 +540,35 @@ class Saml2Directory(Directory):
|
|||
if save:
|
||||
user.store()
|
||||
|
||||
def get_or_create_user_by_name_id(self, login, ni, retry=0):
|
||||
user_class = get_publisher().user_class
|
||||
|
||||
if retry > 3:
|
||||
raise Exception('user lookup on sso failed after %s tries.' % retry)
|
||||
if retry > 0:
|
||||
time.sleep(random.random() * 2 * retry)
|
||||
|
||||
users = sorted(
|
||||
user_class.get_users_with_name_identifier(ni), key=lambda u: (u.last_seen or 0, -int(u.id))
|
||||
)
|
||||
if users:
|
||||
# if multiple users, use the more recently used or the younger
|
||||
user = users[-1]
|
||||
else:
|
||||
user = get_publisher().user_class(ni)
|
||||
user.name_identifiers = [ni]
|
||||
if login.identity:
|
||||
user.lasso_dump = login.identity.dump()
|
||||
user.store()
|
||||
|
||||
others = user_class.get_users_with_name_identifier(ni)
|
||||
# there is an user mapping to the same id with a younger id:
|
||||
# try again.
|
||||
if len(others) > 1:
|
||||
user.remove_self()
|
||||
return self.get_or_create_user_by_name_id(login, ni, retry=retry + 1)
|
||||
return user
|
||||
|
||||
def lookup_user(self, session, login):
|
||||
if not login.nameIdentifier or not login.nameIdentifier.content:
|
||||
return None
|
||||
|
|
Loading…
Reference in New Issue