Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

task/DES-2702: Tapis v3 Auth #1174

Merged
merged 20 commits into from
Mar 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 0 additions & 8 deletions .docs/source/designsafe.apps.auth.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,6 @@ designsafe.apps.auth.backends module
:undoc-members:
:show-inheritance:

designsafe.apps.auth.context_processors module
----------------------------------------------

.. automodule:: designsafe.apps.auth.context_processors
:members:
:undoc-members:
:show-inheritance:

designsafe.apps.auth.middleware module
--------------------------------------

Expand Down
5 changes: 4 additions & 1 deletion .flake8
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
[flake8]
# E501: line is too long.
# H101: Use TODO(NAME)
ignore = E501, H101
# W503: line break before binary operator. Ingore as black will break this rule.
ignore = E501, H101, W503
exclude = __pycache__,
tests.py,
migrations

extend-ignore = W503
7 changes: 5 additions & 2 deletions .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ ignore=CVS,tests.py
# ignore-list. The regex matches against paths and can be in Posix or Windows
# format. Because '\\' represents the directory delimiter on Windows systems,
# it can't be used as an escape character.
ignore-paths=^.*migrations/.*$,^.*_tests/.*$
ignore-paths=^.*migrations/.*$,^.*_tests/.*$,^.*unit_test.*$

# Files or directories matching the regular expression patterns are skipped.
# The regex matches against base names, not paths. The default value ignores
Expand Down Expand Up @@ -430,7 +430,10 @@ disable=raw-checker-failed,
useless-suppression,
deprecated-pragma,
use-symbolic-message-instead,
duplicate-code
line-too-long,
duplicate-code,
logging-fstring-interpolation


# Enable the message, report, category or checker with the given id(s). You can
# either give multiple identifier separated by comma (,) or put this option
Expand Down
7 changes: 2 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -225,8 +225,8 @@ $ docker-compose -f conf/docker/docker-compose-dev.all.debug.yml up
$ npm run dev
```

When using this compose file, your Agave Client should be configured with a `callback_url`
of `http://$DOCKER_HOST_IP:8000/auth/agave/callback/`.
When using this compose file, your Tapis Client should be configured with a `callback_url`
of `http://$DOCKER_HOST_IP:8000/auth/tapis/callback/`.

For developing some services, e.g. Box.com integration, https support is required. To
enable an Nginx http proxy run using the [`docker-compose-http.yml`](docker-compose-http.yml)
Expand All @@ -238,9 +238,6 @@ $ docker-compose -f docker-compose-http.yml build
$ docker-compose -f docker-compose-http.yml up
```

When using this compose file, your Agave Client should be configured with a `callback_url`
of `https://$DOCKER_HOST_IP/auth/agave/callback/`.

### Agave filesystem setup
1. Delete all of the old metadata objects using this command:

Expand Down
30 changes: 26 additions & 4 deletions conf/env_files/designsafe.sample.env
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,13 @@ OPBEAT_SECRET_TOKEN=
#
# To configure Agave Authentication for DesignSafe-CI Portal, you need to
# generate a set of API client keys. The client MUST be configured with a `callbackUrl`
# which should be the URL path to the `designsafe.apps.auth.views.agave_oauth_callback`
# view. In production, this would be `https://www.designsafe-ci.org/auth/agave/callback/`.
# which should be the URL path to the `designsafe.apps.auth.views.tapis_oauth_callback`
# view. In production, this would be `https://www.designsafe-ci.org/auth/tapis/callback/`.
# If you are following the "First time setup" from the README, this would be
# `http://$DOCKER_HOST_IP:8000/auth/agave/callback/`, where `$DOCKER_HOST_IP` is either
# `http://$DOCKER_HOST_IP:8000/auth/tapis/callback/`, where `$DOCKER_HOST_IP` is either
# `localhost` or the result of the `docker-machine ip machine-name` command.
#
# See https://agave.readthedocs.io/en/latest/agave/guides/clients/introduction.html
# See https://tapis.readthedocs.io/en/latest/technical/authentication.html#creating-clients
# for more information
#
AGAVE_TENANT_ID=designsafe
Expand All @@ -81,6 +81,28 @@ AGAVE_JWT_HEADER=
AGAVE_JWT_USER_CLAIM_FIELD=
AGAVE_JWT_SERVICE_ACCOUNT=

########################
# TAPIS v3 SETTINGS
# NOTE: ONLY USED FOR TAPIS V3 DEVELOPMENT.
# YOU CAN IGNORE THIS FOR TAPIS V2 DEVELOPMENT.
########################

# Admin account
PORTAL_ADMIN_USERNAME=

# Tapis Tenant.
TAPIS_TENANT_BASEURL=

# Tapis Client Configuration
TAPIS_CLIENT_ID=
TAPIS_CLIENT_KEY=

# Long-live portal admin access token
TAPIS_ADMIN_JWT=

# Key service token for registering public keys with cloud.corral
KEY_SERVICE_TOKEN=

###
# Box.com Integration
#
Expand Down
117 changes: 23 additions & 94 deletions designsafe/LoginTest.py
Original file line number Diff line number Diff line change
@@ -1,98 +1,27 @@
#python manage.py test designsafe.LoginTest --settings=designsafe.settings.test_settings
from django.test import TestCase, RequestFactory
from django.contrib.auth import get_user_model
from django.urls import reverse
# python manage.py test designsafe.LoginTest --settings=designsafe.settings.test_settings
import mock
from designsafe.apps.auth.models import AgaveOAuthToken
from designsafe.apps.auth.tasks import check_or_create_agave_home_dir
from designsafe.apps.auth.views import agave_oauth_callback
from django.test import TestCase
from designsafe.apps.auth.views import tapis_oauth_callback


class LoginTestClass(TestCase):
def setUp (self):
User = get_user_model()

user_with_agave = User.objects.create_user('test_with_agave', '[email protected]', 'test')
token = AgaveOAuthToken(
token_type="bearer",
scope="default",
access_token="1234abcd",
refresh_token="123123123",
expires_in=14400,
created=1523633447)
token.user = user_with_agave
token.save()
self.rf = RequestFactory()

user_without_agave = User.objects.create_user('test_without_agave', '[email protected]', 'test')
token = AgaveOAuthToken(
token_type="bearer",
scope="default",
access_token="5555abcd",
refresh_token="5555555",
expires_in=14400,
created=1523633447)
token.user = user_without_agave
token.save()


def tearDown(self):
return

""" @mock.patch('designsafe.apps.auth.models.AgaveOAuthToken.client')
@mock.patch('agavepy.agave.Agave')
def test_has_agave_file_listing(self, agave_client, agave):
#agaveclient.return_value.files.list.return_value = [] // whatever the listing should look like
#request.post.return_value = {} // some object that looks like a requests response

self.client.login(username='test_with_agave', password='test')

agave_client.files.list.return_value = {
"name": "test",
"system": "designsafe.storage.default",
"trail": [{"path": "/", "name": "/", "system": "designsafe.storage.default"},
{"path": "/test", "name": "test", "system": "designsafe.storage.default"}],
"path": "test",
"type": "dir",
"children": [],
"permissions": "READ"
}

resp = self.client.get('/api/agave/files/listing/agave/designsafe.storage.default/test', follow=True)

self.assertEqual(resp.status_code, 200)
self.assertJSONEqual(resp.content, agave_client.files.list.return_value, msg='Agave homedir listing has unexpected values') """

@mock.patch('designsafe.apps.auth.models.AgaveOAuthToken.client')
@mock.patch('agavepy.agave.Agave')
def test_no_agave_file_listing(self, agave_client, agave):
self.client.login(username='test_without_agave', password='test')
session = self.client.session
session['auth_state'] = 'test'
session.save()

request_without_agave = self.client.post("/auth/agave/callback/?state=test&code=test", follow = True)
print('Initial Query Status: ' + str(request_without_agave.status_code))
""" request_without_agave.get.return_value = {
'state': 'test',
'session': {'auth_state': 'test'},
'code': 'test'
} """

""" agave_client.files.list.return_value = {
"status": "error",
"message": "File/folder does not exist",
"version": "test"
} """

resp = agave_oauth_callback(request_without_agave)
print('Oauth Callback Status: ' + str(resp))
self.assertEqual(resp.status_code, 200)

""" def test_user_without_agave_homedir_gets_redirected(self, mock_client, mock_Agave):

pass """

""" def test_agave_callbak(self):

resp = self.client.post("/auth/agave/callback?code=code&state=test", data=data) """
def setUp(self):
pass

def tearDown(self):
pass

@mock.patch("designsafe.apps.auth.models.TapisOAuthToken.client")
@mock.patch("tapipy.tapis.Tapis")
def test_no_tapis_file_listing(self, tapis_client, tapis):
self.client.login(username="test_without_tapis", password="test")
session = self.client.session
session["auth_state"] = "test"
session.save()

request_without_tapis = self.client.post(
"/auth/tapis/callback/?state=test&code=test", follow=True
)
resp = tapis_oauth_callback(request_without_tapis)
print("Oauth Callback Status: " + str(resp))
self.assertEqual(resp.status_code, 200)
64 changes: 0 additions & 64 deletions designsafe/apps/accounts/fixtures/user-data.json

This file was deleted.

16 changes: 8 additions & 8 deletions designsafe/apps/accounts/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import io
import logging
from django.conf import settings
from agavepy.agave import Agave, AgaveException
# from agavepy.agave import Agave, AgaveException
from celery import shared_task
from requests import HTTPError
from django.contrib.auth import get_user_model
Expand All @@ -17,8 +17,8 @@
@shared_task(default_retry_delay=1*30, max_retries=3)
def create_report(username, list_name):
"""
This task runs a celery task that creates a report of all DesignSafe users.
It pulls data from both TAS and the Django user model, writes them to a CSV, and
This task runs a celery task that creates a report of all DesignSafe users.
It pulls data from both TAS and the Django user model, writes them to a CSV, and
imports the CSV to the top-level of the user's My Data directory.
"""

Expand Down Expand Up @@ -47,20 +47,20 @@ def create_report(username, list_name):
try:
user_profile = TASUser(username=user)
designsafe_user = get_user_model().objects.get(username=user)

if hasattr(designsafe_user, "profile"):

#making nh_interests QuerySet into list
interests = designsafe_user.profile.nh_interests.all().values('description')
nh_interests = [interest['description'] for interest in interests]

#making research_activities QuerySet into list
activities = designsafe_user.profile.research_activities.all().values('description')
research_activities = [activity['description'] for activity in activities]

# order of items as required by user
writer.writerow([user_profile.lastName if user_profile.lastName else user_profile.lastName,
user_profile.firstName if user_profile.firstName else user_profile.firstName,
user_profile.firstName if user_profile.firstName else user_profile.firstName,
user_profile.email,
user_profile.phone,
user_profile.institution,
Expand Down Expand Up @@ -92,10 +92,10 @@ def create_report(username, list_name):
systemId=settings.AGAVE_STORAGE_SYSTEM,
fileToUpload=csv_file
)

csv_file.close()

except (HTTPError, AgaveException):
logger.exception('Failed to create user report.',
extra={'user': username,
'systemId': settings.AGAVE_STORAGE_SYSTEM})
'systemId': settings.AGAVE_STORAGE_SYSTEM})
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ <h1 class="headline headline-research" style="margin-bottom:40px;">Register an A
If you already have a TACC account, log in with your TACC username and password to access DesignSafe.
</p>
<p class="text-center">
<a href="{% url 'designsafe_auth:agave_oauth' %}"><button class="btn btn-default"
<a href="{% url 'designsafe_auth:tapis_oauth' %}"><button class="btn btn-default"
style="width:100%; font-weight: bold;">Log
in</button></a>
</p>
Expand Down Expand Up @@ -61,4 +61,4 @@ <h1 class="headline headline-research" style="margin-bottom:40px;">Register an A
</div>
{% endblock %}

{% block footer %}{% include 'includes/footer.html' %}{% endblock footer %}
{% block footer %}{% include 'includes/footer.html' %}{% endblock footer %}
2 changes: 1 addition & 1 deletion designsafe/apps/accounts/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def test_mailing_list_access(self):
self.client.login(username='ds_user', password='user/password')
resp = self.client.get(url)
self.assertEqual(resp.status_code, 403)
self.client.logout()

user = get_user_model().objects.get(pk=2)
perm = Permission.objects.get(codename='view_notification_subscribers')
user.user_permissions.add(perm)
Expand Down
Loading
Loading