From 4df79b87beceed1604f687fe70516d16a5902565 Mon Sep 17 00:00:00 2001 From: David Schultz Date: Mon, 16 Oct 2023 15:29:10 -0500 Subject: [PATCH] [minor] remove token from registration page (#63) * remove token from registration page * update setup.cfg * update requirements-tests.txt * update requirements.txt * fix flake8 * remove old import * remove old import 2 * remove reg_token import * remove auth test for username * update requirements-tests.txt * update requirements.txt * fix inst register test * update setup.cfg * update .gitignore * update dependencies*.log files(s) * new py-setup action doesn't generate requirements.txt files anymore * adjust docker container to use python 3.10 and install directly from setup.cfg * update dependencies*.log files(s) --------- Co-authored-by: github-actions --- .github/workflows/wipac_cicd.yaml | 2 +- .gitignore | 4 + Dockerfile | 13 +-- cypress/integration/insts.spec.js | 17 +--- cypress/integration/register.spec.js | 37 +------- cypress/support/reg_token.js | 29 ------- dependencies-from-Dockerfile.log | 30 +++++++ docs/admin_insts.md | 12 +-- requirements-tests.txt | 123 --------------------------- requirements.txt | 87 ------------------- setup.cfg | 5 +- tests/test_api_insts.py | 21 ----- tests/test_api_registration.py | 56 ------------ tests/test_api_users.py | 18 ---- tests/util.py | 7 +- user_mgmt/insts.py | 9 -- user_mgmt/registration.py | 93 -------------------- user_mgmt/server.py | 4 - user_mgmt/static/main.js | 3 +- user_mgmt/static/routes/insts.js | 24 +----- user_mgmt/static/routes/register.js | 102 +++++++++------------- user_mgmt/users.py | 2 - 22 files changed, 101 insertions(+), 597 deletions(-) delete mode 100644 cypress/support/reg_token.js create mode 100644 dependencies-from-Dockerfile.log delete mode 100644 requirements-tests.txt delete mode 100644 requirements.txt delete mode 100644 tests/test_api_registration.py delete mode 100644 user_mgmt/registration.py diff --git a/.github/workflows/wipac_cicd.yaml b/.github/workflows/wipac_cicd.yaml index 83a076e..5555327 100644 --- a/.github/workflows/wipac_cicd.yaml +++ b/.github/workflows/wipac_cicd.yaml @@ -24,7 +24,7 @@ jobs: uses: actions/checkout@v3 with: token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} - - uses: WIPACrepo/wipac-dev-py-setup-action@v1.15 + - uses: WIPACrepo/wipac-dev-py-setup-action@v2.6 py-versions: needs: [py-setup] diff --git a/.gitignore b/.gitignore index b6e4761..5eacea8 100644 --- a/.gitignore +++ b/.gitignore @@ -127,3 +127,7 @@ dmypy.json # Pyre type checker .pyre/ + + +# wipac-dev-py-setup-action +!dependencies*.log diff --git a/Dockerfile b/Dockerfile index 4b65ef9..5901c3c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,15 +1,18 @@ -FROM python:3.9 +FROM python:3.10 RUN useradd -m -U keycloak -COPY requirements.txt ./ -RUN pip install --no-cache-dir -r requirements.txt +RUN mkdir /app && chown keycloak:keycloak /app -WORKDIR /home/keycloak +WORKDIR /app USER keycloak COPY . . -ENV PYTHONPATH=/home/keycloak +USER root + +RUN pip install -e . + +USER keycloak CMD ["python", "-m", "user_mgmt"] diff --git a/cypress/integration/insts.spec.js b/cypress/integration/insts.spec.js index 2fd5bce..0ddf5ea 100644 --- a/cypress/integration/insts.spec.js +++ b/cypress/integration/insts.spec.js @@ -1,6 +1,5 @@ import keycloak from '../support/keycloak' -import reg_token from '../support/reg_token' context('Institutions Page', () => { it('inst approvals', () => { @@ -9,7 +8,6 @@ context('Institutions Page', () => { admin_insts: {instA:{users:['userA', 'userB'], "authorlist-physics":['userA'], "authorlist-astro":[]}}, inst_approvals: {instA: ['userC']} }) - reg_token({}) cy.get('#nav .active').contains('institutions', {matchCase: false}) cy.get('#nav li').should('have.length', 2) @@ -29,7 +27,6 @@ context('Institutions Page', () => { username: 'user', user_profile: {firstName: 'Foo', lastName: 'Bar', email: 'foo@bar'} }) - reg_token({}) cy.get('[data-test="userA"]').within(() => { cy.get('.username').contains('userA', {matchCase: false}) @@ -55,7 +52,6 @@ context('Institutions Page', () => { keycloak({ admin_insts: {instA:{users:['userA', 'userB'], "authorlist-physics":['userA'], "authorlist-astro":[]}} }) - reg_token({}) cy.get('[data-test="userA"] .delete').click() cy.wait('@api-institution-users-delete').its('request.url').should('include', 'userA') @@ -66,7 +62,6 @@ context('Institutions Page', () => { keycloak({ admin_insts: {instA:{users:['userA', 'userB'], "authorlist-physics":['userA'], "authorlist-astro":[]}} }) - reg_token({}) cy.get('[data-test="userB"]').within(() => { cy.get('.username').contains('userB', {matchCase: false}) @@ -89,7 +84,6 @@ context('Institutions Page', () => { keycloak({ admin_insts: {instA:{users:['userA', 'userB'], "authorlist":['userA']}} }) - reg_token({}) cy.get('[data-test="userA"]').within(() => { cy.get('.username').contains('userA', {matchCase: false}) @@ -110,7 +104,6 @@ context('Institutions Page', () => { keycloak({ admin_insts: {instA:{users:['userA', 'userB'], "authorlist-physics":['userA'], "authorlist-astro":[]}} }) - reg_token({}) cy.get('[data-test="userB"] .delete').click() cy.wait('@api-institution-users-delete').its('request.url').should('include', 'userB') @@ -126,15 +119,8 @@ context('Institutions Page', () => { admin_insts: {instA:{users:['userA', 'userB'], "authorlist-physics":['userA'], "authorlist-astro":[]}}, token_raw: 'tokentoken' }) - reg_token({token: 'foobar'}) - cy.wait('@api-reg_token').should(({ request, response }) => { - expect(request.headers).to.include({ - 'authorization': 'bearer tokentoken' - }) - }) - - cy.get('[data-test="registration-link"]').should('exist').should('have.attr', 'data-reg-token', 'foobar') + cy.get('[data-test="registration-link"]').should('exist') }) it('inst edit user profile', () => { @@ -143,7 +129,6 @@ context('Institutions Page', () => { admin_insts: {instA:{users:['userA', 'userB'], "authorlist-physics":['userA'], "authorlist-astro":[]}}, user_profile: {'firstName': 'Foo', 'lastName': 'Bar', 'email': 'foo@bar', 'orcid': '0000-0000-0000-0000'} }) - reg_token({}) cy.get('[data-test="userA"] .profile').click() diff --git a/cypress/integration/register.spec.js b/cypress/integration/register.spec.js index e345ed1..88d46ee 100644 --- a/cypress/integration/register.spec.js +++ b/cypress/integration/register.spec.js @@ -1,20 +1,14 @@ import keycloak from '../support/keycloak' -import reg_token from '../support/reg_token' context('Registration Page', () => { it('register', () => { - reg_token({token: 'foobar'}) - cy.visit('/register?reg_token=foobar') + cy.visit('/register') keycloak({ insts: ['instA'], new_username: 'fbar' }) - cy.wait('@api-reg_token-validate').should(({ request, response }) => { - expect(request.url).to.match(/foobar$/) - }) - cy.get('#nav .active').contains('register', {matchCase: false}) cy.get('[data-test="institution"]').should('exist').should('be.disabled') @@ -26,9 +20,6 @@ context('Registration Page', () => { cy.get('[name="last_name"]').type('bar') cy.wait('@api-username-post').should(({ request, response }) => { - expect(request.headers).to.include({ - 'authorization': 'bearer foobar' - }) expect(response.body).to.deep.eq({ "username": "fbar" }) @@ -39,9 +30,6 @@ context('Registration Page', () => { cy.get('[data-test="submit"]').click() cy.wait('@api-inst-approvals-post').should(({ request, response }) => { - expect(request.headers).to.include({ - 'authorization': 'bearer foobar' - }) expect(request.body).to.deep.eq({ 'experiment': 'test-exp', 'institution': 'instA', @@ -54,17 +42,12 @@ context('Registration Page', () => { }) it('clear-username', () => { - reg_token({token: 'foobar'}) - cy.visit('/register?reg_token=foobar') + cy.visit('/register') keycloak({ insts: ['instA'], new_username: 'fbar' }) - cy.wait('@api-reg_token-validate').should(({ request, response }) => { - expect(request.url).to.match(/foobar$/) - }) - cy.get('#nav .active').contains('register', {matchCase: false}) cy.get('[data-test="institution"]').should('exist').should('be.disabled') @@ -76,9 +59,6 @@ context('Registration Page', () => { cy.get('[name="last_name"]').type('bar') cy.wait('@api-username-post').should(({ request, response }) => { - expect(request.headers).to.include({ - 'authorization': 'bearer foobar' - }) expect(response.body).to.deep.eq({ "username": "fbar" }) @@ -89,9 +69,6 @@ context('Registration Page', () => { cy.get('[name="username"]').should('have.value', '') cy.wait('@api-username-post').should(({ request, response }) => { - expect(request.headers).to.include({ - 'authorization': 'bearer foobar' - }) expect(response.body).to.deep.eq({ "username": "fbar" }) @@ -100,8 +77,7 @@ context('Registration Page', () => { }) it('username-cycle-detect', () => { - reg_token({token: 'foobar'}) - cy.visit('/register?reg_token=foobar') + cy.visit('/register') keycloak({ insts: ['instA'], new_username: function(input_username){ @@ -114,10 +90,6 @@ context('Registration Page', () => { } }) - cy.wait('@api-reg_token-validate').should(({ request, response }) => { - expect(request.url).to.match(/foobar$/) - }) - cy.get('#nav .active').contains('register', {matchCase: false}) cy.get('[data-test="institution"]').should('exist').should('be.disabled') @@ -129,9 +101,6 @@ context('Registration Page', () => { cy.get('[name="last_name"]').type('bar') cy.wait('@api-username-post').should(({ request, response }) => { - expect(request.headers).to.include({ - 'authorization': 'bearer foobar' - }) expect(response.body).to.deep.eq({ "username": "fbar" }) diff --git a/cypress/support/reg_token.js b/cypress/support/reg_token.js deleted file mode 100644 index 1dcc3fc..0000000 --- a/cypress/support/reg_token.js +++ /dev/null @@ -1,29 +0,0 @@ -// ES2015 module export function -// which simply concats a class selector -// to the string argument - -export default (params) => { - params = Object.assign({ - token: 'thetoken' - }, params) - - let obj = {} - - cy.intercept({ - method: 'POST', - url: '/api/reg_token', - }, { - statusCode: 200, - body: {token: params.token}, - }).as('api-reg_token') - - cy.intercept({ - method: 'GET', - url: '/api/reg_token/*', - }, { - statusCode: 200, - body: {}, - }).as('api-reg_token-validate') - - return obj -} \ No newline at end of file diff --git a/dependencies-from-Dockerfile.log b/dependencies-from-Dockerfile.log new file mode 100644 index 0000000..0ed48ef --- /dev/null +++ b/dependencies-from-Dockerfile.log @@ -0,0 +1,30 @@ +aio-pika==9.3.0 +aiormq==6.7.7 +cachetools==5.3.1 +certifi==2023.7.22 +cffi==1.16.0 +charset-normalizer==3.3.0 +cryptography==41.0.4 +dnspython==2.4.2 +idna==3.4 +ldap3==2.9.1 +motor==3.3.1 +multidict==6.0.4 +pamqp==3.2.1 +pyasn1==0.5.0 +pycparser==2.21 +PyJWT==2.8.0 +pymongo==4.5.0 +pypng==0.20220715.0 +qrcode==7.4.2 +requests==2.31.0 +requests-futures==1.0.1 +tornado==6.3.3 +typing_extensions==4.8.0 +Unidecode==1.3.7 +urllib3==2.0.6 +-e /app +wipac-dev-tools==1.7.0 +wipac-keycloak-rest-services==1.4.44 +wipac-rest-tools==1.6.0 +yarl==1.9.2 diff --git a/docs/admin_insts.md b/docs/admin_insts.md index 20fbf6e..167f651 100644 --- a/docs/admin_insts.md +++ b/docs/admin_insts.md @@ -10,16 +10,10 @@ to join your institution, or directly make changes for existing users. ## Registering New Users -New users should register through a link provided on the institution page, -which contains a 7-day authorization. +New users should register through the [https://user-management.icecube.aq/register](registration page). +It is possible to give out a specific link for an institution, like: -As an example, for IceCube/Aachen, a registration link is: - - https://user-management.icecube.aq/register?experiment=IceCube&institution=Aachen®_token=XXXXXXX - -Here is what the registration section looks like: - -![registration link](images/admin_inst_reg_link.png) + https://user-management.icecube.aq/register?experiment=IceCube&institution=Aachen ### Manually adding a new user diff --git a/requirements-tests.txt b/requirements-tests.txt deleted file mode 100644 index 199d3c3..0000000 --- a/requirements-tests.txt +++ /dev/null @@ -1,123 +0,0 @@ -# -# This file is autogenerated by pip-compile with Python 3.10 -# by the following command: -# -# pip-compile --extra=tests --output-file=requirements-tests.txt -# -aio-pika==9.2.2 - # via wipac-keycloak-rest-services -aiormq==6.7.7 - # via aio-pika -cachetools==5.3.1 - # via - # user-mgmt (setup.py) - # wipac-rest-tools -certifi==2023.7.22 - # via requests -cffi==1.15.1 - # via cryptography -charset-normalizer==3.2.0 - # via requests -coverage[toml]==7.3.0 - # via - # pytest-cov - # user-mgmt (setup.py) -cryptography==41.0.3 - # via pyjwt -dnspython==2.4.2 - # via pymongo -exceptiongroup==1.1.3 - # via pytest -flake8==6.1.0 - # via user-mgmt (setup.py) -idna==3.4 - # via - # requests - # yarl -iniconfig==2.0.0 - # via pytest -ldap3==2.9.1 - # via wipac-keycloak-rest-services -mccabe==0.7.0 - # via flake8 -motor==3.3.1 - # via - # user-mgmt (setup.py) - # wipac-keycloak-rest-services -multidict==6.0.4 - # via yarl -packaging==23.1 - # via pytest -pamqp==3.2.1 - # via aiormq -pluggy==1.3.0 - # via pytest -pyasn1==0.5.0 - # via ldap3 -pycodestyle==2.11.0 - # via flake8 -pycparser==2.21 - # via cffi -pyflakes==3.1.0 - # via flake8 -pyjwt[crypto]==2.8.0 - # via wipac-rest-tools -pymongo==4.5.0 - # via motor -pypng==0.20220715.0 - # via qrcode -pytest==7.4.0 - # via - # pytest-asyncio - # pytest-cov - # pytest-mock - # user-mgmt (setup.py) -pytest-asyncio==0.21.1 - # via user-mgmt (setup.py) -pytest-cov==4.1.0 - # via user-mgmt (setup.py) -pytest-mock==3.11.1 - # via user-mgmt (setup.py) -qrcode==7.4.2 - # via wipac-rest-tools -requests==2.31.0 - # via - # requests-futures - # wipac-dev-tools - # wipac-keycloak-rest-services - # wipac-rest-tools -requests-futures==1.0.1 - # via wipac-rest-tools -tomli==2.0.1 - # via - # coverage - # pytest -tornado==6.3.3 - # via - # user-mgmt (setup.py) - # wipac-rest-tools -typing-extensions==4.7.1 - # via - # qrcode - # wipac-dev-tools -unidecode==1.3.6 - # via - # user-mgmt (setup.py) - # wipac-keycloak-rest-services -urllib3==2.0.4 - # via requests -wipac-dev-tools==1.6.16 - # via - # user-mgmt (setup.py) - # wipac-keycloak-rest-services - # wipac-rest-tools -wipac-keycloak-rest-services==1.4.37 - # via user-mgmt (setup.py) -wipac-rest-tools==1.5.2 - # via - # user-mgmt (setup.py) - # wipac-keycloak-rest-services -yarl==1.9.2 - # via - # aio-pika - # aiormq diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 0b45e0d..0000000 --- a/requirements.txt +++ /dev/null @@ -1,87 +0,0 @@ -# -# This file is autogenerated by pip-compile with Python 3.10 -# by the following command: -# -# pip-compile --output-file=requirements.txt -# -aio-pika==9.2.2 - # via wipac-keycloak-rest-services -aiormq==6.7.7 - # via aio-pika -cachetools==5.3.1 - # via - # user-mgmt (setup.py) - # wipac-rest-tools -certifi==2023.7.22 - # via requests -cffi==1.15.1 - # via cryptography -charset-normalizer==3.2.0 - # via requests -cryptography==41.0.3 - # via pyjwt -dnspython==2.4.2 - # via pymongo -idna==3.4 - # via - # requests - # yarl -ldap3==2.9.1 - # via wipac-keycloak-rest-services -motor==3.3.1 - # via - # user-mgmt (setup.py) - # wipac-keycloak-rest-services -multidict==6.0.4 - # via yarl -pamqp==3.2.1 - # via aiormq -pyasn1==0.5.0 - # via ldap3 -pycparser==2.21 - # via cffi -pyjwt[crypto]==2.8.0 - # via wipac-rest-tools -pymongo==4.5.0 - # via motor -pypng==0.20220715.0 - # via qrcode -qrcode==7.4.2 - # via wipac-rest-tools -requests==2.31.0 - # via - # requests-futures - # wipac-dev-tools - # wipac-keycloak-rest-services - # wipac-rest-tools -requests-futures==1.0.1 - # via wipac-rest-tools -tornado==6.3.3 - # via - # user-mgmt (setup.py) - # wipac-rest-tools -typing-extensions==4.7.1 - # via - # qrcode - # wipac-dev-tools -unidecode==1.3.6 - # via - # user-mgmt (setup.py) - # wipac-keycloak-rest-services -urllib3==2.0.4 - # via requests -wipac-dev-tools==1.6.16 - # via - # user-mgmt (setup.py) - # wipac-keycloak-rest-services - # wipac-rest-tools -wipac-keycloak-rest-services==1.4.37 - # via user-mgmt (setup.py) -wipac-rest-tools==1.5.2 - # via - # user-mgmt (setup.py) - # wipac-keycloak-rest-services -yarl==1.9.2 - # via - # aio-pika - # aiormq diff --git a/setup.cfg b/setup.cfg index eed23d8..c71a4ba 100644 --- a/setup.cfg +++ b/setup.cfg @@ -2,8 +2,9 @@ python_min = 3.9 package_dirs = user_mgmt -[metadata] # generated by wipac:cicd_setup_builder: version +[metadata] # generated by wipac:cicd_setup_builder: name, version version = attr: user_mgmt.__version__ +name = user-mgmt [semantic_release] # fully-generated by wipac:cicd_setup_builder version_variable = user_mgmt/__init__.py:__version__ @@ -24,7 +25,7 @@ install_requires = wipac-dev-tools wipac-keycloak-rest-services wipac-rest-tools -python_requires = >=3.9, <3.12 +python_requires = >=3.9, <3.13 packages = find: [options.extras_require] diff --git a/tests/test_api_insts.py b/tests/test_api_insts.py index 7fefcdd..b0659a2 100644 --- a/tests/test_api_insts.py +++ b/tests/test_api_insts.py @@ -221,27 +221,6 @@ async def test_inst_approvals_register(server, mongo_client, reg_token_client, e assert ret[0]['experiment'] == data['experiment'] assert ret[0]['institution'] == data['institution'] -@pytest.mark.asyncio -async def test_inst_approvals_register_invalid_auth(server, mongo_client, reg_token_client, email_patch): - _, krs_client, address, *_ = server - client = await reg_token_client(exp_seconds=0) - await asyncio.sleep(0.01) - - await krs.groups.create_group('/institutions', rest_client=krs_client) - await krs.groups.create_group('/institutions/IceCube', rest_client=krs_client) - await krs.groups.create_group('/institutions/IceCube/UW-Madison', rest_client=krs_client) - - data = { - 'experiment': 'IceCube', - 'institution': 'UW-Madison', - 'first_name': 'First', - 'last_name': 'Last', - 'username': 'flast', - 'email': 'test@test', - } - with pytest.raises(Exception): - await client.request('POST', '/api/inst_approvals', data) - @pytest.mark.asyncio async def test_inst_approvals_register_with_admins(server, mongo_client, reg_token_client, email_patch): rest, krs_client, address, *_ = server diff --git a/tests/test_api_registration.py b/tests/test_api_registration.py deleted file mode 100644 index c572944..0000000 --- a/tests/test_api_registration.py +++ /dev/null @@ -1,56 +0,0 @@ -import asyncio - -import pytest -from rest_tools.client import AsyncSession - -import krs.groups - -from .krs_util import keycloak_bootstrap -from .util import port, server, mongo_client, email_patch -import user_mgmt.registration - -@pytest.mark.asyncio -async def test_valid_token(mongo_client): - token = await user_mgmt.registration.create_token(mongo_client, 'test') - await user_mgmt.registration.valid_token(mongo_client, token) - -@pytest.mark.asyncio -async def test_invalid_token(mongo_client): - token = await user_mgmt.registration.create_token(mongo_client, 'test', exp_seconds=0) - await asyncio.sleep(0.01) - with pytest.raises(Exception): - await user_mgmt.registration.valid_token(mongo_client, token) - -@pytest.mark.asyncio -async def test_registration_token_create(server, mongo_client): - rest, krs_client, *_ = server - - await krs.groups.create_group('/institutions', rest_client=krs_client) - await krs.groups.create_group('/institutions/IceCube', rest_client=krs_client) - await krs.groups.create_group('/institutions/IceCube/UW-Madison', rest_client=krs_client) - - client = await rest('test') - - with pytest.raises(Exception): - await client.request('POST', '/api/reg_token') - - client2 = await rest('test2', groups=['/institutions/IceCube/UW-Madison/_admin']) - - ret = await client2.request('POST', '/api/reg_token') - assert 'token' in ret - -@pytest.mark.asyncio -async def test_registration_token_valid(server, mongo_client): - rest, krs_client, *_ = server - - await krs.groups.create_group('/institutions', rest_client=krs_client) - await krs.groups.create_group('/institutions/IceCube', rest_client=krs_client) - await krs.groups.create_group('/institutions/IceCube/UW-Madison', rest_client=krs_client) - - client = await rest('test') - client2 = await rest('test2', groups=['/institutions/IceCube/UW-Madison/_admin']) - - ret = await client2.request('POST', '/api/reg_token') - token = ret['token'] - - await client.request('GET', f'/api/reg_token/{token}') diff --git a/tests/test_api_users.py b/tests/test_api_users.py index bb55d52..17aa443 100644 --- a/tests/test_api_users.py +++ b/tests/test_api_users.py @@ -161,24 +161,6 @@ async def test_username_select(server, reg_token_client): with pytest.raises(Exception): await client.request('POST', '/api/username', args) -@pytest.mark.asyncio -async def test_username_auth(server, reg_token_client): - rest, krs_client, *_ = server - client = await rest('test') - - args = { - 'first_name': 'Foo', - 'last_name': 'Bar', - 'username': 'fbarbar' - } - with pytest.raises(Exception): - await client.request('POST', '/api/username', args) - - client2 = await reg_token_client(exp_seconds=0) - await asyncio.sleep(0.01) - with pytest.raises(Exception): - await client2.request('POST', '/api/username', args) - @pytest.mark.asyncio async def test_username_invalid(server, reg_token_client, monkeypatch): rest, krs_client, *_ = server diff --git a/tests/util.py b/tests/util.py index 2ffcbec..69a994a 100644 --- a/tests/util.py +++ b/tests/util.py @@ -10,7 +10,6 @@ import motor.motor_asyncio from user_mgmt.server import create_server -from user_mgmt.registration import create_token import krs.bootstrap import krs.users @@ -84,11 +83,7 @@ async def mongo_client(): async def reg_token_client(mongo_client, server): _, _, url, *_ = server async def client(exp_seconds=None, timeout=10): - kwargs = {} - if exp_seconds is not None: - kwargs['exp_seconds'] = exp_seconds - token = await create_token(mongo_client, username='test', **kwargs) - return RestClient(url, token=token, timeout=timeout, retries=0) + return RestClient(url, timeout=timeout, retries=0) yield client @pytest.fixture diff --git a/user_mgmt/insts.py b/user_mgmt/insts.py index ab8249b..f093b72 100644 --- a/user_mgmt/insts.py +++ b/user_mgmt/insts.py @@ -14,7 +14,6 @@ import krs.email from .handler import MyHandler -from .registration import valid_token from .users import Username audit_logger = logging.getLogger('audit') @@ -242,14 +241,6 @@ async def post(self): approval_data['username'] = user else: - try: - type, token = self.request.headers['Authorization'].split(' ', 1) - if type.lower() != 'bearer': - raise Exception('bad header type') - await valid_token(self.db, token) - except Exception: - raise HTTPError(403, reason="authentication failed") - logging.info('new user registration') req_fields = { 'experiment': str, diff --git a/user_mgmt/registration.py b/user_mgmt/registration.py deleted file mode 100644 index c09e1f0..0000000 --- a/user_mgmt/registration.py +++ /dev/null @@ -1,93 +0,0 @@ -""" -Handle group mamagement actions. -""" -from functools import wraps -import logging -import time -import uuid - -from tornado.web import HTTPError -from rest_tools.server import catch_error, authenticated - -from .handler import MyHandler - - -TOKEN_EXP_SEC = 3600*24*7 # 7 days - - -async def create_token(db, username, exp_seconds=TOKEN_EXP_SEC): - token = uuid.uuid1().hex - now = time.time() - exp = now+exp_seconds - logging.debug('create token %s with exp %f (+%f)', token, exp, exp_seconds) - await db.reg_tokens.insert_one({ - 'token': token, - 'create': now, - 'exp': exp, - 'auth_user': username, - }) - return token - - -async def valid_token(db, token): - try: - uuid.UUID(token) - except Exception: - raise HTTPError(403, 'invalid authorization') - logging.debug('testing token for validity: %s', token) - - ret = await db.reg_tokens.find_one({'token': token}, projection={'_id': False}) - now = time.time() - if (not ret) or ret.get('exp') < now: - raise HTTPError(403, 'invalid authorization') - logging.debug('valid token (now=%f): %r', now, ret) - - -def authenticate_reg_token(method): - """Decorate methods with this to require that the Authorization header is - filled with a valid token. - - On failure, raises a 403 error. - - Raises: - :py:class:`tornado.web.HTTPError` - """ - @wraps(method) - async def wrapper(self, *args, **kwargs): - try: - type, token = self.request.headers['Authorization'].split(' ', 1) - if type.lower() != 'bearer': - raise Exception('bad header type') - await valid_token(self.db, token) - except Exception: - logging.debug('failed registration token auth', exc_info=True) - raise HTTPError(403, reason="authentication failed") - return await method(self, *args, **kwargs) - return wrapper - - -class RegistrationToken(MyHandler): - @authenticated - @catch_error - async def post(self): - """ - Create a new registration token. - - Only inst admins can create tokens. - """ - insts = await self.get_admin_institutions() - if not insts: - raise HTTPError(403, 'invalid authorization') - - token = await create_token(self.db, self.auth_data['username']) - self.write({'token': token}) - - -class RegistrationTokenValid(MyHandler): - @catch_error - async def get(self, token): - """Validate a registration token.""" - await valid_token(self.db, token) - - # success - self.write({}) diff --git a/user_mgmt/server.py b/user_mgmt/server.py index eac38f0..2ce9b6f 100644 --- a/user_mgmt/server.py +++ b/user_mgmt/server.py @@ -21,7 +21,6 @@ InstApprovalsActionApprove, InstApprovalsActionDeny) from .groups import (MultiGroups, Group, GroupUser, GroupApprovals, GroupApprovalsActionApprove, GroupApprovalsActionDeny) -from .registration import RegistrationToken, RegistrationTokenValid from .users import MultiUser, User, Username @@ -112,9 +111,6 @@ def create_server(): server.add_route(r'/api/group_approvals/(?P\w+)/actions/approve', GroupApprovalsActionApprove, kwargs) server.add_route(r'/api/group_approvals/(?P\w+)/actions/deny', GroupApprovalsActionDeny, kwargs) - server.add_route('/api/reg_token', RegistrationToken, kwargs) - server.add_route(r'/api/reg_token/(?P\w+)', RegistrationTokenValid, kwargs) - server.add_route(r'/api/users', MultiUser, kwargs) server.add_route(r'/api/users/(?P[\w\-]+)', User, kwargs) server.add_route('/api/username', Username, kwargs) diff --git a/user_mgmt/static/main.js b/user_mgmt/static/main.js index de155c5..79c6f6e 100644 --- a/user_mgmt/static/main.js +++ b/user_mgmt/static/main.js @@ -196,8 +196,7 @@ export default async function vue_startup(keycloak){ { path: '/register', name: 'register', component: Register, props: (route) => ({ experiment: route.query.experiment, - institution: route.query.institution, - reg_token: route.query.reg_token + institution: route.query.institution }) }, { path: '/institutions', name: 'Institutions', component: Insts, diff --git a/user_mgmt/static/routes/insts.js b/user_mgmt/static/routes/insts.js index 0cc449a..497ae2c 100644 --- a/user_mgmt/static/routes/insts.js +++ b/user_mgmt/static/routes/insts.js @@ -73,22 +73,6 @@ Vue.component('inst', { default: [], watch: ['refresh'] }, - reg_token: { - get: async function() { - try { - const token = await this.keycloak.get_token(); - const ret = await axios.post('/api/reg_token', {}, { - headers: {'Authorization': 'bearer '+token} - }) - return ret.data.token - } catch (error) { - console.log('error getting registration token', error) - this.error = "Error getting registration token: "+error['message'] - return '' - } - }, - default: '' - }, members: { get: async function() { try { @@ -302,12 +286,12 @@ Vue.component('inst', {

Register new user:

-
-
New user page: Register
+
+
New user page: Register
-
+
+{{ window.location.protocol + '//' + window.location.host + $router.resolve({name: 'register', query: {experiment: experiment, institution: institution} }).href }}
diff --git a/user_mgmt/static/routes/register.js b/user_mgmt/static/routes/register.js index c5ff838..5751047 100644 --- a/user_mgmt/static/routes/register.js +++ b/user_mgmt/static/routes/register.js @@ -7,7 +7,6 @@ export default { return { experiment: '', institution: '', - reg_token: '', firstName: '', debouncedFirstName: '', lastName: '', @@ -21,10 +20,7 @@ export default { submitted: false } }, - props: ['experiment', 'institution', 'reg_token'], - created: function() { - this.validate_token() - }, + props: ['experiment', 'institution'], computed: { validFirstName: function() { return this.debouncedFirstName @@ -75,9 +71,7 @@ export default { if (orig_username != '') { args.username = orig_username } - const resp = await axios.post('/api/username', args, { - headers: {'Authorization': 'bearer '+this.reg_token} - }); + const resp = await axios.post('/api/username', args, {}); if (orig_username != resp.data['username'] && this.username == orig_username) { this.username = resp.data['username'] } @@ -119,61 +113,49 @@ export default { }, 250) }, methods: { - validate_token: async function() { - try { - await axios.get('/api/reg_token/'+this.reg_token); - } catch(error) { - console.log('invalid reg_token') - this.$router.push({name: 'home'}) - return false - } - return true - }, - submit: async function(e) { - // wait for debounce - await sleep(250) - - // validate - this.valid = (this.validExperiment && this.validInstitution && this.validFirstName - && this.validLastName && this.validUsername && this.validEmail) + submit: async function(e) { + // wait for debounce + await sleep(250) - // now submit - if (this.valid) { - this.errMessage = 'Submission processing'; - try { - const resp = await axios.post('/api/inst_approvals', { - experiment: this.experiment, - institution: this.institution, - first_name: this.firstName, - last_name: this.lastName, - username: this.username, - email: this.email - }, { - headers: {'Authorization': 'bearer '+this.reg_token} - }); - console.log('Response:') - console.log(resp) - this.errMessage = 'Submission successful' - this.submitted = true - } catch (error) { - console.log('error') - console.log(error) - let error_message = 'undefined error'; - if (error.response) { - if ('code' in error.response.data) { - error_message = 'Code: '+error.response.data['code']+'
Message: '+error.response.data['error']; - } else { - error_message = JSON.stringify(error.response.data) - } - } else if (error.request) { - error_message = 'server did not respond'; - } - this.errMessage = 'Error in submission
'+error_message+'
' - } - } else { - this.errMessage = 'Please fix invalid entries' + // validate + this.valid = (this.validExperiment && this.validInstitution && this.validFirstName + && this.validLastName && this.validUsername && this.validEmail) + + // now submit + if (this.valid) { + this.errMessage = 'Submission processing'; + try { + const resp = await axios.post('/api/inst_approvals', { + experiment: this.experiment, + institution: this.institution, + first_name: this.firstName, + last_name: this.lastName, + username: this.username, + email: this.email + }, {}); + console.log('Response:') + console.log(resp) + this.errMessage = 'Submission successful' + this.submitted = true + } catch (error) { + console.log('error') + console.log(error) + let error_message = 'undefined error'; + if (error.response) { + if ('code' in error.response.data) { + error_message = 'Code: '+error.response.data['code']+'
Message: '+error.response.data['error']; + } else { + error_message = JSON.stringify(error.response.data) + } + } else if (error.request) { + error_message = 'server did not respond'; } + this.errMessage = 'Error in submission
'+error_message+'
' + } + } else { + this.errMessage = 'Please fix invalid entries' } + } }, template: `
diff --git a/user_mgmt/users.py b/user_mgmt/users.py index 59eb986..8d50df6 100644 --- a/user_mgmt/users.py +++ b/user_mgmt/users.py @@ -13,7 +13,6 @@ import krs.groups from .handler import MyHandler -from .registration import authenticate_reg_token #: user mgmt name : keycloak name @@ -87,7 +86,6 @@ async def _username_in_use(self, username): return False # username is available return True - @authenticate_reg_token @catch_error async def post(self): """