TinyUrl clone
posted in programming by jon on 2007-07-10
Yesterday, while listening to lugradio, I heard Aq mention someone creating a tinyurl clone that allowed you to specify your own link "handle" to reference the url (tinyurl uses a random alphanumeric id). That got me thinking about the usefulness of having my own personal url aliasing service. I use del.icio.us to manage my bookmarks, but I can definitely see the use-case for named, shortcut bookmarks for things of particular interest.
My second thought was how trivial this would be to implement in django. Result: 44 lines of code and 75 lines of html later, and I have my own tinyurl service. You can see the working implementation here (disclaimer: don't assume it's remotely bug-free, secure, or well implemented.)
the model
from django.db import models
class Link(models.Model):
handle = models.CharField(maxlength=255,unique=True)
url = models.CharField(maxlength=255)
def __str__(self):
return self.handle
class Admin:
pass
The views
from django.http import HttpResponse, HttpResponseRedirect, Http404, HttpResponseNotFound
from django.shortcuts import render_to_response, get_object_or_404
from django.newforms import form_for_model
from django.utils import simplejson
from jonallie.tinylink.models import Link
def index(request):
LinkForm = form_for_model(Link)
message = ""
if request.method == 'POST':
form = LinkForm(request.POST)
if form.is_valid():
form.save()
message = "Form saved: access your new link at http://jonallie.com/tinylink/go/%s" % form.data['handle']
form = LinkForm()
else:
message = "Form NOT saved. Check errors below"
else:
form = LinkForm()
return render_to_response('tinylink/index.html',{'form':form,'message':message})
def redirect(request,link_handle):
link = get_object_or_404(Link, handle=link_handle)
return HttpResponseRedirect(link.url)
def check_handle(request,link_handle):
link = get_object_or_404(Link,handle=link_handle)
return HttpResponse(simplejson.dumps({'handle':link.handle}), mimetype='application/javascript')
Permanent Link
Password expiration notification in python
posted in programming by jon on 2007-06-08
I've seen several scripts to notify users of impending AD password expiration, but nearly all of them are vbs. Here's a script to accomplish the same goal using python-ldap.
#!/usr/bin/env python
"""
password_notify.py:
provides notification of active directory password expiration via email
"""
import sys
import time
import smtplib
import ldap
import syslog
ldaphost = 'HOST'
ldapbase = 'SEARCHBASE'
ldapfilter = '(&(objectCategory=person)(objectClass=user)(!(pwdLastSet=0)))'
ldapuser = 'USER'
ldappass = 'PASS'
mailhost = 'MAILHOST'
from_address = 'donotreply@YOURDOMAIN.COM'
expiry_days = 90
notify_days = 5 # days before expiration to start notification
msg_template = """\
To: %s
From: %s
Subject: Password Expiration Notification
This message is to notify you that your directory password will expire
%s . Please change it before that time using one of the following methods:
1.) Reboot or logoff/logon your PC and follow the password change prompts.
2.) If you're a Webmail user, you can change your password from within webmail by choosing
'Options' from the menu on the left, then clicking the 'Change Password' button
near the bottom of the page. In the popup window that appears, type 'DOMAIN' (no quotes)
into the field labled 'Domain', and populate the rest of the fields with your username,
old password, and new password twice.
3.) If you're currently logged into your PC _as yourself_, you can change your password
by pressing Ctrl-Alt-Del, and clicking the 'Change Password...' button
[ password guidelines here ]
You'll continue to receive this message each day until your password expires,
or until you reset it using one of the procedures above.
Thanks,
The Helpdesk
"""
def exit_with_error(errorstr, errno=2):
"""Print error to screen and exit"""
print errorstr
sys.exit(2)
def wintime_to_epoch(wintime):
"""Convert windows time stamps to epoch seconds"""
return (int(wintime)-116444736000000000)/10000000
def epoch_to_wintime(epoch_secs):
"""Convert unix epoc seconds to windows time units"""
return (int(epoch_secs)*10000000)+116444736000000000
cur_win_time = epoch_to_wintime(time.time())
# explicitly open a syslog connection
syslog.openlog("password_notify.py")
# open a connection to the mail server
smail = smtplib.SMTP(mailhost)
# attempt to establish a connection to the ldap server
try:
conn = ldap.open(ldaphost)
conn.simple_bind_s(ldapuser, ldappass)
except:
syslog.syslog("Error opening connection to %s" % ldaphost)
exit_with_error("Ldap connection error")
# grab the list of "person" objects from AD
try:
users = conn.search_st(ldapbase, ldap.SCOPE_SUBTREE, ldapfilter)
except:
syslog.syslog("Error retreiving userlist using filter: %s" % ldapfilter)
exit_with_error("LDAP search error")
for user in users:
try:
name = user[1]['cn'][0]
mail = user[1]['mail'][0]
last_set = long(user[1]['pwdLastSet'][0])
expires_win = last_set + expiry_days*24*60*60*10000000
notify_win = expires_win - notify_days*24*60*60*10000000
except:
continue
if cur_win_time > notify_win and cur_win_time < expires_win:
syslog.syslog("Sending notification to: %s" % mail)
smail.sendmail(from_address,mail,msg_template % (mail, from_address,time.ctime(wintime_to_epoch(expires_win))))
sys.exit(0)
Permanent Link
Better net-bridge script
posted in programming by jon on 2007-04-15
I recently set up an openvpn server on Ubuntu LTS using bridged networking and was really dissatisfied with the sample scripts for bringing the bridge adapter up/down. The script below is what I came up with instead:
# startup script to establish bridged network
# based on sambple scripts from openvpn
function exit_with_error {
echo $*
exit 2
}
# bridged interface
br="br0"
# tap interface
tap="tap0"
# physical address
eth="eth0"
eth_addr="172.16.1.28"
eth_mask="255.255.0.0"
eth_cast="172.16.255.255"
eth_gw="172.16.32.2"
case "$1" in
"start")
echo "Starting bridged networking"
# create tunnel
/usr/sbin/openvpn --mktun --dev $tap
[ "$?" != "0" ] && exit_with_error "Failed to create tunnel"
/usr/sbin/brctl addbr $br
/usr/sbin/brctl addif $br $eth
/usr/sbin/brctl addif $br $tap
[ "$?" != "0" ] && exit_with_error "Failed to create bridge"
/sbin/ifconfig $tap 0.0.0.0 promisc up || exit_with_error "Failed to configure $tap"
/sbin/ifconfig $eth 0.0.0.0 promisc up || exit_with_error "Falied to configure $eth"
/sbin/ifconfig $br $eth_addr netmask $eth_mask broadcast $eth_cast || exit_with_error "Failed to configure $br"
#/sbin/ifconfig $br up
# add default route
/sbin/route add -net default gw $eth_gw
# create iptables rules
/sbin/iptables -A INPUT -i $br -j ACCEPT
/sbin/iptables -A INPUT -i $tap -j ACCEPT
/sbin/iptables -A FORWARD -i $br -j ACCEPT
/sbin/iptables -A FORWARD -i $tap -j ACCEPT
;;
"stop")
echo "Stopping bridged networking"
# bring down bridge
/sbin/ifconfig $br down
# tear down bridge
/usr/sbin/brctl delbr $br
/usr/sbin/openvpn --rmtun --dev $tap
# reset address of eth0
/sbin/ifconfig $eth $eth_addr netmask $eth_mask broadcast $eth_cast
#/sbin/ifconfig $eth up
/sbin/route add -net default gw $eth_gw
# flush iptables rules
/sbin/iptables -F INPUT
/sbin/iptables -F FORWARD
;;
*)
echo "Usage: $0 {start|stop}" >&2
exit 1
;;
esac
exit 0
Permanent Link
MySQL Backup script with encryption
posted in programming by jon on 2007-02-27
I recently rewrote the script that handles our mysql backups, and thought someone else might find it useful. It includes support for optional bzip compression of the dump files, and optional encryption using openssl. It's available for download here or you can see it inline after the jump
#!/bin/bash
##################################################################
# sqlbackup.sh:
# rolling sqlbackups using mysqldump
#
#
# This script will dump mysql databases to a specified directory
# with optional encrytion.
#
#
###################################################################
# program options
backup_dir=/backup
backup_age=28
use_encryption=0
use_bzip=0
nice_level=19
mysql_user=root
mysql_passfile=/etc/keys/.mysqlpass
openssl_passfile=/etc/keys/.opensslpass
date=`date +%Y%m%d`
exten=".sql"
logfile=/var/log/mysqlbackup.log
mysqldump_options=" --all-databases --single-transaction --quote-names "
# program paths
mysqldump=/usr/bin/mysqldump
openssl=/usr/bin/openssl
bzip2=/usr/bin/bzip2
# usage summary
function usage {
echo "
sqlbackup.sh:
creates backups of sql databases using the mysqldump command,
including optional encryption, bzip2 compression, and
variable backup aging
Usage: sqlbackup.sh [OPTIONS]
Options:
-h : display this help
-b <dir> : select directory to store backups. This directory
will not be created.
-e : enable encryption using openssl's des3 encryption.
-k <file> : specify file containing password to be used for encryption
This defaults to /etc/keys/.opensslpass
-u <user> : username to use for connecting to MySQL
-p </user></file><file> : specifies a file containing a password to be used for
connecting to MySQL
-z : enable bzip2 compression of backups
-a <days> : backup age in days. Backups older than this will be deleted
-n <level> : Specifies a nice level. This level will apply to all operations of the
backup. Defaults to 19 (very nice)
-l </level></days></file><file> : logfile path. Alternate path to a file for logging. Defaults
to '/var/log/mysqlbackup.log'
"
}
# echo a string to screen and log
function echo_and_log {
logstring="[ `date` ] : $*"
echo $logstring
echo $logstring >> $logfile
}
# print error to screen and exit
function exit_with_error {
echo_and_log "Error: $*"
echo_and_log "Exiting with code 1"
exit 1
}
# funtion to delete old backups
function clean_backups {
find $backup_dir -type f -mtime +${backup_age} -exec rm -f {} \;
[ "$?" == "0" ] || echo_and_log "Error encountered when cleaning old backups from $backup_dir"
}
# process command line options
while getopts ":hb:ek:u:p:za:n:l:" opt
do
case $opt in
b) backup_dir=$OPTARG ;;
e) use_encryption=1 ;;
k) openssl_passfile=$OPTARG ;;
u) mysql_user=$OPTARG ;;
p) mysql_passfile=$OPTARG ;;
z) use_bzip=1 ;;
a) backup_age=$OPTARG ;;
n) nice_level=$OPTARG ;;
l) logfile=$OPTARG ;;
h) usage
exit 0 ;;
\?) echo "unknown option"
usage
exit 1 ;;
*) usage
exit 1 ;;
esac
done
# set umask
umask 0077
# start processing
echo_and_log "Starting MySQL backup: encryption = $use_encryption, bzip = $use_bzip, backup directory = $backup_dir"
# error checking and validation
[ -d $backup_dir ] || exit_with_error "Backup directory '$backup_dir' does not exist"
[ -n "$logfile" ] || exit_with_error "No log file specified."
[ -f $mysql_passfile ] || exit_with_error "MySQL password file '$mysql_passfile' does not exist"
[ -x $mysqldump ] || exit_with_error "MySQLDump program at '$mysqldump' does not exist or is not executable"
# validate compression options
if [ "$use_bzip" == "1" ]
then
[ -x $bzip2 ] || exit_with_error "Bzip2 program at '$bzip2' does not exist or is not executable"
bzip2_command=" | nice -n $nice_level $bzip2 "
exten="${exten}.bz2"
fi
# validate encryption options
if [ "$use_encryption" == "1" ]
then
[ -x $openssl ] || exit_with_error "OpenSSL program at '$openssl' does not exist or is not executable"
[ -f $openssl_passfile ] || exit_with_error "OpenSSL password file '$openssl_passfile' does not exist"
openssl_command=" | nice -n $nice_level $openssl des3 -salt -pass file:${openssl_passfile} "
exten="${exten}.cryp"
fi
# call funtion to clean backup directory
clean_backups
# execute dump command
command="$mysqldump $mysqldump_options --user=${mysql_user} --password=$(<$mysql_passfile) $bzip2_command $openssl_command > $backup_dir/mysqlbackup_${date}${exten}"
eval $command
[ "$?" == "0" ] || exit_with_error "MySQLDump command returned non-zero error code. Backup not completed successfully"
# finish
echo_and_log "Backup complete"
exit 0
</file></dir>
Permanent Link
Joel Spolsky on What Design is All About
posted in programming by jon on 2006-01-29
Brilliant article from Joel Spolsky (of "Joel on Software" fame) about what 'design' actually is; what makes one design better than another; and why design is the most important thing to get right in a product.
Great Design
Permanent Link