diff --git a/rhodecode/controllers/api/__init__.py b/rhodecode/controllers/api/__init__.py --- a/rhodecode/controllers/api/__init__.py +++ b/rhodecode/controllers/api/__init__.py @@ -43,7 +43,7 @@ from webob.exc import HTTPNotFound, HTTP HTTPBadRequest, HTTPError from rhodecode.model.db import User -from rhodecode.lib.auth import AuthUser, check_ip_access +from rhodecode.lib.auth import AuthUser from rhodecode.lib.base import _get_ip_addr, _get_access_path from rhodecode.lib.utils2 import safe_unicode @@ -148,17 +148,15 @@ class JSONRPCController(WSGIController): if u is None: return jsonrpc_error(retid=self._req_id, message='Invalid API KEY') + #check if we are allowed to use this IP - allowed_ips = AuthUser.get_allowed_ips(u.user_id) - if check_ip_access(source_ip=ip_addr, allowed_ips=allowed_ips) is False: - log.info('Access for IP:%s forbidden, ' - 'not in %s' % (ip_addr, allowed_ips)) + auth_u = AuthUser(u.user_id, self._req_api_key, ip_addr=ip_addr) + if not auth_u.ip_allowed: return jsonrpc_error(retid=self._req_id, message='request from IP:%s not allowed' % (ip_addr)) else: log.info('Access for IP:%s allowed' % (ip_addr)) - auth_u = AuthUser(u.user_id, self._req_api_key, ip_addr=ip_addr) except Exception, e: return jsonrpc_error(retid=self._req_id, message='Invalid API KEY') diff --git a/rhodecode/controllers/login.py b/rhodecode/controllers/login.py --- a/rhodecode/controllers/login.py +++ b/rhodecode/controllers/login.py @@ -54,10 +54,9 @@ class LoginController(BaseController): def index(self): # redirect if already logged in c.came_from = request.GET.get('came_from') - - if self.rhodecode_user.is_authenticated \ - and self.rhodecode_user.username != 'default': - + not_default = self.rhodecode_user.username != 'default' + ip_allowed = self.rhodecode_user.ip_allowed + if self.rhodecode_user.is_authenticated and not_default and ip_allowed: return redirect(url('home')) if request.POST: diff --git a/rhodecode/lib/auth.py b/rhodecode/lib/auth.py --- a/rhodecode/lib/auth.py +++ b/rhodecode/lib/auth.py @@ -46,6 +46,7 @@ from rhodecode.lib.auth_ldap import Auth from rhodecode.model import meta from rhodecode.model.user import UserModel from rhodecode.model.db import Permission, RhodeCodeSetting, User, UserIpMap +from rhodecode.lib.caching_query import FromCache log = logging.getLogger(__name__) @@ -327,7 +328,6 @@ class AuthUser(object): self.admin = False self.inherit_default_permissions = False self.permissions = {} - self.allowed_ips = set() self._api_key = api_key self.propagate_data() self._instance = None @@ -377,13 +377,29 @@ class AuthUser(object): log.debug('Auth User is now %s' % self) user_model.fill_perms(self) - log.debug('Filling Allowed IPs') - self.allowed_ips = AuthUser.get_allowed_ips(self.user_id) @property def is_admin(self): return self.admin + @property + def ip_allowed(self): + """ + Checks if ip_addr used in constructor is allowed from defined list of + allowed ip_addresses for user + + :returns: boolean, True if ip is in allowed ip range + """ + #check IP + allowed_ips = AuthUser.get_allowed_ips(self.user_id, cache=True) + if check_ip_access(source_ip=self.ip_addr, allowed_ips=allowed_ips): + log.debug('IP:%s is in range of %s' % (self.ip_addr, allowed_ips)) + return True + else: + log.info('Access for IP:%s forbidden, ' + 'not in %s' % (self.ip_addr, allowed_ips)) + return False + def __repr__(self): return "" % (self.user_id, self.username, self.is_authenticated) @@ -411,9 +427,12 @@ class AuthUser(object): return AuthUser(user_id, api_key, username) @classmethod - def get_allowed_ips(cls, user_id): + def get_allowed_ips(cls, user_id, cache=False): _set = set() - user_ips = UserIpMap.query().filter(UserIpMap.user_id == user_id).all() + user_ips = UserIpMap.query().filter(UserIpMap.user_id == user_id) + if cache: + user_ips = user_ips.options(FromCache("sql_cache_short", + "get_user_ips_%s" % user_id)) for ip in user_ips: _set.add(ip.ip_addr) return _set or set(['0.0.0.0/0']) @@ -462,6 +481,15 @@ class LoginRequired(object): def __wrapper(self, func, *fargs, **fkwargs): cls = fargs[0] user = cls.rhodecode_user + loc = "%s:%s" % (cls.__class__.__name__, func.__name__) + + #check IP + ip_access_ok = True + if not user.ip_allowed: + from rhodecode.lib import helpers as h + h.flash(h.literal(_('IP %s not allowed' % (user.ip_addr))), + category='warning') + ip_access_ok = False api_access_ok = False if self.api_access: @@ -470,9 +498,9 @@ class LoginRequired(object): api_access_ok = True else: log.debug("API KEY token not valid") - loc = "%s:%s" % (cls.__class__.__name__, func.__name__) + log.debug('Checking if %s is authenticated @ %s' % (user.username, loc)) - if user.is_authenticated or api_access_ok: + if (user.is_authenticated or api_access_ok) and ip_access_ok: reason = 'RegularAuth' if user.is_authenticated else 'APIAuth' log.info('user %s is authenticated and granted access to %s ' 'using %s' % (user.username, loc, reason) diff --git a/rhodecode/lib/base.py b/rhodecode/lib/base.py --- a/rhodecode/lib/base.py +++ b/rhodecode/lib/base.py @@ -20,7 +20,7 @@ from rhodecode import __version__, BACKE from rhodecode.lib.utils2 import str2bool, safe_unicode, AttributeDict,\ safe_str, safe_int from rhodecode.lib.auth import AuthUser, get_container_username, authfunc,\ - HasPermissionAnyMiddleware, CookieStoreWrapper, check_ip_access + HasPermissionAnyMiddleware, CookieStoreWrapper from rhodecode.lib.utils import get_repo_slug, invalidate_cache from rhodecode.model import meta @@ -146,10 +146,8 @@ class BaseVCSController(object): :param repo_name: repository name """ #check IP - allowed_ips = AuthUser.get_allowed_ips(user.user_id) - if check_ip_access(source_ip=ip_addr, allowed_ips=allowed_ips) is False: - log.info('Access for IP:%s forbidden, ' - 'not in %s' % (ip_addr, allowed_ips)) + authuser = AuthUser(user_id=user.user_id, ip_addr=ip_addr) + if not authuser.ip_allowed: return False else: log.info('Access for IP:%s allowed' % (ip_addr)) diff --git a/rhodecode/model/db.py b/rhodecode/model/db.py --- a/rhodecode/model/db.py +++ b/rhodecode/model/db.py @@ -536,6 +536,7 @@ class UserIpMap(Base, BaseModel): ip_id = Column("ip_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None) ip_addr = Column("ip_addr", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None) + active = Column("active", Boolean(), nullable=True, unique=None, default=True) user = relationship('User', lazy='joined') @classmethod diff --git a/rhodecode/model/user.py b/rhodecode/model/user.py --- a/rhodecode/model/user.py +++ b/rhodecode/model/user.py @@ -27,7 +27,6 @@ import logging import traceback import itertools import collections -import functools from pylons import url from pylons.i18n.translation import _ diff --git a/rhodecode/public/css/style.css b/rhodecode/public/css/style.css --- a/rhodecode/public/css/style.css +++ b/rhodecode/public/css/style.css @@ -2002,7 +2002,6 @@ a.metatag[tag="license"]:hover { } #login div.title { - width: 420px; clear: both; overflow: hidden; position: relative; @@ -2021,7 +2020,6 @@ a.metatag[tag="license"]:hover { } #login div.inner { - width: 380px; background: #FFF url("../images/login.png") no-repeat top left; border-top: none; border-bottom: none; @@ -2038,7 +2036,6 @@ a.metatag[tag="license"]:hover { } #login div.form div.fields div.field div.input input { - width: 176px; background: #FFF; border-top: 1px solid #b3b3b3; border-left: 1px solid #b3b3b3;