diff --git a/server/src/uds/auths/IP/authenticator.py b/server/src/uds/auths/IP/authenticator.py index b18a0c412..d1e155e23 100644 --- a/server/src/uds/auths/IP/authenticator.py +++ b/server/src/uds/auths/IP/authenticator.py @@ -38,11 +38,7 @@ from django.utils.translation import ugettext_noop as _ from uds.core import auths from uds.core.util import net from uds.core.ui import gui -from uds.core.util.request import getRequest - -# Not imported at runtime, just for type checking -if typing.TYPE_CHECKING: - from django.http import HttpRequest # pylint: disable=ungrouped-imports +from uds.core.util.request import getRequest, ExtendedHttpRequest logger = logging.getLogger(__name__) @@ -51,7 +47,7 @@ class IPAuth(auths.Authenticator): acceptProxy = gui.CheckBoxField( label=_('Accept proxy'), defvalue=gui.FALSE, - order=3, + order=50, tooltip=_( 'If checked, requests via proxy will get FORWARDED ip address' ' (take care with this bein checked, can take internal IP addresses from internet)' @@ -59,6 +55,14 @@ class IPAuth(auths.Authenticator): tab=gui.ADVANCED_TAB ) + visibleFromNets = gui.TextField( + order=50, + label=_('Visible only from this networks'), + defvalue='', + tooltip=_('This authenticator will be visible only from these networks. Leave empty to allow all networks'), + tab=gui.ADVANCED_TAB + ) + typeName = _('IP Authenticator') typeType = 'IPAuth' typeDescription = _('IP Authenticator') @@ -93,6 +97,15 @@ class IPAuth(auths.Authenticator): return True return False + def isVisibleFrom(self, request: 'ExtendedHttpRequest'): + """ + Used by the login interface to determine if the authenticator is visible on the login page. + """ + validNets = self.visibleFromNets.value.strip() + if not validNets or net.ipInNetwork(request.ip, validNets): + return True + return False + def internalAuthenticate(self, username: str, credentials: str, groupsManager: 'auths.GroupsManager') -> bool: # In fact, username does not matter, will get IP from request username = self.getIp() # Override provided username and use source IP @@ -108,7 +121,7 @@ class IPAuth(auths.Authenticator): def check(self): return _("All seems to be fine.") - def getJavascript(self, request: 'HttpRequest') -> typing.Optional[str]: + def getJavascript(self, request: 'ExtendedHttpRequest') -> typing.Optional[str]: # We will authenticate ip here, from request.ip # If valid, it will simply submit form with ip submited and a cached generated random password ip = self.getIp() diff --git a/server/src/uds/core/auths/authenticator.py b/server/src/uds/core/auths/authenticator.py index 227fbf01e..032008294 100644 --- a/server/src/uds/core/auths/authenticator.py +++ b/server/src/uds/core/auths/authenticator.py @@ -333,6 +333,12 @@ class Authenticator(Module): # pylint: disable=too-many-public-methods """ return False + def isVisibleFrom(self, request: 'HttpRequest'): + """ + Used by the login interface to determine if the authenticator is visible on the login page. + """ + return True + def transformUsername(self, username: str) -> str: """ On login, this method get called so we can "transform" provided user name. diff --git a/server/src/uds/web/util/authentication.py b/server/src/uds/web/util/authentication.py index 8da0efb0f..73e63c541 100644 --- a/server/src/uds/web/util/authentication.py +++ b/server/src/uds/web/util/authentication.py @@ -97,8 +97,11 @@ def checkLogin( # pylint: disable=too-many-branches, too-many-statements (cache.get(request.ip) or 0) if GlobalConfig.LOGIN_BLOCK_IP.getBool() else 0 ) maxTries = GlobalConfig.MAX_LOGIN_TRIES.getInt() + # Get instance.. + authInstance = authenticator.getInstance() + # Check if user is locked if ( - authenticator.getInstance().blockUserOnLoginFailures is True + authInstance.blockUserOnLoginFailures is True and (tries >= maxTries) or triesByIp >= maxTries ): @@ -107,6 +110,15 @@ def checkLogin( # pylint: disable=too-many-branches, too-many-statements None, _('Too many authentication errrors. User temporarily blocked'), ) + # check if authenticator is visible for this requests + if authInstance.isVisibleFrom(request=request) is False: + authLogLogin( + request, + authenticator, + userName, + 'Access tried from an unallowed source', + ) + return (None, _('Access tried from an unallowed source')) password = form.cleaned_data['password'] user = None diff --git a/server/src/uds/web/util/configjs.py b/server/src/uds/web/util/configjs.py index 6c56f498c..d64f18b8b 100644 --- a/server/src/uds/web/util/configjs.py +++ b/server/src/uds/web/util/configjs.py @@ -103,6 +103,9 @@ def udsJs(request: 'ExtendedHttpRequest') -> str: else: authenticators = Authenticator.objects.all().exclude(visible=False) + # Filter out non visible authenticators + authenticators = [a for a in authenticators if a.getInstance().isVisibleFrom(request)] + # logger.debug('Authenticators PRE: %s', authenticators) if (