-
-
Notifications
You must be signed in to change notification settings - Fork 960
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1371 from pierotofy/quotas
External auth support, task sizes, quotas
- Loading branch information
Showing
42 changed files
with
1,081 additions
and
113 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,3 +10,4 @@ WO_DEBUG=NO | |
WO_DEV=NO | ||
WO_BROKER=redis://broker | ||
WO_DEFAULT_NODES=1 | ||
WO_SETTINGS= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
from django.contrib.auth.models import User | ||
from django.contrib.auth import login | ||
from rest_framework.views import APIView | ||
from rest_framework import exceptions, permissions, parsers | ||
from rest_framework.response import Response | ||
from app.auth.backends import get_user_from_external_auth_response | ||
import requests | ||
from webodm import settings | ||
|
||
class ExternalTokenAuth(APIView): | ||
permission_classes = (permissions.AllowAny,) | ||
parser_classes = (parsers.JSONParser, parsers.FormParser,) | ||
|
||
def post(self, request): | ||
# This should never happen | ||
if settings.EXTERNAL_AUTH_ENDPOINT == '': | ||
return Response({'error': 'EXTERNAL_AUTH_ENDPOINT not set'}) | ||
|
||
token = request.COOKIES.get('external_access_token', '') | ||
if token == '': | ||
return Response({'error': 'external_access_token cookie not set'}) | ||
|
||
try: | ||
r = requests.post(settings.EXTERNAL_AUTH_ENDPOINT, headers={ | ||
'Authorization': "Bearer %s" % token | ||
}) | ||
res = r.json() | ||
if res.get('user_id') is not None: | ||
user = get_user_from_external_auth_response(res) | ||
if user is not None: | ||
login(request, user, backend='django.contrib.auth.backends.ModelBackend') | ||
return Response({'redirect': '/'}) | ||
else: | ||
return Response({'error': 'Invalid credentials'}) | ||
else: | ||
return Response({'error': res.get('message', 'Invalid external server response')}) | ||
except Exception as e: | ||
return Response({'error': str(e)}) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
import requests | ||
from django.contrib.auth.backends import ModelBackend | ||
from django.contrib.auth.models import User | ||
from nodeodm.models import ProcessingNode | ||
from webodm import settings | ||
from guardian.shortcuts import assign_perm | ||
import logging | ||
|
||
logger = logging.getLogger('app.logger') | ||
|
||
def get_user_from_external_auth_response(res): | ||
if 'message' in res or 'error' in res: | ||
return None | ||
|
||
if 'user_id' in res and 'username' in res: | ||
try: | ||
user = User.objects.get(pk=res['user_id']) | ||
except User.DoesNotExist: | ||
user = User(pk=res['user_id'], username=res['username']) | ||
user.save() | ||
|
||
# Update user info | ||
if user.username != res['username']: | ||
user.username = res['username'] | ||
user.save() | ||
|
||
maxQuota = -1 | ||
if 'maxQuota' in res: | ||
maxQuota = res['maxQuota'] | ||
if 'node' in res and 'limits' in res['node'] and 'maxQuota' in res['node']['limits']: | ||
maxQuota = res['node']['limits']['maxQuota'] | ||
|
||
# Update quotas | ||
if user.profile.quota != maxQuota: | ||
user.profile.quota = maxQuota | ||
user.save() | ||
|
||
# Setup/update processing node | ||
if 'node' in res and 'hostname' in res['node'] and 'port' in res['node']: | ||
hostname = res['node']['hostname'] | ||
port = res['node']['port'] | ||
token = res['node'].get('token', '') | ||
|
||
# Only add/update if a token is provided, since we use | ||
# tokens as unique identifiers for hostname/port updates | ||
if token != "": | ||
try: | ||
node = ProcessingNode.objects.get(token=token) | ||
if node.hostname != hostname or node.port != port: | ||
node.hostname = hostname | ||
node.port = port | ||
node.save() | ||
|
||
except ProcessingNode.DoesNotExist: | ||
node = ProcessingNode(hostname=hostname, port=port, token=token) | ||
node.save() | ||
|
||
if not user.has_perm('view_processingnode', node): | ||
assign_perm('view_processingnode', user, node) | ||
|
||
return user | ||
else: | ||
return None | ||
|
||
class ExternalBackend(ModelBackend): | ||
def authenticate(self, request, username=None, password=None): | ||
if settings.EXTERNAL_AUTH_ENDPOINT == "": | ||
return None | ||
|
||
try: | ||
r = requests.post(settings.EXTERNAL_AUTH_ENDPOINT, { | ||
'username': username, | ||
'password': password | ||
}, headers={'Accept': 'application/json'}) | ||
res = r.json() | ||
|
||
return get_user_from_external_auth_response(res) | ||
except: | ||
return None | ||
|
||
def get_user(self, user_id): | ||
if settings.EXTERNAL_AUTH_ENDPOINT == "": | ||
return None | ||
|
||
try: | ||
return User.objects.get(pk=user_id) | ||
except User.DoesNotExist: | ||
return None |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
# Generated by Django 2.2.27 on 2023-08-21 14:50 | ||
import os | ||
from django.db import migrations, models | ||
from webodm import settings | ||
|
||
def task_path(project_id, task_id, *args): | ||
return os.path.join(settings.MEDIA_ROOT, | ||
"project", | ||
str(project_id), | ||
"task", | ||
str(task_id), | ||
*args) | ||
|
||
def update_size(task): | ||
try: | ||
total_bytes = 0 | ||
for dirpath, _, filenames in os.walk(task_path(task.project.id, task.id)): | ||
for f in filenames: | ||
fp = os.path.join(dirpath, f) | ||
if not os.path.islink(fp): | ||
total_bytes += os.path.getsize(fp) | ||
task.size = (total_bytes / 1024 / 1024) | ||
task.save() | ||
print("Updated {} with size {}".format(task, task.size)) | ||
except Exception as e: | ||
print("Cannot update size for task {}: {}".format(task, str(e))) | ||
|
||
|
||
|
||
def update_task_sizes(apps, schema_editor): | ||
Task = apps.get_model('app', 'Task') | ||
|
||
for t in Task.objects.all(): | ||
update_size(t) | ||
|
||
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
('app', '0035_task_orthophoto_bands'), | ||
] | ||
|
||
operations = [ | ||
migrations.AddField( | ||
model_name='task', | ||
name='size', | ||
field=models.FloatField(blank=True, default=0.0, help_text='Size of the task on disk in megabytes', verbose_name='Size'), | ||
), | ||
|
||
migrations.RunPython(update_task_sizes), | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
# Generated by Django 2.2.27 on 2023-08-24 16:35 | ||
|
||
from django.conf import settings | ||
from django.db import migrations, models | ||
import django.db.models.deletion | ||
|
||
|
||
def create_profiles(apps, schema_editor): | ||
User = apps.get_model('auth', 'User') | ||
Profile = apps.get_model('app', 'Profile') | ||
|
||
for u in User.objects.all(): | ||
p = Profile.objects.create(user=u) | ||
p.save() | ||
print("Created user profile for %s" % u.username) | ||
|
||
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
migrations.swappable_dependency(settings.AUTH_USER_MODEL), | ||
('app', '0036_task_size'), | ||
] | ||
|
||
operations = [ | ||
migrations.CreateModel( | ||
name='Profile', | ||
fields=[ | ||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||
('quota', models.FloatField(blank=True, default=-1, help_text='Maximum disk quota in megabytes', verbose_name='Quota')), | ||
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), | ||
], | ||
), | ||
|
||
migrations.RunPython(create_profiles), | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.