Skip to content

Commit

Permalink
Merge github.com:hackerdojo/hd-signup
Browse files Browse the repository at this point in the history
Conflicts:
	main.py
  • Loading branch information
Brian Klug committed Jul 19, 2010
2 parents df5ee7f + 157e148 commit 6a20993
Show file tree
Hide file tree
Showing 4 changed files with 178 additions and 90 deletions.
1 change: 1 addition & 0 deletions TODO
@@ -0,0 +1 @@
- cron to get people to make hd account, every 2 weeks
3 changes: 3 additions & 0 deletions app.yaml
Expand Up @@ -14,5 +14,8 @@ handlers:
static_dir: static
- url: /remote_api
script: $PYTHON_LIB/google/appengine/ext/remote_api/handler.py
- url: /_km/.*
script: keymaster.py
login: admin
- url: .*
script: main.py
97 changes: 57 additions & 40 deletions keymaster.py
@@ -1,44 +1,61 @@
from google.appengine.api import urlfetch, memcache
from google.appengine.ext import webapp
import urllib
from google.appengine.api import urlfetch, memcache, users
from google.appengine.ext import webapp, db
from google.appengine.ext.webapp import util
import os
try:
from Crypto.Cipher import ARC4
except ImportError:
# Just pass through in dev mode
class ARC4:
new = classmethod(lambda k,x: ARC4)
encrypt = classmethod(lambda k,x: x)
decrypt = classmethod(lambda k,x: x)

## Example usage
#
# def needs_key():
# key = keymaster.get('my-key')
# if key:
# # Do something with key
# else:
# keymaster.request('my-key')
#
# def main():
# application = webapp.WSGIApplication([
# ('/key/(.+)', keymaster.Handler({
# 'my-key': ('6f7e21711e29e6d4b4e64daceb2a7348', '2isy046g', needs_key),
# 'another-key': ('keymaster-hash', 'keymaster-secret', optional_key_arrival_callback),
# })),
# ], debug=True)

_keys = {}

def get(keyname):
return memcache.get(keyname, namespace='keymaster')
class Keymaster(db.Model):
secret = db.BlobProperty(required=True)

def request(keyname):
urlfetch.fetch('http://www.thekeymaster.org/%s' % _keys[keyname][0], method='POST', payload=urllib.urlencode({'secret': _keys[keyname][1]}), deadline=10)
@classmethod
def encrypt(cls, key_name, secret):
secret = ARC4.new(os.environ['APPLICATION_ID']).encrypt(secret)
k = cls.get_by_key_name(key_name)
if k:
k.secret = str(secret)
else:
k = cls(key_name=str(key_name), secret=str(secret))
return k.put()

class _Handler(webapp.RequestHandler):
def get(self, keyname):
request(keyname)

def post(self, keyname):
key = self.request.get('key')
if key:
memcache.set(keyname, key, namespace='keymaster')
if len(_keys[keyname]) > 2:
_keys[keyname][2]()
@classmethod
def decrypt(cls, key_name):
k = cls.get_by_key_name(str(key_name))
if not k:
raise Exception("Keymaster has no secret for %s" % key_name)
return ARC4.new(os.environ['APPLICATION_ID']).encrypt(k.secret)

def get(key):
return Keymaster.decrypt(key)

class KeymasterHandler(webapp.RequestHandler):
@util.login_required
def get(self):
if users.is_current_user_admin():
self.response.out.write("""<html><body><form method="post">
<input type="text" name="key" /><input type="text" name="secret" /><input type="submit" /></form></body></html>""")
else:
self.redirect('/')

def post(self):
if users.is_current_user_admin():
Keymaster.encrypt(self.request.get('key'), self.request.get('secret'))
self.response.out.write("Saved: %s" % Keymaster.decrypt(self.request.get('key')))
else:
self.redirect('/')

def main():
application = webapp.WSGIApplication([
('/_km/key', KeymasterHandler),
],debug=True)
util.run_wsgi_app(application)

if __name__ == '__main__':
main()

def Handler(keys):
global _keys
_keys = keys
return _Handler
167 changes: 117 additions & 50 deletions main.py
Expand Up @@ -6,6 +6,7 @@
from google.appengine.ext.webapp import template
from django.utils import simplejson
from pprint import pprint
from datetime import datetime, date, time
import logging
import spreedly
import keymaster
Expand All @@ -14,21 +15,20 @@

APP_NAME = 'hd-signup'
EMAIL_FROM = "Dojo Signup <no-reply@%s.appspotmail.com>" % APP_NAME
DAYS_FOR_KEY = 60

try:
is_dev = os.environ['SERVER_SOFTWARE'].startswith('Dev')
except:
is_dev = False

import keys

if is_dev:
SPREEDLY_ACCOUNT = 'hackerdojotest'
SPREEDLY_APIKEY = keys.hackerdojotest
SPREEDLY_APIKEY = keymaster.get('spreedly:hackerdojotest')
PLAN_IDS = {'full': '1957'}
else:
SPREEDLY_ACCOUNT = 'hackerdojo'
SPREEDLY_APIKEY = keys.hackerdojo
SPREEDLY_APIKEY = keymaster.get('spreedly:hackerdojo')
PLAN_IDS = {'full': '1987', 'hardship': '2537', 'supporter': '1988', 'family': '3659', 'minor': '3660'}

is_prod = not is_dev
Expand All @@ -39,7 +39,7 @@ def fetch_usernames(use_cache=True):
if usernames and use_cache:
return usernames
else:
resp = urlfetch.fetch('http://hackerdojo-domain.appspot.com/users', deadline=10)
resp = urlfetch.fetch('http://domain.hackerdojo.com/users', deadline=10)
if resp.status_code == 200:
usernames = [m.lower() for m in simplejson.loads(resp.content)]
if not memcache.set('usernames', usernames, 3600*24):
Expand All @@ -55,6 +55,7 @@ class Membership(db.Model):
status = db.StringProperty() # None, active, suspended
referrer = db.StringProperty()
username = db.StringProperty()
rfid_tag = db.StringProperty()

spreedly_token = db.StringProperty()

Expand Down Expand Up @@ -135,8 +136,6 @@ def get(self, hash):
m.put()
self.redirect(users.create_logout_url('/success/%s' % hash))
else:
if not keymaster.get('api-secret'):
keymaster.request('api-secret')
message = self.request.get('message')
p = re.compile(r'[^\w]')
username = '.'.join([p.sub('', m.first_name), p.sub('', m.last_name)]).lower()
Expand All @@ -157,37 +156,32 @@ def post(self, hash):
elif len(password) < 6:
self.redirect(self.request.path + "?message=Password must be 6 characters or longer")
else:
if not keymaster.get('api-secret'):
self.redirect(self.request.path + "?message=There was a caching error, please try again.")
m = Membership.all().filter('hash =', hash).get()

if m and m.spreedly_token:
try:
resp = urlfetch.fetch('http://domain.hackerdojo.com/users', method='POST', payload=urllib.urlencode({
'username': username,
'password': password,
'first_name': m.first_name,
'last_name': m.last_name,
'secret': keymaster.get('api@hackerdojo.com'),
}), deadline=10)
out = resp.content
except urlfetch.DownloadError, e:
out = str(e)

usernames = fetch_usernames(False)
if username in usernames:
m.username = username
m.put()
self.redirect('/success/%s?email' % hash)
else:
m = Membership.all().filter('hash =', hash).get()

if m.spreedly_token:
try:
resp = urlfetch.fetch('http://hackerdojo-domain.appspot.com/users', method='POST', payload=urllib.urlencode({
'username': username,
'password': password,
'first_name': m.first_name,
'last_name': m.last_name,
'secret': keymaster.get('api-secret'),
}), deadline=10)
if 'try again' in resp.content:
self.redirect(self.request.path + "?message=There was a caching error, please try again.")
return
except urlfetch.DownloadError:
pass

usernames = fetch_usernames(False)
if username in usernames:
m.username = username
m.put()
self.redirect('/success/%s?email' % hash)
else:
mail.send_mail(sender=EMAIL_FROM,
to="Jeff Lindsay <progrium@gmail.com>",
subject="Error creating account",
body=resp.content if m.spreedly_token else "Attempt to make user without paying: " + self.request.remote_addr)
self.redirect(self.request.path + "?message=There was a problem creating your account. Please contact an admin.")
mail.send_mail(sender=EMAIL_FROM,
to="Jeff Lindsay <progrium@gmail.com>",
subject="Error creating account for %s" % username,
body=out if m.spreedly_token else "Attempt to make user without paying: " + self.request.remote_addr)
self.redirect(self.request.path + "?message=There was a problem creating your account. Please contact an admin.")


class SuccessHandler(webapp.RequestHandler):
Expand Down Expand Up @@ -238,18 +232,27 @@ def post(self, ids=None):
subscriber = s.subscriber_details(sub_id=int(id))
member = Membership.get_by_id(int(subscriber['customer-id']))
if member:
if member.status == 'paypal':
mail.send_mail(sender=EMAIL_FROM,
to="PayPal <paypal@hackerdojo.com>",
subject="Please cancel PayPal subscription for %s" % member.full_name(),
body=member.email)
member.status = 'active' if subscriber['active'] == 'true' else 'suspended'
member.spreedly_token = subscriber['token']
member.plan = subscriber['feature-level'] or member.plan
member.email = subscriber['email']
member.put()
if member.status == 'paypal':
mail.send_mail(sender=EMAIL_FROM,
to="PayPal <paypal@hackerdojo.com>",
subject="Please cancel PayPal subscription for %s" % member.full_name(),
body=member.email)
member.status = 'active' if subscriber['active'] == 'true' else 'suspended'
member.spreedly_token = subscriber['token']
member.plan = subscriber['feature-level'] or member.plan
member.email = subscriber['email']
member.put()

self.response.out.write("ok")

class LinkedHandler(webapp.RequestHandler):
def get(self):
self.response.out.write(simplejson.dumps([m.username for m in Membership.all().filter('username !=', None)]))

class SuspendedHandler(webapp.RequestHandler):
def get(self):
self.response.out.write(simplejson.dumps([[m.fullname(), m.username] for m in Membership.all().filter('status =', 'suspended')]))

class CleanupHandler(webapp.RequestHandler):
def get(self):
self.post()
Expand All @@ -266,17 +269,81 @@ def post(self):
subject="Recent almost members",
body='\n'.join(deleted_emails))

class KeyHandler(webapp.RequestHandler):
def get(self):
user = users.get_current_user()
if not user:
self.redirect(users.create_login_url('/key'))
return
else:
account = Membership.all().filter('email =', user.email()).get()
if not account or not account.spreedly_token:
error = "<p>It appears that you have an account on @hackerdojo.com, but you do not have a corresponding account in the signup application.</p><p>How to remedy:</p><ol><li>If you <b>are not</b> in the Spreedly system yet, <a href=\"/\">sign up</a> now.</li><li>If you <b>are</b> in Spreedly already, please contact <a href=\"mailto:signupops@hackerdojo.com?Subject=Spreedly+account+not+linked+to+hackerdojo+account\">signupops@hackerdojo.com</a>.</li></ol>"
self.response.out.write(template.render('templates/error.html', locals()))
return
if account.status != "active":
url = "https://spreedly.com/"+SPREEDLY_ACCOUNT+"/subscriber_accounts/"+account.spreedly_token
error = "<p>Your Spreedly account status does not appear to me marked as active. This might be a mistake, in which case we apologize. <p>To investigate your account, you may go here: <a href=\""+url+"\">"+url+"</a> <p>If you believe this message is in error, please contact <a href=\"mailto:signupops@hackerdojo.com?Subject=Spreedly+account+not+linked+to+hackerdojo+account\">signupops@hackerdojo.com</a></p>";
self.response.out.write(template.render('templates/error.html', locals()))
return
delta = datetime.utcnow() - account.created
if delta.days < DAYS_FOR_KEY:
error = "<p>You have been a member for "+str(delta.days)+" days. After "+str(DAYS_FOR_KEY)+" days you qualify for a key. Check back in "+str(DAYS_FOR_KEY-delta.days)+" days!</p>";
self.response.out.write(template.render('templates/error.html', locals()))
return
bc = BadgeChange.all().filter('username =', account.username).fetch(100)
self.response.out.write(template.render('templates/key.html', locals()))

def post(self):
user = users.get_current_user()
if not user:
self.redirect(users.create_login_url('/key'))
return
account = Membership.all().filter('email =', user.email()).get()
if not account or not account.spreedly_token or account.status != "active":
error = "<p>Error #1982, which should never happen."
self.response.out.write(template.render('templates/error.html', locals()))
return
rfid_tag = self.request.get('rfid_tag').strip()
description = self.request.get('description').strip()
if rfid_tag.isdigit():
if Membership.all().filter('rfid_tag =', rfid_tag).get():
error = "<p>That RFID tag is in use by someone else.</p>"
self.response.out.write(template.render('templates/error.html', locals()))
return
if not description:
error = "<p>Please enter a reason why you are associating a replacement RFID key. Please hit BACK and try again.</p>"
self.response.out.write(template.render('templates/error.html', locals()))
return
account.rfid_tag = rfid_tag
account.put()
bc = BadgeChange(rfid_tag = rfid_tag, username=account.username, description=description)
bc.put()
self.response.out.write(template.render('templates/key_ok.html', locals()))
return
else:
error = "<p>That RFID ID seemed invalid. Hit back and try again.</p>"
self.response.out.write(template.render('templates/error.html', locals()))
return

class BadgeChange(db.Model):
created = db.DateTimeProperty(auto_now_add=True)
rfid_tag = db.StringProperty()
username = db.StringProperty()
description = db.StringProperty()

def main():
application = webapp.WSGIApplication([
('/', MainHandler),
('/api/linked', LinkedHandler),
('/api/suspended', SuspendedHandler),
('/cleanup', CleanupHandler),
('/key', KeyHandler),
('/account/(.+)', AccountHandler),
('/upgrade/needaccount', NeedAccountHandler),
('/success/(.+)', SuccessHandler),
('/update', UpdateHandler),
('/key/(.+)', keymaster.Handler({
'api-secret': ('c94d981ca589581cd270439854f08679', '1w5q7v3h'),
})),], debug=True)
], debug=True)
wsgiref.handlers.CGIHandler().run(application)

if __name__ == '__main__':
Expand Down

0 comments on commit 6a20993

Please sign in to comment.