Add support for auth-type=form

I'm using a gerrit installation that uses neither 'basic' nor 'digest'
authentication methods, but instead redirects users to a custom login
form which sets a cookie to provide authentication.

This commit adds a new auth-type 'form', that automates logging in
through that form.

Currently, the path to the form is hardcoded to '/login' and the names
of the username and password fields to 'username' and 'password'
respectively. If preferred, I can add configuration options for each of
those too.

Change-Id: I3fff38b1be3f17c753787c9eb62ede0ce60a3ece
This commit is contained in:
Christoph Gysin 2015-03-29 00:43:44 +02:00 committed by James E. Blair
parent e45faea1b3
commit ccc5baaf9e
4 changed files with 74 additions and 4 deletions

View File

@ -23,9 +23,8 @@ servers:
# username: CHANGEME
# Your password in Gerrit (Settings -> HTTP Password). [required]
# password: CHANGEME
# Authentication type required by the Gerrit server. Can be 'basic' or
# 'digest'. Defaults to 'digest' if not set or set to an unexpected
# value.
# Authentication type required by the Gerrit server. Can be 'basic', 'digest' or
# 'form'. Defaults to 'digest' if not set or set to an unexpected value.
# auth-type: digest
# A location where Gertty should store its git repositories. These
# can be the same git repositories where you do your own work --

68
gertty/auth.py Normal file
View File

@ -0,0 +1,68 @@
# Copyright 2015 Christoph Gysin <christoph.gysin@gmail.com>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import logging
import requests
import urlparse
class FormAuth(requests.auth.AuthBase):
def __init__(self, username, password):
self.username = username
self.password = password
self.log = logging.getLogger('gertty.auth')
def _retry_using_form_auth(self, response, args):
adapter = requests.adapters.HTTPAdapter()
request = _copy_request(response.request)
u = urlparse.urlparse(response.url)
url = urlparse.urlunparse([u.scheme, u.netloc, '/login',
None, None, None])
auth = {'username': self.username,
'password': self.password}
request2 = requests.Request('POST', url, data=auth).prepare()
response2 = adapter.send(request2, **args)
if response2.status_code == 401:
self.log.error('Login failed: Invalid username or password?')
return response
cookie = response2.headers.get('set-cookie')
if cookie is not None:
request.headers['Cookie'] = cookie
response3 = adapter.send(request, **args)
return response3
def _response_hook(self, response, **kwargs):
if response.status_code == 401:
return self._retry_using_form_auth(response, kwargs)
return response
def __call__(self, request):
request.headers["Connection"] = "Keep-Alive"
request.register_hook('response', self._response_hook)
return request
def _copy_request(request):
new_request = requests.PreparedRequest()
new_request.method = request.method
new_request.url = request.url
new_request.body = request.body
new_request.hooks = request.hooks
new_request.headers = request.headers.copy()
return new_request

View File

@ -165,7 +165,7 @@ class Config(object):
"Permissions are: {}".format(self.path, oct(mode)))
exit(1)
self.auth_type = server.get('auth-type', 'digest')
auth_types = ['digest', 'basic']
auth_types = ['digest', 'basic', 'form']
if self.auth_type not in auth_types:
self.auth_type = 'digest'
self.verify_ssl = server.get('verify-ssl', True)

View File

@ -37,6 +37,7 @@ from six.moves.urllib import parse as urlparse
import gertty.version
from gertty import gitrepo
from gertty.auth import FormAuth
HIGH_PRIORITY=0
NORMAL_PRIORITY=1
@ -1306,6 +1307,8 @@ class Sync(object):
self.session = requests.Session()
if self.app.config.auth_type == 'basic':
authclass = requests.auth.HTTPBasicAuth
elif self.app.config.auth_type == 'form':
authclass = FormAuth
else:
authclass = requests.auth.HTTPDigestAuth
self.auth = authclass(