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

Feature/553 api endpoint list groups #574

5 changes: 3 additions & 2 deletions docs/source/user/management_commands.rst
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,12 @@ This command deactivates expired user accounts which were created temporarily
--------------------------------

This command deletes users that have expired (and should have been deactivated by
``deactivate_expired_users``) for more than the specified ``<duration_in_months>``.
``deactivate_expired_users``) for more than the duration specified by ``--older-than-days <duration_in_days>``
or ``--older-than-months `<duration_in_months>``.

.. code-block:: shell

./manage.py delete_old_radiusbatch_users --older-than-months <duration_in_months>
./manage.py delete_old_radiusbatch_users --older-than-days <duration_in_days>

Note that the default duration is set to 18 months.

Expand Down
21 changes: 21 additions & 0 deletions openwisp_radius/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
RadiusBatch = load_model('RadiusBatch')
RadiusToken = load_model('RadiusToken')
RadiusGroupCheck = load_model('RadiusGroupCheck')
RadiusGroup = load_model('RadiusGroup')
RadiusUserGroup = load_model('RadiusUserGroup')
RegisteredUser = load_model('RegisteredUser')
OrganizationUser = swapper.load_model('openwisp_users', 'OrganizationUser')
Expand Down Expand Up @@ -711,3 +712,23 @@ class Meta:
'password_expired',
'radius_user_token',
]


class RadiusGroupSerializer(serializers.ModelSerializer):
"""
Used to list all the groups available
"""

organization = serializers.CharField(source='organization.slug', read_only=True)

class Meta:
model = RadiusGroup
fields = [
'name',
'description',
'default',
'organization',
'created',
'modified',
]
ref_name = 'radius_group_serializer'
5 changes: 5 additions & 0 deletions openwisp_radius/api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,11 @@ def get_api_urls(api_views=None):
api_views.download_rad_batch_pdf,
name='download_rad_batch_pdf',
),
path(
'radius/radiusgroup/',
api_views.list_radius_group,
name='list_radius_groups',
),
]
else:
return []
20 changes: 20 additions & 0 deletions openwisp_radius/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
ChangePhoneNumberSerializer,
RadiusAccountingSerializer,
RadiusBatchSerializer,
RadiusGroupSerializer,
UserRadiusUsageSerializer,
ValidatePhoneTokenSerializer,
)
Expand All @@ -81,6 +82,7 @@
RadiusAccounting = load_model('RadiusAccounting')
RadiusToken = load_model('RadiusToken')
RadiusBatch = load_model('RadiusBatch')
RadiusGroup = load_model('RadiusGroup')
RadiusUserGroup = load_model('RadiusUserGroup')
RadiusGroupCheck = load_model('RadiusGroupCheck')
auth_backend = UsersAuthenticationBackend()
Expand Down Expand Up @@ -801,3 +803,21 @@ def create_phone_token(self, *args, **kwargs):


change_phone_number = ChangePhoneNumberView.as_view()


@method_decorator(
name='get',
decorator=swagger_auto_schema(
operation_description="""
**Requires the user auth token (Bearer Token).**
Returns the list of all RADIUS groups.
"""
),
)
class RadiusGroupListView(ListAPIView):
authentication_classes = (BearerAuthentication, SessionAuthentication)
queryset = RadiusGroup.objects.all()
serializer_class = RadiusGroupSerializer


list_radius_group = RadiusGroupListView.as_view()
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,32 @@ class BaseDeleteOldRadiusBatchUsersCommand(BaseCommand):
help = 'Deactivating users added in batches which have expired'

def add_arguments(self, parser):
parser.add_argument(
'--older-than-days',
action='store',
type=int,
default=30 * BATCH_DELETE_EXPIRED,
help='Delete users that are older than days provided',
)
parser.add_argument(
'--older-than-months',
action='store',
default=BATCH_DELETE_EXPIRED,
help='delete users which have expired before this time',
type=int,
help='Delete users that are older than months provided',
)

def handle(self, *args, **options):
months = now() - timedelta(days=30 * int(options['older_than_months']))
batches = RadiusBatch.objects.filter(expiration_date__lt=months)
days = options.get('older_than_days')
months = options.get('older_than_months')

if months is not None:
threshold_date = now() - timedelta(days=30 * months)
else:
threshold_date = now() - timedelta(days=days)

batches = RadiusBatch.objects.filter(expiration_date__lt=threshold_date)
time_period = threshold_date.strftime('%Y-%m-%d %H:%M:%S')

for b in batches:
b.delete()
self.stdout.write(
f'Deleted accounts older than {options["older_than_months"]} months'
)
self.stdout.write(f'Deleted accounts older than {time_period}')
17 changes: 17 additions & 0 deletions openwisp_radius/saml/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from urllib.parse import parse_qs, urlparse

import swapper
from allauth.account.models import EmailAddress
from django.conf import settings
from django.contrib.auth import logout
from django.core.exceptions import ObjectDoesNotExist, PermissionDenied
Expand Down Expand Up @@ -66,6 +67,22 @@ def post_login_hook(self, request, user, session_info):
try:
user.registered_user
except ObjectDoesNotExist:
email = None
uid_is_email = 'email' in getattr(
settings, 'SAML_ATTRIBUTE_MAPPING', {}
).get('uid', ())
if uid_is_email:
email = session_info['name_id'].text
if email is None:
email = session_info['ava'].get('email', [None])[0]
if email:
user.email = email
user.save()
email_address = EmailAddress.objects.create(
user=user, email=email, verified=True, primary=True
)
email_address.save()

registered_user = RegisteredUser(
user=user, method='saml', is_verified=app_settings.SAML_IS_VERIFIED
)
Expand Down
6 changes: 4 additions & 2 deletions openwisp_radius/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,11 @@ def deactivate_expired_users():


@shared_task
def delete_old_radiusbatch_users(older_than_months=12):
def delete_old_radiusbatch_users(
older_than_days=30 * app_settings.BATCH_DELETE_EXPIRED,
):
management.call_command(
'delete_old_radiusbatch_users', older_than_months=older_than_months
'delete_old_radiusbatch_users', older_than_days=older_than_days
)


Expand Down
3 changes: 3 additions & 0 deletions openwisp_radius/tests/static/test_batch_users.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
rahulyadav,cleartext$password,[email protected],Rahul,Yadav
rahulyadav,pbkdf2_sha256$100000$x3DUBnOFwraV$PU2dZZq1FcuBjagxVLPhhFvpicLn18fFCN5xiLsxATc=,[email protected],,
,,[email protected],,
7 changes: 7 additions & 0 deletions openwisp_radius/tests/test_api/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
User = get_user_model()
RadiusToken = load_model('RadiusToken')
RadiusBatch = load_model('RadiusBatch')
RadiusGroup = load_model('RadiusGroup')
RadiusUserGroup = load_model('RadiusUserGroup')
OrganizationRadiusSettings = load_model('OrganizationRadiusSettings')
Organization = swapper.load_model('openwisp_users', 'Organization')
Expand Down Expand Up @@ -304,6 +305,12 @@ def test_register_duplicate_different_org(self):
self.default_org.radius_settings.sms_verification = False
self.default_org.radius_settings.save()

def test_list_radius_groups(self):
url = reverse('radius:list_radius_groups')
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data), RadiusGroup.objects.all().count())

def test_radius_user_serializer(self):
self._register_user()
try:
Expand Down
15 changes: 13 additions & 2 deletions openwisp_radius/tests/test_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,10 +171,21 @@ def test_delete_old_radiusbatch_users_command(self):
name='test1',
)
self._call_command('batch_add_users', **options)
self.assertEqual(get_user_model().objects.all().count(), 6)
path = self._get_path('static/test_batch_users.csv')
expiration_date = (now() - timedelta(days=10)).strftime('%d-%m-%Y')
options = dict(
organization=self.default_org.slug,
file=path,
expiration=expiration_date,
name='test2',
)
self._call_command('batch_add_users', **options)
self.assertEqual(get_user_model().objects.all().count(), 9)
call_command('delete_old_radiusbatch_users')
self.assertEqual(get_user_model().objects.all().count(), 3)
self.assertEqual(get_user_model().objects.all().count(), 6)
call_command('delete_old_radiusbatch_users', older_than_months=12)
self.assertEqual(get_user_model().objects.all().count(), 3)
call_command('delete_old_radiusbatch_users', older_than_days=9)
self.assertEqual(get_user_model().objects.all().count(), 0)

@capture_stdout()
Expand Down
3 changes: 3 additions & 0 deletions openwisp_radius/tests/test_saml/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from urllib.parse import parse_qs, urlparse

import swapper
from allauth.account.models import EmailAddress
from django.contrib.auth import SESSION_KEY, get_user_model
from django.test import TestCase, override_settings
from django.urls import reverse
Expand Down Expand Up @@ -69,6 +70,8 @@ def _post_successful_auth_assertions(self, query_params, org_slug):
self.assertEqual(User.objects.count(), 1)
user_id = self.client.session[SESSION_KEY]
user = User.objects.get(id=user_id)
email = EmailAddress.objects.filter(user=user)
self.assertEqual(email.count(), 1)
self.assertEqual(user.username, '[email protected]')
self.assertEqual(OrganizationUser.objects.count(), 1)
org_user = OrganizationUser.objects.get(user_id=user_id)
Expand Down
2 changes: 1 addition & 1 deletion openwisp_radius/tests/test_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ def test_deactivate_expired_users(self):
def test_delete_old_radiusbatch_users(self):
self._get_expired_user_from_radius_batch()
self.assertEqual(User.objects.all().count(), 1)
result = tasks.delete_old_radiusbatch_users.delay(1)
result = tasks.delete_old_radiusbatch_users.delay(30)
self.assertTrue(result.successful())
self.assertEqual(User.objects.all().count(), 0)

Expand Down
6 changes: 6 additions & 0 deletions tests/openwisp2/sample_radius/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
PasswordResetConfirmView as BasePasswordResetConfirmView,
)
from openwisp_radius.api.views import PasswordResetView as BasePasswordResetView
from openwisp_radius.api.views import RadiusGroupListView as BaseRadiusGroupListView
from openwisp_radius.api.views import RegisterView as BaseRegisterView
from openwisp_radius.api.views import UserAccountingView as BaseUserAccountingView
from openwisp_radius.api.views import UserRadiusUsageView as BaseUserRadiusUsageView
Expand Down Expand Up @@ -93,6 +94,10 @@ class DownloadRadiusBatchPdfView(BaseDownloadRadiusBatchPdfView):
pass


class RadiusGroupListView(BaseRadiusGroupListView):
pass


authorize = AuthorizeView.as_view()
postauth = PostAuthView.as_view()
accounting = AccountingView.as_view()
Expand All @@ -110,3 +115,4 @@ class DownloadRadiusBatchPdfView(BaseDownloadRadiusBatchPdfView):
validate_phone_token = ValidatePhoneTokenView.as_view()
change_phone_number = ChangePhoneNumberView.as_view()
download_rad_batch_pdf = DownloadRadiusBatchPdfView.as_view()
list_radius_group = RadiusGroupListView.as_view()