Created
October 3, 2011 12:01
-
-
Save kisom/1258960 to your computer and use it in GitHub Desktop.
update hackerhub profile
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# -*- coding: utf-8 -*- | |
# author: kyle isom <[email protected]> | |
# 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" | |
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