1#!/usr/bin/env python
2#
3# Copyright (C) 2013 Federico Ceratto and others, see AUTHORS file.
4# Released under GPLv3+ license, see LICENSE.txt
5#
6# Cork example web application
7#
8# The following users are already available:
9#  admin/admin, demo/demo
10
11import bottle
12from beaker.middleware import SessionMiddleware
13from cork import Cork
14from cork.backends import SQLiteBackend
15import logging
16
17logging.basicConfig(format='localhost - - [%(asctime)s] %(message)s', level=logging.DEBUG)
18log = logging.getLogger(__name__)
19bottle.debug(True)
20
21def populate_backend():
22    b = SQLiteBackend('example.db', initialize=True)
23    b.connection.executescript("""
24        INSERT INTO users (username, email_addr, desc, role, hash, creation_date) VALUES
25        (
26            'admin',
27            'admin@localhost.local',
28            'admin test user',
29            'admin',
30            'cLzRnzbEwehP6ZzTREh3A4MXJyNo+TV8Hs4//EEbPbiDoo+dmNg22f2RJC282aSwgyWv/O6s3h42qrA6iHx8yfw=',
31            '2012-10-28 20:50:26.286723'
32        );
33        INSERT INTO roles (role, level) VALUES ('special', 200);
34        INSERT INTO roles (role, level) VALUES ('admin', 100);
35        INSERT INTO roles (role, level) VALUES ('editor', 60);
36        INSERT INTO roles (role, level) VALUES ('user', 50);
37    """)
38    return b
39
40b = populate_backend()
41aaa = Cork(backend=b, email_sender='federico.ceratto@gmail.com', smtp_url='smtp://smtp.magnet.ie')
42
43
44
45app = bottle.app()
46session_opts = {
47    'session.cookie_expires': True,
48    'session.encrypt_key': 'please use a random key and keep it secret!',
49    'session.httponly': True,
50    'session.timeout': 3600 * 24,  # 1 day
51    'session.type': 'cookie',
52    'session.validate_key': True,
53}
54app = SessionMiddleware(app, session_opts)
55
56
57# #  Bottle methods  # #
58
59def postd():
60    return bottle.request.forms
61
62
63def post_get(name, default=''):
64    return bottle.request.POST.get(name, default).strip()
65
66
67@bottle.post('/login')
68def login():
69    """Authenticate users"""
70    username = post_get('username')
71    password = post_get('password')
72    aaa.login(username, password, success_redirect='/', fail_redirect='/login')
73
74@bottle.route('/user_is_anonymous')
75def user_is_anonymous():
76    if aaa.user_is_anonymous:
77        return 'True'
78
79    return 'False'
80
81@bottle.route('/logout')
82def logout():
83    aaa.logout(success_redirect='/login')
84
85
86@bottle.post('/register')
87def register():
88    """Send out registration email"""
89    aaa.register(post_get('username'), post_get('password'), post_get('email_address'))
90    return 'Please check your mailbox.'
91
92
93@bottle.route('/validate_registration/:registration_code')
94def validate_registration(registration_code):
95    """Validate registration, create user account"""
96    aaa.validate_registration(registration_code)
97    return 'Thanks. <a href="/login">Go to login</a>'
98
99
100@bottle.post('/reset_password')
101def send_password_reset_email():
102    """Send out password reset email"""
103    aaa.send_password_reset_email(
104        username=post_get('username'),
105        email_addr=post_get('email_address')
106    )
107    return 'Please check your mailbox.'
108
109
110@bottle.route('/change_password/:reset_code')
111@bottle.view('password_change_form')
112def change_password(reset_code):
113    """Show password change form"""
114    return dict(reset_code=reset_code)
115
116
117@bottle.post('/change_password')
118def change_password():
119    """Change password"""
120    aaa.reset_password(post_get('reset_code'), post_get('password'))
121    return 'Thanks. <a href="/login">Go to login</a>'
122
123
124@bottle.route('/')
125def index():
126    """Only authenticated users can see this"""
127    aaa.require(fail_redirect='/login')
128    return 'Welcome! <a href="/admin">Admin page</a> <a href="/logout">Logout</a>'
129
130
131@bottle.route('/restricted_download')
132def restricted_download():
133    """Only authenticated users can download this file"""
134    aaa.require(fail_redirect='/login')
135    return bottle.static_file('static_file', root='.')
136
137
138@bottle.route('/my_role')
139def show_current_user_role():
140    """Show current user role"""
141    session = bottle.request.environ.get('beaker.session')
142    print "Session from simple_webapp", repr(session)
143    aaa.require(fail_redirect='/login')
144    return aaa.current_user.role
145
146
147# Admin-only pages
148
149@bottle.route('/admin')
150@bottle.view('admin_page')
151def admin():
152    """Only admin users can see this"""
153    aaa.require(role='admin', fail_redirect='/sorry_page')
154    return dict(
155        current_user=aaa.current_user,
156        users=aaa.list_users(),
157        roles=aaa.list_roles()
158    )
159
160
161@bottle.post('/create_user')
162def create_user():
163    try:
164        aaa.create_user(postd().username, postd().role, postd().password)
165        return dict(ok=True, msg='')
166    except Exception, e:
167        return dict(ok=False, msg=e.message)
168
169
170@bottle.post('/delete_user')
171def delete_user():
172    try:
173        aaa.delete_user(post_get('username'))
174        return dict(ok=True, msg='')
175    except Exception, e:
176        print repr(e)
177        return dict(ok=False, msg=e.message)
178
179
180@bottle.post('/create_role')
181def create_role():
182    try:
183        aaa.create_role(post_get('role'), post_get('level'))
184        return dict(ok=True, msg='')
185    except Exception, e:
186        return dict(ok=False, msg=e.message)
187
188
189@bottle.post('/delete_role')
190def delete_role():
191    try:
192        aaa.delete_role(post_get('role'))
193        return dict(ok=True, msg='')
194    except Exception, e:
195        return dict(ok=False, msg=e.message)
196
197
198# Static pages
199
200@bottle.route('/login')
201@bottle.view('login_form')
202def login_form():
203    """Serve login form"""
204    return {}
205
206
207@bottle.route('/sorry_page')
208def sorry_page():
209    """Serve sorry page"""
210    return '<p>Sorry, you are not authorized to perform this action</p>'
211
212
213# #  Web application main  # #
214
215def main():
216
217    # Start the Bottle webapp
218    bottle.debug(True)
219    bottle.run(app=app, quiet=False, reloader=False)
220
221if __name__ == "__main__":
222    main()
223