Skip to content

Instantly share code, notes, and snippets.

@kisom
Created October 3, 2011 12:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kisom/1258960 to your computer and use it in GitHub Desktop.
Save kisom/1258960 to your computer and use it in GitHub Desktop.
update hackerhub profile
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# author: kyle isom <coder@kyleisom.net>
# license: isc / public domain dual-license
#
# automate updates to hackerhub
"""
Automate updates to hackerhub. Validates a local JSON profile, sftp's it to
a server, and notifies hackerhub to update the profile.
"""
import getopt
import json
import os
import paramiko
import sys
import urllib
import urllib2
VERSION = "1.0.0"
def check_config(configfile):
"""
Check configuration file. By default it looks for ${HOME}/.config/hhup.conf
"""
# validate the config file, creating as required
configdir = os.path.dirname(configfile)
if not os.path.exists(configdir):
if os.pardir in configdir:
# from the pydocs: Note makedirs() will become confused
# if the path elements to create include os.pardir
return False
os.makedirs(configdir, 0700)
if not os.path.exists(configdir):
# the makedirs failed, where is your god now!?
return False
if not os.path.exists(configfile):
if not write_config(configfile):
return False
# do it outside the if to read the newly written config
profile = parse_config(configfile)
if (not profile or not 'user' in profile or not 'remote' in profile
or not 'remote path' in profile):
return False
return profile
def write_config(configfile):
"""
Get the information from the user required to set up the config file.
This is the only function that requires interaction from the user.
"""
user = raw_input('user id: ').strip()
remote = raw_input('remote server: ').strip()
remotepath = raw_input('remote path: ').strip()
config = open(configfile, 'w')
config.write("\n\tuser: %s\n" % user)
config.write("\tremote: %s\n" % remote)
config.write("\tremote path: %s\n" % remotepath)
config.close()
# right now, any errors result in a exception being thrown, so we
# will always return true.
return True
def parse_config(configfile):
"""
Load the options from the configuration file.
"""
config = open(configfile).read()
profile = {}
# just in case we're infected with windows carriage returns
config.replace('\r', '')
config = [line.strip() for line in config.split('\n')
if line.strip() and ':' in line]
for line in config:
# set maxsplit to 1 because protocols have a colon too
key, value = line.split(':', 1)
if key and value:
profile[key] = value.strip()
return profile
def validate_json(profile):
"""
Load the json file and make sure it can be properly loaded.
"""
json_str = open(profile['json']).read()
try:
json.loads(json_str)
except ValueError as err:
profile['json_error'] = err
return False
else:
return True
def push_update(profile):
"""
Upload the new profile to the server via sftp.
"""
port = 22
paramiko.util.log_to_file('paramiko.log')
conn = paramiko.SSHClient()
conn.load_system_host_keys()
conn.set_missing_host_key_policy(paramiko.AutoAddPolicy())
if '@' in profile['remote']:
username, host = profile['remote'].split('@')
if 'rport' in profile:
port = int(profile['rport'])
if not 'ruser' in profile and not username:
return False
if 'prvkey' in profile:
if 'rsa' in profile['prvkey']:
pkey = paramiko.RSAKey.from_private_key_file(profile['prvkey'])
elif 'dsa' in profile['prvkey']:
pkey = paramiko.DSSKey.from_private_key_file(profile['prvkey'])
else:
pkey = None
if 'password' in profile:
password = profile['password']
else:
password = None
client = paramiko.Transport((host, port))
client.connect(username=username, password=password, pkey=pkey)
if not client.is_active or not client.is_authenticated:
print 'connect failed!'
return False
sftp = paramiko.SFTPClient.from_transport(client)
if not sftp.put(profile['json'], profile['remote path']):
print '\n\t[!] put file failed!'
return False
return True
def request_update(profile):
"""
Request that hackerhub update the profile.
"""
user = urllib.quote_plus(profile['user'])
req = "http://www.hackerhub.org/r/%s" % user
res = urllib2.urlopen(req)
res.read()
if not 200 == res.getcode():
profile['http_status'] = res.getcode()
return False
else:
return True
def usage():
"""
Print a usage message and exit.
"""
print "usage: %s -hv [-f config] <json profile>" % (
os.path.basename(sys.argv[0]))
print " -h show this message"
print " -v show the version number and exit"
print " -f config load the specified config file"
print
exit(0)
def main(args):
"""
Parse command line options, load the configuration, validate the JSON
profile, and push the update.
"""
configfile = None
opts, args = getopt.getopt(args, 'hv')
for opt, arg in opts:
opt = opt.strip('-')
if 'h' == opt:
usage()
elif 'v' == opt:
print "%s version %s" % (os.path.basename(sys.argv[0]), VERSION)
exit(0)
elif 'f' == opt:
configfile = arg
if not configfile:
configfile = os.path.join(os.getenv('HOME'), '.config', 'hhup.conf')
print '[+] attempting to update hackerhub profile...\n'
print '[+] checking config file... ',
profile = check_config(configfile)
if not profile:
print 'FAILED!'
return
else:
print 'ok!'
print '[+] validating json profile... ',
profile['json'] = args[0]
did_validate = validate_json(profile)
if not did_validate:
print 'malformed json!\n\t%s' % profile['json_error']
return
print 'ok!'
print '[+] push update to server... ',
did_push = push_update(profile)
if not did_push:
print 'failed!'
return
print 'ok!'
print '[+] requesting profile update... ',
did_update = request_update(profile)
if not did_update:
print 'failed!\n\t[!] request returned: %d' % profile['http_status']
print 'ok!'
print '\n[+] successfully updated profile.'
if '__main__' == __name__:
main(sys.argv[1:])
"""
<ono-sendai: ~/code/the_basement> $ pylint hhup.py
No config file found, using default configuration
Report
======
141 statements analysed.
Messages by category
--------------------
+-----------+-------+---------+-----------+
|type |number |previous |difference |
+===========+=======+=========+===========+
|convention |0 |0 |= |
+-----------+-------+---------+-----------+
|refactor |0 |0 |= |
+-----------+-------+---------+-----------+
|warning |0 |0 |= |
+-----------+-------+---------+-----------+
|error |0 |0 |= |
+-----------+-------+---------+-----------+
Global evaluation
-----------------
Your code has been rated at 10.00/10 (previous run: 10.00/10)
Raw metrics
-----------
+----------+-------+------+---------+-----------+
|type |number |% |previous |difference |
+==========+=======+======+=========+===========+
|code |140 |64.52 |140 |= |
+----------+-------+------+---------+-----------+
|docstring |36 |16.59 |36 |= |
+----------+-------+------+---------+-----------+
|comment |12 |5.53 |12 |= |
+----------+-------+------+---------+-----------+
|empty |29 |13.36 |28 |+1.00 |
+----------+-------+------+---------+-----------+
Statistics by type
------------------
+---------+-------+-----------+-----------+------------+---------+
|type |number |old number |difference |%documented |%badname |
+=========+=======+===========+===========+============+=========+
|module |1 |1 |= |100.00 |0.00 |
+---------+-------+-----------+-----------+------------+---------+
|class |0 |0 |= |0 |0 |
+---------+-------+-----------+-----------+------------+---------+
|method |0 |0 |= |0 |0 |
+---------+-------+-----------+-----------+------------+---------+
|function |8 |8 |= |100.00 |0.00 |
+---------+-------+-----------+-----------+------------+---------+
Duplication
-----------
+-------------------------+------+---------+-----------+
| |now |previous |difference |
+=========================+======+=========+===========+
|nb duplicated lines |0 |0 |= |
+-------------------------+------+---------+-----------+
|percent duplicated lines |0.000 |0.000 |= |
+-------------------------+------+---------+-----------+
External dependencies
---------------------
::
paramiko (hhup)
"""
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment