Skip to content

Commit

Permalink
Merge pull request #100 from rockychen-dpaw/master
Browse files Browse the repository at this point in the history
Add health check & fix session timeout issue
  • Loading branch information
rockychen-dpaw authored Oct 29, 2024
2 parents 4819b24 + 362c0f1 commit 58dcd82
Show file tree
Hide file tree
Showing 35 changed files with 1,286 additions and 494 deletions.
8 changes: 4 additions & 4 deletions authome/admin/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ class NormalUser(models.User):
class Meta:
proxy = True
verbose_name="User"
verbose_name_plural="{}Users".format(" " * 11)
verbose_name_plural="{}Users".format(" " * 16)

class DeleteMixin(object):
def delete_model(self, request, obj):
Expand Down Expand Up @@ -314,7 +314,7 @@ class SystemUser(models.User):
class Meta:
proxy = True
verbose_name="System User"
verbose_name_plural="{}System Users".format(" " * 9)
verbose_name_plural="{}System Users".format(" " * 14)


class SystemUserAdmin(PermissionCheckMixin,DbcaAccountMixin,UserGroupsMixin,DatetimeMixin,CatchModelExceptionMixin,auth.admin.UserAdmin):
Expand Down Expand Up @@ -462,14 +462,14 @@ class SystemUserToken(models.User):
class Meta:
proxy = True
verbose_name="System User"
verbose_name_plural="{}System User Tokens".format(" " * 8)
verbose_name_plural="{}System User Tokens".format(" " * 13)

class NormalUserToken(models.User):
objects = models.NormalUserManager()
class Meta:
proxy = True
verbose_name="System User"
verbose_name_plural="{}User Tokens".format(" " * 10)
verbose_name_plural="{}User Tokens".format(" " * 15)


class AccessTokenAdmin(DatetimeMixin,CatchModelExceptionMixin,auth.admin.UserAdmin):
Expand Down
32 changes: 31 additions & 1 deletion authome/admin/adminsite.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from .. import models as auth2_models
from .. import utils
from .. import signals
from ..cache import cache

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -109,12 +110,41 @@ def _build_app_dict(self, request, label=None):
app_label = auth2_models.UserGroup._meta.app_label
if app_label in app_dict:
app_dict[app_label]["models"].append({
'name': "Renew Apple Secret Key",
'name': "{}Renew Apple Secret Key".format(" " * 3),
'object_name': "Renew Apple Secret Key",
'perms': [],
'admin_url': reverse("admin:renew_apple_secretkey"),
'add_url': None,
})
if settings.AUTH2_MONITORING_DIR:
if auth2_models.can_access(request.user.email,settings.AUTH2_DOMAIN,'/admin/monitor/'):
app_label = auth2_models.UserGroup._meta.app_label
if app_label in app_dict:
if settings.AUTH2_CLUSTER_ENABLED:
app_dict[app_label]["models"].append({
'name': "{1}Healthcheck({0})".format(settings.AUTH2_CLUSTERID," " * 2),
'object_name': "{}_Healthcheck".format(settings.AUTH2_CLUSTERID),
'perms': [],
'admin_url': reverse("admin:auth2_status",kwargs={"clusterid":settings.AUTH2_CLUSTERID}),
'add_url': None,
})
for cluster in cache.auth2_clusters.values():
app_dict[app_label]["models"].append({
'name': "{1}Healthcheck({0})".format(cluster.clusterid," " * 2),
'object_name': "{}_Healthcheck".format(cluster.clusterid),
'perms': [],
'admin_url': reverse("admin:auth2_status",kwargs={"clusterid":cluster.clusterid}),
'add_url': None,
})

else:
app_dict[app_label]["models"].append({
'name': "{}Healthcheck".format(" " * 2),
'object_name': "Healthcheck",
'perms': [],
'admin_url': reverse("admin:auth2_status"),
'add_url': None,
})


return app_dict
Expand Down
16 changes: 12 additions & 4 deletions authome/admin/clusteradmin.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,10 +177,7 @@ class SystemUserAccessTokenAdmin(SyncObjectChangeMixin,admin.SystemUserAccessTok
def _sync_change(self,objids):
return cache.usertokens_changed(objids,True)

class Auth2ClusterAdmin(admin.ExtraToolsMixin,admin.DeleteMixin,admin.DatetimeMixin,admin.CatchModelExceptionMixin,djangoadmin.ModelAdmin):
list_display = ('clusterid','_running_status','default','endpoint','_last_heartbeat','_usergroup_status','_usergroupauthorization_status','_userflow_status','_idp_status')
readonly_fields = ('clusterid','_running_status','default','endpoint','_last_heartbeat','_usergroup_status','_usergroup_lastrefreshed','_usergroupauthorization_status','_usergroupauthorization_lastrefreshed','_userflow_status','_userflow_lastrefreshed','_idp_status','_idp_lastrefreshed','modified','registered')
fields = readonly_fields
class BaseAuth2ClusterAdmin(admin.ExtraToolsMixin,admin.DeleteMixin,admin.DatetimeMixin,admin.CatchModelExceptionMixin,djangoadmin.ModelAdmin):
ordering = ('clusterid',)
extra_tools = [("cluster status",'cluster_status',"clusterstatus")]

Expand Down Expand Up @@ -271,3 +268,14 @@ def has_add_permission(self, request, obj=None):

def has_delete_permission(self, request, obj=None):
return True

if settings.AUTH2_MONITORING_DIR:
class Auth2ClusterAdmin(BaseAuth2ClusterAdmin):
list_display = ('clusterid','_running_status','default','endpoint','_usergroup_status','_usergroupauthorization_status','_userflow_status','_idp_status')
readonly_fields = ('clusterid','_running_status','default','endpoint','_usergroup_status','_usergroup_lastrefreshed','_usergroupauthorization_status','_usergroupauthorization_lastrefreshed','_userflow_status','_userflow_lastrefreshed','_idp_status','_idp_lastrefreshed','modified','registered')
fields = readonly_fields
else:
class Auth2ClusterAdmin(BaseAuth2ClusterAdmin):
list_display = ('clusterid','_running_status','default','endpoint','_last_heartbeat','_usergroup_status','_usergroupauthorization_status','_userflow_status','_idp_status')
readonly_fields = ('clusterid','_running_status','default','endpoint','_last_heartbeat','_usergroup_status','_usergroup_lastrefreshed','_usergroupauthorization_status','_usergroupauthorization_lastrefreshed','_userflow_status','_userflow_lastrefreshed','_idp_status','_idp_lastrefreshed','modified','registered')
fields = readonly_fields
2 changes: 1 addition & 1 deletion authome/backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ def get_logout_url(cls):
def logout_url(self):
return self.LOGOUT_URL.format(base_url=self.base_url)

error_re = re.compile("^\s*(?P<code>[A-Z0-9]+)\s*:")
error_re = re.compile("^\\s*(?P<code>[A-Z0-9]+)\\s*:")
def process_error(self, data):
try:
super().process_error(data)
Expand Down
180 changes: 126 additions & 54 deletions authome/basetest.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,76 +28,86 @@ def get(self,path, authorization=None,domain=None,url=None):

return super().get(path,headers=headers)

class BaseAuthTestCase(TestCase):
client_class = Auth2Client
home_url = reverse('home')
auth_url = reverse('auth')
auth_optional_url = reverse('auth_optional')
auth_basic_url = reverse('auth_basic')
auth_basic_optional_url = reverse('auth_basic_optional')
test_usergroups = None
class BaseTestCase(TestCase):
test_usergroupauthorization = None
test_userauthorization = None
test_users = None


def create_client(self):
return Auth2Client()

def setUp(self):
test_usergroups = None
@classmethod
def setUpClass(cls):
super(BaseTestCase,cls).setUpClass()
print("*********************************************************")
if settings.RELEASE:
print("Running in release mode")
print("Running unitest({}) in release mode".format(cls.__name__))
else:
print("Running in dev mode")
settings.AUTH_CACHE_SIZE=2000
settings.BASIC_AUTH_CACHE_SIZE=1000
settings.AUTH_BASIC_CACHE_EXPIRETIME=timedelta(seconds=3600)
settings.AUTH_CACHE_EXPIRETIME=timedelta(seconds=3600)
settings.STAFF_AUTH_CACHE_EXPIRETIME=settings.AUTH_CACHE_EXPIRETIME
settings.AUTH_CACHE_CLEAN_HOURS = [0]
settings.AUTHORIZATION_CACHE_CHECK_HOURS = [0,12]
print("Running unitest({}) in dev mode".format(cls.__name__))

settings.CHECK_AUTH_BASIC_PER_REQUEST = False
User.objects.filter(email__endswith="@gunfire.com").delete()
User.objects.filter(email__endswith="@gunfire.com.au").delete()
User.objects.filter(email__endswith="@hacker.com").delete()
UserToken.objects.all().delete()
UserGroup.objects.all().exclude(users=["*"],excluded_users__isnull=True).delete()
UserAuthorization.objects.all().delete()
def delete_testdata(self):
#delete user group authorization
if self.test_usergroupauthorization:
for groupname,domain,paths,excluded_paths in self.test_usergroupauthorization :
UserGroupAuthorization.objects.filter(usergroup=UserGroup.objects.get(name=groupname),domain=domain,paths=paths,excluded_paths=excluded_paths).delete()

#delete user authorization
if self.test_userauthorization:
for user,domain,paths,excluded_paths in self.test_userauthorization:
UserAuthorization.objects.filter(user=user,domain=domain,paths=paths,excluded_paths=excluded_paths).delete()

cache.refresh_authorization_cache(True)
if not UserGroup.objects.filter(users=["*"], excluded_users__isnull=True).exists():
public_group = UserGroup(name="Public User",groupid="PUBLIC",users=["*"])
public_group.clean()
public_group.save()
if not CustomizableUserflow.objects.filter(domain="*").exists():
default_flow = CustomizableUserflow(
domain='*',
default='default',
mfa_set="default_mfa_set",
mfa_reset="default_mfa_reset",
password_reset="default_password_reset",
verifyemail_from="[email protected]",
verifyemail_subject="test"
)
default_flow.clean()
default_flow.save()
#delete users
if self.test_users:
for user in self.test_users.values():
user.delete()

cache.clean_auth_cache(True)
cache.refresh_authorization_cache(True)

#delete UserGroup objects
if self.test_usergroups:
del_usergroups = []
created_usergroups = [(UserGroup.public_group(),self.test_usergroups)]
while created_usergroups:
parent_obj,subgroup_datas = created_usergroups.pop()
for subgroup in subgroup_datas:
if len(subgroup) == 4:
name,users,excluded_users,subgroups = subgroup
session_timeout = None
else:
name,users,excluded_users,session_timeout,subgroups = subgroup

obj = UserGroup.objects.filter(name=name,parent_group=parent_obj).first()
if obj:
del_usergroups.insert(0,obj)
if subgroups:
created_usergroups.append((obj,subgroups))

def basic_auth(self, username, password):
return 'Basic {}'.format(base64.b64encode('{}:{}'.format(username, password).encode('utf-8')).decode('utf-8'))
for usergroup in del_usergroups:
usergroup.delete()

def populate_testdata(self):
#popuate UserGroup objects
if self.test_usergroups:
uncreated_usergroups = [(UserGroup.public_group(),self.test_usergroups)]
while uncreated_usergroups:
parent_obj,subgroup_datas = uncreated_usergroups.pop()
for name,users,excluded_users,subgroups in subgroup_datas:
obj = UserGroup(name=name,groupid=name,users=users,excluded_users=excluded_users,parent_group=parent_obj)
for subgroup in subgroup_datas:
if len(subgroup) == 4:
name,users,excluded_users,subgroups = subgroup
session_timeout = None
else:
name,users,excluded_users,session_timeout,subgroups = subgroup

if users and isinstance(users,str):
users = [users]
if excluded_users and isinstance(excluded_users,str):
excluded_users = [excluded_users]

obj = UserGroup.objects.filter(name=name).first()
if obj:
obj.groupid=name
obj.users=users
obj.excluded_users=excluded_users
obj.parent_group=parent_obj
obj.session_timeout=session_timeout
else:
obj = UserGroup(name=name,groupid=name,users=users,excluded_users=excluded_users,parent_group=parent_obj,session_timeout=session_timeout)
obj.clean()
print("save usergroup={}".format(obj))
obj.save()
Expand All @@ -107,7 +117,11 @@ def populate_testdata(self):
users = OrderedDict()
if self.test_users:
for test_user in self.test_users:
obj = User(username=test_user[0],email=test_user[1])
obj = User.objects.filter(username=test_user[0]).first()
if obj:
obj.email = test_user[1]
else:
obj = User(username=test_user[0],email=test_user[1])
obj.clean()
obj.save()
users[test_user[0]] = obj
Expand Down Expand Up @@ -135,6 +149,64 @@ def populate_testdata(self):

cache.refresh_authorization_cache(True)

class BaseAuthTestCase(BaseTestCase):
client_class = Auth2Client
home_url = reverse('home')
auth_url = reverse('auth')
auth_optional_url = reverse('auth_optional')
auth_basic_url = reverse('auth_basic')
auth_basic_optional_url = reverse('auth_basic_optional')


def create_client(self):
return Auth2Client()

def setUp(self):
settings.AUTH_CACHE_SIZE=2000
settings.BASIC_AUTH_CACHE_SIZE=1000
settings.AUTH_BASIC_CACHE_EXPIRETIME=timedelta(seconds=3600)
settings.AUTH_CACHE_EXPIRETIME=timedelta(seconds=3600)
settings.STAFF_AUTH_CACHE_EXPIRETIME=settings.AUTH_CACHE_EXPIRETIME
settings.AUTH_CACHE_CLEAN_HOURS = [0]
settings.AUTHORIZATION_CACHE_CHECK_HOURS = [0,12]

settings.CHECK_AUTH_BASIC_PER_REQUEST = False
User.objects.filter(email__endswith="@gunfire.com").delete()
User.objects.filter(email__endswith="@gunfire.com.au").delete()
User.objects.filter(email__endswith="@hacker.com").delete()
UserToken.objects.all().delete()
UserGroup.objects.all().exclude(users=["*"],excluded_users__isnull=True).delete()
UserAuthorization.objects.all().delete()

cache.refresh_authorization_cache(True)
public_group = UserGroup.objects.filter(users=["*"], excluded_users__isnull=True).first()
if public_group:
public_group.session_timeout = 900
public_group.save()
else:
public_group = UserGroup(name="Public User",groupid="PUBLIC",users=["*"],session_timeout=900)
public_group.clean()
public_group.save()
if not CustomizableUserflow.objects.filter(domain="*").exists():
default_flow = CustomizableUserflow(
domain='*',
default='default',
mfa_set="default_mfa_set",
mfa_reset="default_mfa_reset",
password_reset="default_password_reset",
verifyemail_from="[email protected]",
verifyemail_subject="test"
)
default_flow.clean()
default_flow.save()

cache.clean_auth_cache(True)
cache.refresh_authorization_cache(True)


def basic_auth(self, username, password):
return 'Basic {}'.format(base64.b64encode('{}:{}'.format(username, password).encode('utf-8')).decode('utf-8'))

class BaseAuthCacheTestCase(BaseAuthTestCase):
def setUp(self):
super().setUp()
Expand Down
26 changes: 26 additions & 0 deletions authome/cache/clustercache.py
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,32 @@ def _send_request(cluster):
except Exception as ex:
return (False,str(ex))

def get_auth2_status(self,clusterid):
"""
get the status of the cluster server
Return server status
"""
def _send_request(cluster):
return requests.get("{}{}".format(
cluster.endpoint,
reverse('cluster:auth2_status',kwargs={"clusterid":cluster.clusterid})
),headers=self._get_headers(),timeout=settings.AUTH2_INTERCONNECTION_TIMEOUT,verify=settings.SSL_VERIFY)
res = self._send_request_to_cluster(None,clusterid,_send_request)
return res.text

def get_auth2_liveness(self,clusterid,serviceid,monitordate):
"""
get the status of the cluster server
Return server status
"""
def _send_request(cluster):
return requests.get("{}{}".format(
cluster.endpoint,
reverse('cluster:auth2_liveness',kwargs={"clusterid":cluster.clusterid,"serviceid":serviceid,"monitordate":monitordate})
),headers=self._get_headers(),timeout=settings.AUTH2_INTERCONNECTION_TIMEOUT,verify=settings.SSL_VERIFY)
res = self._send_request_to_cluster(None,clusterid,_send_request)
return res.text

@property
def status(self):
result = super().status
Expand Down
2 changes: 1 addition & 1 deletion authome/models/clustermodels.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class Auth2Cluster(models.Model):
modified = models.DateTimeField(editable=False,auto_now=False)

class Meta:
verbose_name_plural = "{}Auth2 Clusters".format(" " * 2)
verbose_name_plural = "{}Auth2 Clusters".format(" " * 7)

@classmethod
def register(cls,only_update_heartbeat=False):
Expand Down
2 changes: 1 addition & 1 deletion authome/models/debugmodels.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ class DebugLog(models.Model):
message = models.TextField(editable=False,null=True)

class Meta:
verbose_name_plural = "{}Auth2 Logs".format(" " * 0)
verbose_name_plural = "{}Auth2 Logs".format(" " * 4)

@classmethod
def attach_request(cls,request):
Expand Down
Loading

0 comments on commit 58dcd82

Please sign in to comment.