From 8fcb39ef38d4937b395a1a549acddede4db29be0 Mon Sep 17 00:00:00 2001 From: Mike McGrath Date: Tue, 2 Oct 2007 20:09:41 -0500 Subject: [PATCH] Added upload.cgi script --- scripts/upload.cgi/README | 26 ++++ scripts/upload.cgi/upload.cgi | 228 ++++++++++++++++++++++++++++++++++ 2 files changed, 254 insertions(+) create mode 100644 scripts/upload.cgi/README create mode 100755 scripts/upload.cgi/upload.cgi diff --git a/scripts/upload.cgi/README b/scripts/upload.cgi/README new file mode 100644 index 0000000..55b8cf3 --- /dev/null +++ b/scripts/upload.cgi/README @@ -0,0 +1,26 @@ +If you have a better installation or one that is proven to work. Also please +let us know if there is a better or more efficient way to do what we're trying +to do here. Technologies change over time, keep us up to date ;) + +Alias /repo/pkgs/ /repo/pkgs/ + + + SSLVerifyClient optional + SSLVerifyDepth 1 + SSLOptions +StrictRequire +StdEnvVars +OptRenegotiate + # require that the access comes from internal or that + # the client auth cert was created by us and signed by us + SSLRequire ( %{SSL_CIPHER} !~ m/^(EXP|NULL)/ \ + and %{SSL_CLIENT_S_DN_O} eq "Fedora Project" \ + and %{SSL_CLIENT_I_DN_O} eq "Fedora Project" \ + and %{SSL_CLIENT_I_DN_OU} eq "Upload Files" ) + + + + SetHandler cgi-script + Options ExecCGI + Order Allow,Deny + Allow from all + SSLRequireSSL + + diff --git a/scripts/upload.cgi/upload.cgi b/scripts/upload.cgi/upload.cgi new file mode 100755 index 0000000..4500d04 --- /dev/null +++ b/scripts/upload.cgi/upload.cgi @@ -0,0 +1,228 @@ +#!/usr/bin/python +# +# CGI script to handle file updates for the rpms CVS repository. There +# is nothing really complex here other than tedious checking of our +# every step along the way... +# +# $Id: upload.cgi,v 1.10 2005/04/15 23:44:24 gafton Exp $ +# License: GPL + +import os +import sys +import cgi +import stat +import md5 +import tempfile +import StringIO + +sys.path.append('/var/fedora-accounts') +import website + +# reading buffer size +BUFFER_SIZE = 4096 + +# Debugging version +DEBUG = 0 + +# We check modules exist from this dircetory +CVSREPO = "/cvs/pkgs/rpms" + +do_userdb_auth = 1 + +# log a trace of what we're doing +def log_msg(*msgs): + s = StringIO.StringIO() + for m in msgs: + s.write("%s " % (m,)) + sys.stderr.write("%s\n" % (s.getvalue(),)) + del s + +os.umask(002) + +# log the entire environment +def log_environ(): + for k in os.environ.keys(): + log_msg("%s=%s" % (k, os.environ[k])) + return + +# abort running the script +def send_error(text): + print "Content-type: text/plain\n" + print text + sys.exit(1) + +# prepare to exit graciously +def send_ok(text): + print "Content-type: text/plain\n" + if text: + print text + +# check and validate that all the fields are present +def check_form(var): + if not form.has_key(var): + send_error("required field '%s' is not present" % (var,)) + ret = form.getvalue(var) + if type(ret) == type([]): + send_error("Multiple values given for '%s'. Aborting" % (var,)) + ret = os.path.basename(ret) # this is a path component + return ret + +# if a directory exists, check that it has the proper permissions +def check_dir(tmpdir, wok = os.W_OK): + if not os.access(tmpdir, os.F_OK): + return 0 + if not os.access(tmpdir, os.R_OK|os.X_OK|wok): + send_error("Unable to write to %s repository." % ( + tmpdir,)) + if not os.path.isdir(tmpdir): + send_error("Path %s is not a directory." % (tmpdir,)) + return 1 + +# +# MAIN START +# +if do_userdb_auth: + dbh = website.get_dbh() +auth_username = auth_password = None +need_auth = 1 +if os.environ.has_key('SSL_CLIENT_S_DN_CN'): + auth_username = os.environ['SSL_CLIENT_S_DN_CN'] + need_auth = 0 +elif do_userdb_auth and 0: + authtype, authinfo = website.get_http_auth_info() + + need_auth = 1 + auth_msg = "Authentication is required." + + if authinfo: + if authtype.lower() == 'basic': + need_auth = not website.do_checkpass(dbh, authinfo[0], authinfo[1]) + auth_username, auth_password = authinfo + auth_msg = "Username or password incorrect." + else: + auth_msg = "Unknown authentication type %s" % authtype + +pieces = os.environ['REQUEST_URI'].split('/') +assert pieces[1] == 'repo' +if do_userdb_auth: + #need_auth = need_auth or not website.have_group(dbh, auth_username, 'cvs' + pieces[2]) + need_auth = need_auth or not website.have_group(dbh, auth_username, 'cvsextras') +auth_msg = "You do not have the appropriate authorization to upload. %s %s %s" % (dbh, auth_username, 'cvs' + pieces[2]) + +if need_auth: + print """Status: 403 Unauthorized to access the document +WWW-authenticate: Basic realm="fedora.redhat.com" +Content-type: text/plain + +""" + str(auth_msg) + sys.exit(0) + +form = cgi.FieldStorage() +NAME = check_form("name") +MD5SUM = check_form("md5sum") + +# Is this a submission or a test? +# in a test, we don't get a FILE, just a FILENAME. +# In a submission, we don;t get a FILENAME, just the FILE. +FILE = None +FILENAME = None +if form.has_key("filename"): + # check the presence of the file + FILENAME = check_form("filename") + log_msg("Checking file status", + "NAME=%s FILENAME=%s MD5SUM=%s" % (NAME,FILENAME,MD5SUM)) +else: + if form.has_key("file"): + FILE = form["file"] + if not FILE.file: + send_error("No file given for upload. Aborting") + try: + FILENAME = os.path.basename(FILE.filename) + except: + send_error("Could not extract the filename for upload. Aborting") + else: + send_error("required field '%s' is not present" % ("file", )) + log_msg("Processing upload request", + "NAME=%s FILENAME=%s MD5SUM=%s" % (NAME,FILENAME,MD5SUM)) +# Now that all the fields are valid,, figure out our operating environment +if not os.environ.has_key("SCRIPT_FILENAME"): + send_error("My running environment is funky. Aborting") + +# start processing this request +my_script = os.environ["SCRIPT_FILENAME"] +# the module's top level directory +my_topdir = os.path.dirname(my_script) +my_moddir = "%s/%s" % (my_topdir, NAME) +my_filedir = "%s/%s" % (my_moddir, FILENAME) +my_md5dir = "%s/%s" % (my_filedir, MD5SUM) + +# first test if the module really exists +if not check_dir("%s/%s" % (CVSREPO, NAME), 0): + log_msg("Unknown module", NAME) + send_ok("Module '%s' does not exist!" % (NAME,)) + sys.exit(-9) + +# try to see if we already have this file... +file_dest = "%s/%s/%s/%s/%s" % (my_topdir, NAME, FILENAME, MD5SUM, FILENAME) +if os.access(file_dest, os.F_OK | os.R_OK): + # already there, spare the effort + s = os.stat(file_dest) + # if we're just checking we need to be rather terse + if FILE is None: + message = "Available" + else: + FILE.file.close() + message = "File %s already exists\nFile: %s Size: %d" % ( + FILENAME, file_dest, s[stat.ST_SIZE]) + send_ok(message) + sys.exit(0) +# just checking? +if FILE is None: + send_ok("Missing") + sys.exit(-9) + +# check that all directories are in place +for tmpdir in [ my_topdir, my_moddir, my_filedir, my_md5dir]: + if not check_dir(tmpdir): + # we agree to create this directory if the corresponding cvs module dir exists + if tmpdir == my_moddir: + # W_OK access is not necessary for this cgi + if check_dir("%s/%s" % (CVSREPO, NAME), 0): + os.mkdir(tmpdir, 02775) + log_msg("mkdir", tmpdir) + continue + if tmpdir in [ my_filedir, my_md5dir ]: + # don't require those directories just yet + continue + send_error("Directory %s does not exist and I won't create it" % (tmpdir,)) + +# grab a temporary filename and dump our file in there +tempfile.tempdir = my_moddir +tmpfile = tempfile.mktemp(MD5SUM) +tmpfd = open(tmpfile, "wb+") +# now read the whole file in +m = md5.new() +FILELENGTH=0 +while 1: + s = FILE.file.read(BUFFER_SIZE) + if not s: + break + tmpfd.write(s) + m.update(s) + FILELENGTH = FILELENGTH + len(s) +# now we're done reading, check the MD5 sum of what we got +tmpfd.close() +my_md5sum = m.hexdigest() +if MD5SUM != my_md5sum: + send_error("MD5 check failed. Received %s instead of %s" % ( + my_md5sum, MD5SUM)) +# wow, even the MD5SUM matches. make sure full path is valid now +for tmpdir in [ my_moddir, my_filedir, my_md5dir ]: + if not check_dir(tmpdir): + os.mkdir(tmpdir, 02775) + log_msg("mkdir", tmpdir) +# and move our file to the final location +os.rename(tmpfile, file_dest) +log_msg("Stored filesize", FILELENGTH, file_dest) + +send_ok("File %s size %d MD5 %s stored OK" % (FILENAME, FILELENGTH, MD5SUM))