| 1 | ##################################################################### |
|---|
| 2 | # Copyright (C) 2007 Alex Clemesha <clemesha@gmail.com> |
|---|
| 3 | # |
|---|
| 4 | # Distributed under the terms of the GNU General Public License (GPL) |
|---|
| 5 | # http://www.gnu.org/licenses/ |
|---|
| 6 | ##################################################################### |
|---|
| 7 | |
|---|
| 8 | import twist |
|---|
| 9 | import os |
|---|
| 10 | |
|---|
| 11 | from twisted.cred import portal, checkers, credentials, error as credError |
|---|
| 12 | from twisted.internet import protocol, defer |
|---|
| 13 | from zope.interface import Interface, implements |
|---|
| 14 | from twisted.web2 import iweb |
|---|
| 15 | from twisted.python import log |
|---|
| 16 | |
|---|
| 17 | |
|---|
| 18 | def user_type(avatarId): |
|---|
| 19 | if twist.notebook.user_is_admin(avatarId): |
|---|
| 20 | return 'admin' |
|---|
| 21 | return 'user' |
|---|
| 22 | |
|---|
| 23 | class PasswordDataBaseChecker(object): |
|---|
| 24 | implements(checkers.ICredentialsChecker) |
|---|
| 25 | credentialInterfaces = (credentials.IUsernamePassword,) |
|---|
| 26 | |
|---|
| 27 | def __init__(self, dbConnection): |
|---|
| 28 | self.dbConnection = dbConnection |
|---|
| 29 | |
|---|
| 30 | def queryDatabase(self, result): |
|---|
| 31 | if result: |
|---|
| 32 | avatarId = str(result[0][0]) |
|---|
| 33 | return avatarId #defer.succeed(avatarId) |
|---|
| 34 | else: |
|---|
| 35 | return checkers.ANONYMOUS #defer.succeed(checkers.ANONYMOUS) |
|---|
| 36 | |
|---|
| 37 | def requestAvatarId(self, credentials): |
|---|
| 38 | log.msg("=== requestAvatarId ===") |
|---|
| 39 | username = credentials.username |
|---|
| 40 | password = credentials.password |
|---|
| 41 | query = "SELECT avatarId FROM users WHERE avatarId = ? AND password = ?" |
|---|
| 42 | d = self.dbConnection.runQuery(query, (username, password)) |
|---|
| 43 | d.addCallback(self.queryDatabase) |
|---|
| 44 | #d.addErrback(self._failed) |
|---|
| 45 | return d |
|---|
| 46 | |
|---|
| 47 | class PasswordDictChecker(object): |
|---|
| 48 | implements(checkers.ICredentialsChecker) |
|---|
| 49 | credentialInterfaces = (credentials.IUsernamePassword,) |
|---|
| 50 | |
|---|
| 51 | def __init__(self, passwords): |
|---|
| 52 | "passwords: a dict-like object mapping usernames to passwords" |
|---|
| 53 | self.passwords = passwords |
|---|
| 54 | |
|---|
| 55 | def requestAvatarId(self, credentials): |
|---|
| 56 | log.msg("=== requestAvatarId ===") |
|---|
| 57 | username = credentials.username |
|---|
| 58 | log.msg("un: %s, pw: %s"%(credentials.username, credentials.password)) |
|---|
| 59 | if self.passwords.has_key(username): |
|---|
| 60 | log.msg("password.has_key(%s)"%username) |
|---|
| 61 | password = self.passwords[username] |
|---|
| 62 | if credentials.password == password: |
|---|
| 63 | return defer.succeed(username) |
|---|
| 64 | else: |
|---|
| 65 | log.msg("=== %s entered the wrong password" % username) |
|---|
| 66 | log.msg("=== Returning anonymous credentials.") |
|---|
| 67 | return defer.succeed(checkers.ANONYMOUS) |
|---|
| 68 | else: |
|---|
| 69 | log.msg("=== Returning anonymous credentials.") |
|---|
| 70 | return defer.succeed(checkers.ANONYMOUS) |
|---|
| 71 | |
|---|
| 72 | class PasswordFileChecker(PasswordDictChecker): |
|---|
| 73 | implements(checkers.ICredentialsChecker) |
|---|
| 74 | credentialInterfaces = (credentials.IUsernamePassword,) |
|---|
| 75 | |
|---|
| 76 | def __init__(self, password_file): |
|---|
| 77 | """ |
|---|
| 78 | INPUT: |
|---|
| 79 | password_file - file that contains passwords |
|---|
| 80 | |
|---|
| 81 | """ |
|---|
| 82 | |
|---|
| 83 | self.password_file = password_file |
|---|
| 84 | self.load_passwords() |
|---|
| 85 | |
|---|
| 86 | def load_passwords(self): |
|---|
| 87 | passwords = {} |
|---|
| 88 | if not os.path.exists(self.password_file): |
|---|
| 89 | open(self.password_file,'w').close() |
|---|
| 90 | f = open(self.password_file).readlines() |
|---|
| 91 | for line in f: |
|---|
| 92 | username, password, email, account_type = line.split(':') |
|---|
| 93 | password = password.strip() |
|---|
| 94 | passwords[username] = password |
|---|
| 95 | |
|---|
| 96 | self.passwords = passwords |
|---|
| 97 | |
|---|
| 98 | def add_user(self, username, password, email, account_type='user'): |
|---|
| 99 | self.check_username(username) |
|---|
| 100 | f = open(self.password_file, 'a') |
|---|
| 101 | s = '%s:%s:%s:%s\n' % (username, password, email, account_type) |
|---|
| 102 | f.writelines(s) |
|---|
| 103 | f.close() |
|---|
| 104 | |
|---|
| 105 | def check_username(self, username): |
|---|
| 106 | usernames = [] |
|---|
| 107 | f = open(self.password_file).readlines() |
|---|
| 108 | for line in f: |
|---|
| 109 | v = line.split(':') |
|---|
| 110 | usernames.append(v[0]) |
|---|
| 111 | if username in usernames: |
|---|
| 112 | raise ValueError('Username %s already exists' % username) |
|---|
| 113 | else: |
|---|
| 114 | return True |
|---|
| 115 | |
|---|
| 116 | def requestAvatarId(self, credentials): |
|---|
| 117 | self.load_passwords() |
|---|
| 118 | log.msg("=== requestAvatarId ===") |
|---|
| 119 | username = credentials.username |
|---|
| 120 | log.msg("un: %s, pw: %s"%(credentials.username, credentials.password)) |
|---|
| 121 | if self.passwords.has_key(username): |
|---|
| 122 | log.msg("password.has_key(%s)"%username) |
|---|
| 123 | password = self.passwords[username] |
|---|
| 124 | if credentials.password == password: |
|---|
| 125 | return defer.succeed(username) |
|---|
| 126 | else: |
|---|
| 127 | log.msg("=== %s entered the wrong password" % username) |
|---|
| 128 | log.msg("=== Returning anonymous credentials.") |
|---|
| 129 | return defer.succeed(checkers.ANONYMOUS) |
|---|
| 130 | else: |
|---|
| 131 | log.msg("=== Returning anonymous credentials.") |
|---|
| 132 | return defer.succeed(checkers.ANONYMOUS) |
|---|
| 133 | |
|---|
| 134 | class LoginSystem(object): |
|---|
| 135 | implements(portal.IRealm) |
|---|
| 136 | |
|---|
| 137 | def __init__(self, users): |
|---|
| 138 | self.users = users #empty, stored in database right now |
|---|
| 139 | # self.dbConnection = dbConnection |
|---|
| 140 | self.usersResources = {} #store created resource objects |
|---|
| 141 | self.kernels = {} #logged in users kernel connections. |
|---|
| 142 | self.logout = lambda: None #find a good use for logout |
|---|
| 143 | |
|---|
| 144 | def requestAvatar(self, avatarId, mind, *interfaces): |
|---|
| 145 | """ |
|---|
| 146 | Return a given Avatar depending on the avatarID. |
|---|
| 147 | |
|---|
| 148 | This approximatly boils down to, for a protected web site, |
|---|
| 149 | that given a username (avatarId, which could just be '()' for |
|---|
| 150 | an anonymous user) returned from a login page, |
|---|
| 151 | (which first went through a password check in requestAvatarId) |
|---|
| 152 | We serve up a given "web site" -> twisted resources, that depends |
|---|
| 153 | on the avatarId, (i.e. different permissions / view depending on |
|---|
| 154 | if the user is anonymous, regular, or an admin) |
|---|
| 155 | |
|---|
| 156 | """ |
|---|
| 157 | |
|---|
| 158 | from sage.server.notebook.twist import AnonymousToplevel, UserToplevel, AdminToplevel |
|---|
| 159 | log.msg("=== requestAvatar ===") |
|---|
| 160 | self.cookie = mind[0] |
|---|
| 161 | if iweb.IResource in interfaces: |
|---|
| 162 | if avatarId is checkers.ANONYMOUS: #anonymous user |
|---|
| 163 | log.msg("returning AnonymousResources") |
|---|
| 164 | rsrc = AnonymousToplevel(self.cookie, avatarId) |
|---|
| 165 | return (iweb.IResource, rsrc, self.logout) |
|---|
| 166 | elif user_type(avatarId) == 'user': |
|---|
| 167 | log.msg("returning User resources for %s" % avatarId) |
|---|
| 168 | self._mind = mind #mind = [cookie, request.args, segments] |
|---|
| 169 | self._avatarId = avatarId |
|---|
| 170 | rsrc = UserToplevel(self.cookie, avatarId) |
|---|
| 171 | return (iweb.IResource, rsrc, self.logout) |
|---|
| 172 | elif user_type(avatarId) == 'admin': |
|---|
| 173 | self._mind = mind #mind = [cookie, request.args, segments] |
|---|
| 174 | self._avatarId = avatarId |
|---|
| 175 | rsrc = AdminToplevel(self.cookie, avatarId) |
|---|
| 176 | return (iweb.IResource, rsrc, self.logout) |
|---|
| 177 | else: |
|---|
| 178 | raise KeyError("None of the requested interfaces is supported") |
|---|
| 179 | |
|---|
| 180 | def getUserResource(self, result): |
|---|
| 181 | ktype = str(result[0][0]) |
|---|
| 182 | kernelConnection = self.kernels[self.nbid] = kernel.KernelManager(ktype) |
|---|
| 183 | rsrc = resources.Root(self._avatarId, self.cookie, kernelConnection, self.dbConnection) |
|---|
| 184 | return (iweb.IResource, rsrc, self.logout) |
|---|