diff --git a/server/src/uds/auths/IP/Authenticator.py b/server/src/uds/auths/IP/Authenticator.py index 3690995bf..d3447d9fe 100644 --- a/server/src/uds/auths/IP/Authenticator.py +++ b/server/src/uds/auths/IP/Authenticator.py @@ -49,7 +49,7 @@ logger = logging.getLogger(__name__) class IPAuth(Authenticator): - translateProxy = gui.CheckBoxField(label=_('Accept proxy'), order=3, tooltip=_('If checked, requests via proxy will get FORWARDED ip address (take care with this bein checked, can take internal IP addresses from internet)')) + acceptProxy = gui.CheckBoxField(label=_('Accept proxy'), order=3, tooltip=_('If checked, requests via proxy will get FORWARDED ip address (take care with this bein checked, can take internal IP addresses from internet)')) typeName = _('IP Authenticator') typeType = 'IPAuth' @@ -69,6 +69,9 @@ class IPAuth(Authenticator): def __unicode__(self): return "IP Authenticator" + def getIp(self): + return getRequest().ip_proxy if self.acceptProxy.isTrue() else getRequest().ip + def getGroups(self, ip, groupsManager): # these groups are a bit special. They are in fact ip-ranges, and we must check that the ip is in betwen # The ranges are stored in group names @@ -82,12 +85,14 @@ class IPAuth(Authenticator): def authenticate(self, username, credentials, groupsManager): # If credentials is a dict, that can't be sent directly from web interface, we allow entering # We use this "trick" so authenticators - if username == getRequest().ip: + if username == self.getIp(): self.getGroups(username, groupsManager) return True return False def internalAuthenticate(self, username, credentials, groupsManager): + # In fact, username does not matter, will get IP from request + username = self.getIp() self.getGroups(username, groupsManager) if groupsManager.hasValidGroups() and self.dbAuthenticator().isValidUser(username, True): return True @@ -95,19 +100,21 @@ class IPAuth(Authenticator): @staticmethod def test(env, data): - return _("All seems fine in the authenticator.") + return _("All seems to be fine.") def check(self): - return _("All seems fine in the authenticator.") + return _("All seems to be fine.") def getHtml(self, request): # doAutoLogin = Config.section('IPAUTH').value('autoLogin', '0').getBool() + ip = self.getIp() gm = GroupsManager(self.dbAuthenticator()) - self.getGroups(request.ip, gm) - if gm.hasValidGroups() and self.dbAuthenticator().isValidUser(request.ip, True): + self.getGroups(ip, gm) + + if gm.hasValidGroups() and self.dbAuthenticator().isValidUser(ip, True): passw = '' - return '' + return '' else: - return '
This ip is not allowed to autologin (' + request.ip + ')
' + return '
Invalid auth (' + ip + ')
' # We will authenticate ip here, from request.ip # If valid, it will simply submit form with ip submited and a cached generated random password diff --git a/server/src/uds/core/auths/auth.py b/server/src/uds/core/auths/auth.py index ce9dc3e13..7b30dc88e 100644 --- a/server/src/uds/core/auths/auth.py +++ b/server/src/uds/core/auths/auth.py @@ -131,7 +131,6 @@ def trustedSourceRequired(view_func): Wrapped function for decorator ''' from uds.core.util import net - getIp(request, False) if net.ipInNetwork(request.ip, GlobalConfig.TRUSTED_SOURCES.get(True)) is False: return HttpResponseForbidden() return view_func(request, *args, **kwargs) diff --git a/server/src/uds/core/util/request.py b/server/src/uds/core/util/request.py index ce6299a57..19196b07b 100644 --- a/server/src/uds/core/util/request.py +++ b/server/src/uds/core/util/request.py @@ -49,18 +49,32 @@ def getRequest(): class GlobalRequestMiddleware(object): def process_request(self, request): # Add IP to request - GlobalRequestMiddleware.getIp(request) + GlobalRequestMiddleware.fillIps(request) _requests[threading._get_ident()] = request return None + def process_response(self, request, response): + # Remove IP from global cache (processing responses after this will make global request unavailable, + # but can be got from request again) + try: + del _requests[threading._get_ident()] + except: + logger.exception('Deleting stored request') + return response + @staticmethod - def getIp(request): + def fillIps(request): ''' Obtains the IP of a Django Request, even behind a proxy Returns the obtained IP, that is always be a valid ip address. ''' - request.ip = request.META['REMOTE_ADDR'] + try: + request.ip = request.META['REMOTE_ADDR'] + except: + logger.exception('Request ip not found!!') + request.ip = '0.0.0.0' # No remote addr?? set this IP to a "basic" one, anyway, this should never ocur + try: request.ip_proxy = request.META['HTTP_X_FORWARDED_FOR'].split(",")[0] request.is_proxy = True diff --git a/server/src/uds/web/views.py b/server/src/uds/web/views.py index 954dcb74d..a65ed5558 100644 --- a/server/src/uds/web/views.py +++ b/server/src/uds/web/views.py @@ -42,7 +42,7 @@ from django.utils import timezone from django.views.decorators.http import last_modified from django.views.i18n import javascript_catalog -from uds.core.auths.auth import getIp, webLogin, webLogout, webLoginRequired, authenticate, webPassword, authenticateViaCallback, authLogLogin, authLogLogout, getUDSCookie +from uds.core.auths.auth import webLogin, webLogout, webLoginRequired, authenticate, webPassword, authenticateViaCallback, authLogLogin, authLogLogout, getUDSCookie from uds.models import Authenticator, DeployedService, Transport, UserService, Network from uds.web.forms.LoginForm import LoginForm from uds.core.managers.UserServiceManager import UserServiceManager @@ -140,7 +140,6 @@ def login(request, smallName=None): def customAuth(request, idAuth): res = '' try: - getIp(request) a = Authenticator.objects.get(pk=idAuth).getInstance() res = a.getHtml(request) if res is None: diff --git a/server/src/uds/xmlrpc/auths/AdminAuth.py b/server/src/uds/xmlrpc/auths/AdminAuth.py index 11c40a230..643dae7d4 100644 --- a/server/src/uds/xmlrpc/auths/AdminAuth.py +++ b/server/src/uds/xmlrpc/auths/AdminAuth.py @@ -4,27 +4,27 @@ # Copyright (c) 2012 Virtual Cable S.L. # All rights reserved. # -# Redistribution and use in source and binary forms, with or without modification, +# Redistribution and use in source and binary forms, with or without modification, # are permitted provided that the following conditions are met: # -# * Redistributions of source code must retain the above copyright notice, +# * Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. -# * Neither the name of Virtual Cable S.L. nor the names of its contributors -# may be used to endorse or promote products derived from this software +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software # without specific prior written permission. # -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' @@ -37,7 +37,7 @@ from uds.models import Authenticator from uds.xmlrpc.util.Exceptions import AuthException from uds.core.util.Config import GlobalConfig from uds.core.util import log -from uds.core.auths.auth import authenticate, getIp +from uds.core.auths.auth import authenticate from functools import wraps from django.conf import settings import logging @@ -59,20 +59,20 @@ class Credentials(object): self.user = session['username'] self.locale = session['locale'] self.key = credential_key - + def __unicode__(self): return "authId: {0}, isAdmin: {1}, user: {2}, locale: {3}, key: {4}".format(self.idAuth, self.isAdmin, self.user, self.locale, self.key) - + def __str__(self): return "authId: {0}, isAdmin: {1}, user: {2}, locale: {3}, key: {4}".format(self.idAuth, self.isAdmin, self.user, self.locale, self.key) - + def logout(self): ''' Logout administration user ''' logger.info('Logged out admin user {0}'.format(self)) - - if self.idAuth == ADMIN_AUTH: # Root administrator does nothing on logout + + if self.idAuth == ADMIN_AUTH: # Root administrator does nothing on logout return '' try: a = Authenticator.objects.get(pk=self.idAuth).getInstance() @@ -80,10 +80,10 @@ class Credentials(object): return a.logout(self.user) except Exception: logger.exception('Exception at logout (managed)') - + return '' - - + + def makeCredentials(idAuth, username, locale, isAdmin): session = SessionStore() @@ -105,10 +105,10 @@ def needs_credentials(xmlrpc_func): def _wrapped_xmlrcp_func(credentials, *args, **kwargs): # We expect that request is passed in as last argument ALWAYS (look at views) args = list(args) - request = args.pop() # Last argumment is request + request = args.pop() # Last argumment is request args = tuple(args) logger.debug('Checkin credentials {0} for function {1}'.format(credentials, xmlrpc_func.__name__)) - cred = validateCredentials(request, credentials) + cred = validateCredentials(request, credentials) if cred is not None: logger.debug('Credentials valid, executing') return xmlrpc_func(cred, *args, **kwargs) @@ -121,7 +121,7 @@ def validateCredentials(request, credentials): Validates the credentials of an user :param credentials: ''' - session = SessionStore(session_key = credentials) + session = SessionStore(session_key=credentials) if session.exists(credentials) is False: return None if session.has_key('idAuth') is False: @@ -131,34 +131,32 @@ def validateCredentials(request, credentials): # Updates the expire key, this is the slow part as we can see at debug log, better if we can only update the expire_key this takes 80 ms!!! session.save() logger.debug('Session updated') - return Credentials(request, session, credentials ) + return Credentials(request, session, credentials) def invalidateCredentials(credentials): - session = SessionStore(session_key = credentials.key) + session = SessionStore(session_key=credentials.key) session.delete() def getAdminAuths(locale): ''' - Returns the authenticators + Returns the authenticators ''' activate(locale) res = [] for a in Authenticator.all(): if a.getType().isCustom() is False: - res.append( { 'id' : str(a.id), 'name' : a.name } ) - return res + [ {'id' : ADMIN_AUTH, 'name' : _('Administration') }] - + res.append({ 'id' : str(a.id), 'name' : a.name }) + return res + [ {'id' : ADMIN_AUTH, 'name' : _('Administration') }] + # Xmlrpc functions def login(username, password, idAuth, locale, request): ''' Validates the user/password credentials, assign to it the specified locale for this session and returns a credentials response ''' - - getIp(request) - + logger.info("Validating user {0} with authenticator {1} with locale {2}".format(username, idAuth, locale)) activate(locale) if idAuth == ADMIN_AUTH: @@ -171,7 +169,7 @@ def login(username, password, idAuth, locale, request): user = authenticate(username, password, auth) except Exception: raise AuthException(_('Invalid authenticator')) - + if user is None: log.doLog(auth, log.ERROR, 'Invalid credentials for {0} from {1}'.format(username, request.ip), log.ADMIN) try: @@ -180,18 +178,18 @@ def login(username, password, idAuth, locale, request): except: pass raise AuthException(_('Access denied')) - + if user.staff_member is False: log.doLog(auth, log.ERROR, 'Access denied from {1}. User {0} is not membef of staff'.format(username, request.ip), log.ADMIN) log.doLog(user, log.ERROR, 'Access denied from {0}. This user is not membef of staff'.format(request.ip), log.ADMIN) - + raise AuthException(_('Access denied')) - + log.doLog(auth, log.INFO, 'Access granted to user {0} from {1} to administration'.format(username, request.ip), log.ADMIN) log.doLog(user, log.INFO, 'Access granted from {0} to administration'.format(request.ip), log.ADMIN) - + return makeCredentials(idAuth, username, locale, user.is_admin) - + @needs_credentials def logout(credentials):