diff --git a/rhodecode/authentication/plugins/auth_headers.py b/rhodecode/authentication/plugins/auth_headers.py --- a/rhodecode/authentication/plugins/auth_headers.py +++ b/rhodecode/authentication/plugins/auth_headers.py @@ -190,8 +190,8 @@ class RhodeCodeAuthPlugin(RhodeCodeExter username = getattr(userobj, 'username') if not username: - # we don't have any objects in DB user doesn't exist extrac username - # from environ based on the settings + # we don't have any objects in DB user doesn't exist extract + # username from environ based on the settings username = self._get_username(environ, settings) # if cannot fetch username, it's a no-go for this plugin to proceed diff --git a/rhodecode/authentication/plugins/auth_token.py b/rhodecode/authentication/plugins/auth_token.py new file mode 100644 --- /dev/null +++ b/rhodecode/authentication/plugins/auth_token.py @@ -0,0 +1,134 @@ +# -*- coding: utf-8 -*- + +# Copyright (C) 2016-2016 RhodeCode GmbH +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License, version 3 +# (only), as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +# This program is dual-licensed. If you wish to learn more about the +# RhodeCode Enterprise Edition, including its added features, Support services, +# and proprietary license terms, please see https://rhodecode.com/licenses/ + +""" +RhodeCode authentication token plugin for built in internal auth +""" + +import logging + +from sqlalchemy.ext.hybrid import hybrid_property + +from rhodecode.translation import _ +from rhodecode.authentication.base import RhodeCodeAuthPluginBase, VCS_TYPE +from rhodecode.authentication.routes import AuthnPluginResourceBase +from rhodecode.model.db import User, UserApiKeys + + +log = logging.getLogger(__name__) + + +def plugin_factory(plugin_id, *args, **kwds): + plugin = RhodeCodeAuthPlugin(plugin_id) + return plugin + + +class RhodecodeAuthnResource(AuthnPluginResourceBase): + pass + + +class RhodeCodeAuthPlugin(RhodeCodeAuthPluginBase): + """ + Enables usage of authentication tokens for vcs operations. + """ + + def includeme(self, config): + config.add_authn_plugin(self) + config.add_authn_resource(self.get_id(), RhodecodeAuthnResource(self)) + config.add_view( + 'rhodecode.authentication.views.AuthnPluginViewBase', + attr='settings_get', + request_method='GET', + route_name='auth_home', + context=RhodecodeAuthnResource) + config.add_view( + 'rhodecode.authentication.views.AuthnPluginViewBase', + attr='settings_post', + request_method='POST', + route_name='auth_home', + context=RhodecodeAuthnResource) + + def get_display_name(self): + return _('Rhodecode Token Auth') + + @hybrid_property + def name(self): + return "authtoken" + + def user_activation_state(self): + def_user_perms = User.get_default_user().AuthUser.permissions['global'] + return 'hg.register.auto_activate' in def_user_perms + + def allows_authentication_from( + self, user, allows_non_existing_user=True, + allowed_auth_plugins=None, allowed_auth_sources=None): + """ + Custom method for this auth that doesn't accept empty users. And also + allows rhodecode and authtoken extern_type to auth with this. But only + via vcs mode + """ + # only this and rhodecode plugins can use this type + from rhodecode.authentication.plugins import auth_rhodecode + allowed_auth_plugins = [ + self.name, auth_rhodecode.RhodeCodeAuthPlugin.name] + # only for vcs operations + allowed_auth_sources = [VCS_TYPE] + + return super(RhodeCodeAuthPlugin, self).allows_authentication_from( + user, allows_non_existing_user=False, + allowed_auth_plugins=allowed_auth_plugins, + allowed_auth_sources=allowed_auth_sources) + + def auth(self, userobj, username, password, settings, **kwargs): + if not userobj: + log.debug('userobj was:%s skipping' % (userobj, )) + return None + + user_attrs = { + "username": userobj.username, + "firstname": userobj.firstname, + "lastname": userobj.lastname, + "groups": [], + "email": userobj.email, + "admin": userobj.admin, + "active": userobj.active, + "active_from_extern": userobj.active, + "extern_name": userobj.user_id, + "extern_type": userobj.extern_type, + } + + log.debug('Authenticating user with args %s', user_attrs) + if userobj.active: + role = UserApiKeys.ROLE_VCS + active_tokens = [x.api_key for x in + User.extra_valid_auth_tokens(userobj, role=role)] + if userobj.username == username and password in active_tokens: + log.info( + 'user `%s` successfully authenticated via %s', + user_attrs['username'], self.name) + return user_attrs + log.error( + 'user `%s` failed to authenticate via %s, reason: bad or ' + 'inactive token.', username, self.name) + else: + log.warning( + 'user `%s` failed to authenticate via %s, reason: account not ' + 'active.', username, self.name) + return None