#!/usr/bin/python # Requires the python-memcached package to be installed. # # Requires the following command to be run: # setsebool -P httpd_can_network_connect 1 # setsebool -P httpd_can_network_memcache 1 import cgi import json from memcached import MemcacheRing import os import random import re import subprocess from time import time # After how many seconds the cached information about an authentication # token is discarded. TOKEN_LIFE = 86400 # This is used as a prefix for tokens and memcache keys. We use the default # value from the Swift tempauth filter. In the future, this should be turned # into a configuration parameter. RESELLER_PREFIX = 'AUTH_' MEMCACHE_SERVERS = ['rhs1.example.com:11211'] def main(): remote_user = os.environ['REMOTE_USER'] matches = re.match('([^@]+)@.*', remote_user) if not matches: raise RuntimeError("Malformed REMOTE_USER \"%s\"" % remote_user) username = matches.group(1) mc = MemcacheRing(MEMCACHE_SERVERS) # Check if we already got a token for this user. memcache_user_key = '%s/user/%s' % (RESELLER_PREFIX, username) token = None candidate_token = mc.get(memcache_user_key) if candidate_token: memcache_token_key = '%s/token/%s' % (RESELLER_PREFIX, candidate_token) cached_auth_data = mc.get(memcache_token_key) if cached_auth_data: expires, groups = cached_auth_data if expires > time(): token = candidate_token if not token: # We don't use uuid.uuid4() here because importing the uuid module # causes (harmless) SELinux denials in the audit log on RHEL 6. If this # is a security concern, a custom SELinux policy module could be written # to not log those denials. r = random.SystemRandom() token = '%stk%s' % (RESELLER_PREFIX, ''.join(r.choice('abcdef0123456789') for x in range(32))) # Retrieve the numerical group IDs. We cannot list the group names # because group names from Active Directory may contain spaces, and # we wouldn't be able to split the list of group names into its # elements. p = subprocess.Popen(['id', '-G', username], stdout=subprocess.PIPE) if p.wait() != 0: raise RuntimeError("Failure running id -G for %s" % remote_user) (p_stdout, p_stderr) = p.communicate() # Convert the group numbers into group names. groups = [] for gid in p_stdout.strip().split(" "): groups.append(grp.getgrgid(int(gid))[0]) # The first element of the list is considered a unique identifier # for the user. We add the username to accomplish this. if username in groups: groups.remove(username) groups = [username] + groups groups = ','.join(groups) expires = time() + TOKEN_LIFE auth_data = (expires, groups) memcache_token_key = "%s/token/%s" % (RESELLER_PREFIX, token) mc.set(memcache_token_key, auth_data, timeout=TOKEN_LIFE) # Record the token with the user info for future use. memcache_user_key = '%s/user/%s' % (RESELLER_PREFIX, username) mc.set(memcache_user_key, token, timeout=TOKEN_LIFE) print "X-Auth-Token: %s\n" % token # For debugging. print "
%i / %s
" % mc.get(memcache_token_key) try: print("Content-Type: text/html") main() except: cgi.print_exception()