Changeset - c5169e445fb8
[Not reviewed]
beta
0 7 0
Marcin Kuzminski - 13 years ago 2013-01-04 23:34:53
marcin@python-works.com
Full IP restrictions enabled
- short cache query for IP for performance
- remove redundant logic
- some small css fixes for login form to better show IP restricted message
7 files changed with 46 insertions and 26 deletions:
0 comments (0 inline, 0 general)
rhodecode/controllers/api/__init__.py
Show inline comments
 
@@ -40,13 +40,13 @@ from pylons.controllers import WSGIContr
 

	
 

	
 
from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError, \
 
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
 

	
 
log = logging.getLogger('JSONRPC')
 

	
 

	
 
@@ -145,23 +145,21 @@ class JSONRPCController(WSGIController):
 
        # check if we can find this session using api_key
 
        try:
 
            u = User.get_by_api_key(self._req_api_key)
 
            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')
 

	
 
        self._error = None
 
        try:
rhodecode/controllers/login.py
Show inline comments
 
@@ -51,16 +51,15 @@ class LoginController(BaseController):
 
    def __before__(self):
 
        super(LoginController, self).__before__()
 

	
 
    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:
 
            # import Login Form validator class
 
            login_form = LoginForm()
 
            try:
rhodecode/lib/auth.py
Show inline comments
 
@@ -43,12 +43,13 @@ from rhodecode.lib.exceptions import Lda
 
from rhodecode.lib.utils import get_repo_slug, get_repos_group_slug
 
from rhodecode.lib.auth_ldap import AuthLdap
 

	
 
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__)
 

	
 

	
 
class PasswordGenerator(object):
 
    """
 
@@ -324,13 +325,12 @@ class  AuthUser(object):
 
        self.lastname = ''
 
        self.email = ''
 
        self.is_authenticated = False
 
        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
 

	
 
    def propagate_data(self):
 
        user_model = UserModel()
 
@@ -374,19 +374,35 @@ class  AuthUser(object):
 

	
 
        if not self.username:
 
            self.username = 'None'
 

	
 
        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 "<AuthUser('id:%s:%s|%s')>" % (self.user_id, self.username,
 
                                              self.is_authenticated)
 

	
 
    def set_authenticated(self, authenticated=True):
 
        if self.user_id != self.anonymous_user.user_id:
 
@@ -408,15 +424,18 @@ class  AuthUser(object):
 
        user_id = cookie_store.get('user_id')
 
        username = cookie_store.get('username')
 
        api_key = cookie_store.get('api_key')
 
        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'])
 

	
 

	
 
def set_available_permissions(config):
 
@@ -459,23 +478,32 @@ class LoginRequired(object):
 
    def __call__(self, func):
 
        return decorator(self.__wrapper, func)
 

	
 
    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:
 
            log.debug('Checking API KEY access for %s' % cls)
 
            if user.api_key == request.GET.get('api_key'):
 
                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)
 
            )
 
            return func(*fargs, **fkwargs)
 
        else:
rhodecode/lib/base.py
Show inline comments
 
@@ -17,13 +17,13 @@ from pylons.templating import render_mak
 

	
 
from rhodecode import __version__, BACKENDS
 

	
 
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
 

	
 
from rhodecode.model.db import Repository, RhodeCodeUi, User, RhodeCodeSetting
 
from rhodecode.model.notification import NotificationModel
 
from rhodecode.model.scm import ScmModel
 
@@ -143,16 +143,14 @@ class BaseVCSController(object):
 

	
 
        :param action: push or pull action
 
        :param user: user instance
 
        :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))
 
        if action == 'push':
 
            if not HasPermissionAnyMiddleware('repository.write',
 
                                              'repository.admin')(user,
rhodecode/model/db.py
Show inline comments
 
@@ -533,12 +533,13 @@ class UserIpMap(Base, BaseModel):
 
    )
 
    __mapper_args__ = {}
 

	
 
    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
 
    def _get_ip_range(cls, ip_addr):
 
        from rhodecode.lib import ipaddr
 
        net = ipaddr.IPv4Network(ip_addr)
rhodecode/model/user.py
Show inline comments
 
@@ -24,13 +24,12 @@
 
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 

	
 
import logging
 
import traceback
 
import itertools
 
import collections
 
import functools
 
from pylons import url
 
from pylons.i18n.translation import _
 

	
 
from sqlalchemy.exc import DatabaseError
 
from sqlalchemy.orm import joinedload
 

	
rhodecode/public/css/style.css
Show inline comments
 
@@ -1999,13 +1999,12 @@ a.metatag[tag="license"]:hover {
 
#footer div#footer-inner .footer-link a,#footer div#footer-inner .footer-link-right a
 
	{
 
	color: #FFF;
 
}
 
 
#login div.title {
 
	width: 420px;
 
	clear: both;
 
	overflow: hidden;
 
	position: relative;
 
	background-color: #003B76; 
 
	background-repeat : repeat-x;
 
	background-image : -khtml-gradient( linear, left top, left bottom, from(#003B76), to(#00376E)); 
 
@@ -2018,13 +2017,12 @@ a.metatag[tag="license"]:hover {
 
	filter : progid : DXImageTransform.Microsoft.gradient ( startColorstr = '#003b76', endColorstr = '#00376e', GradientType = 0);
 
	margin: 0 auto;
 
	padding: 0;
 
}
 
 
#login div.inner {
 
	width: 380px;
 
	background: #FFF url("../images/login.png") no-repeat top left;
 
	border-top: none;
 
	border-bottom: none;
 
	margin: 0 auto;
 
	padding: 20px;
 
}
 
@@ -2035,13 +2033,12 @@ a.metatag[tag="license"]:hover {
 
	text-align: right;
 
	margin: 2px 10px 0 0;
 
	padding: 5px 0 0 5px;
 
}
 
 
#login div.form div.fields div.field div.input input {
 
	width: 176px;
 
	background: #FFF;
 
	border-top: 1px solid #b3b3b3;
 
	border-left: 1px solid #b3b3b3;
 
	border-right: 1px solid #eaeaea;
 
	border-bottom: 1px solid #eaeaea;
 
	color: #000;
0 comments (0 inline, 0 general)