| 1 | ############################################################################ |
|---|
| 2 | # |
|---|
| 3 | # DSAGE: Distributed SAGE |
|---|
| 4 | # |
|---|
| 5 | # Copyright (C) 2006, 2007 Yi Qiang <yqiang@gmail.com> |
|---|
| 6 | # |
|---|
| 7 | # Distributed under the terms of the GNU General Public License (GPL) |
|---|
| 8 | # |
|---|
| 9 | # This code is distributed in the hope that it will be useful, |
|---|
| 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|---|
| 12 | # General Public License for more details. |
|---|
| 13 | # |
|---|
| 14 | # The full text of the GPL is available at: |
|---|
| 15 | # |
|---|
| 16 | # http://www.gnu.org/licenses/ |
|---|
| 17 | ############################################################################ |
|---|
| 18 | |
|---|
| 19 | import os |
|---|
| 20 | import random |
|---|
| 21 | import base64 |
|---|
| 22 | from glob import glob |
|---|
| 23 | |
|---|
| 24 | from twisted.trial import unittest |
|---|
| 25 | from twisted.spread import pb |
|---|
| 26 | from twisted.internet import reactor |
|---|
| 27 | from twisted.cred import portal, credentials |
|---|
| 28 | from twisted.conch.ssh import keys |
|---|
| 29 | |
|---|
| 30 | from sage.dsage.twisted.pb import Realm |
|---|
| 31 | from sage.dsage.server.server import DSageServer |
|---|
| 32 | from sage.dsage.twisted.pb import _SSHKeyPortalRoot |
|---|
| 33 | from sage.dsage.twisted.pb import PBClientFactory |
|---|
| 34 | from sage.dsage.twisted.pubkeyauth import PublicKeyCredentialsCheckerDB |
|---|
| 35 | from sage.dsage.database.jobdb import JobDatabaseSQLite |
|---|
| 36 | from sage.dsage.database.monitordb import MonitorDatabase |
|---|
| 37 | from sage.dsage.database.clientdb import ClientDatabase |
|---|
| 38 | from sage.dsage.errors.exceptions import AuthenticationError |
|---|
| 39 | |
|---|
| 40 | TEST_PUB_KEY = """ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAusOUk3wZof9orc7YuKZP/wxog2uAU5BsagK4lkHgdfBc+ZR3s+Rk+k6prvuNuUXIfn2A+UkPa0xmjtQnMlqClrZXXMHhDV8iXto/vM1BopF+Ja1Y+pCK2vRRZVsZsdzL7XqyVc+kstsgKWrrguNCMIuEyc37wcsgdd1PxPmuB8Mwm3YZmNRV6yEq8Qq3IprZHfBl5S6htmwTXt4VEzvJgX1PJBLg4BauJtLxeEzYgMLY4VG3buJ2VDwlqwVPO/oVZwK3uXifXtxVx6VJO4pKUBdDSyjudPQTHxogos+8scaClx0XMh0eM7xw92j4SpA+mtzXnAKM4CqCSFH3w+/LbQ== yqiang@six |
|---|
| 41 | """ |
|---|
| 42 | |
|---|
| 43 | TEST_PRIV_KEY = """-----BEGIN RSA PRIVATE KEY----- |
|---|
| 44 | MIIEoQIBAAKCAQEAusOUk3wZof9orc7YuKZP/wxog2uAU5BsagK4lkHgdfBc+ZR3 |
|---|
| 45 | s+Rk+k6prvuNuUXIfn2A+UkPa0xmjtQnMlqClrZXXMHhDV8iXto/vM1BopF+Ja1Y |
|---|
| 46 | +pCK2vRRZVsZsdzL7XqyVc+kstsgKWrrguNCMIuEyc37wcsgdd1PxPmuB8Mwm3YZ |
|---|
| 47 | mNRV6yEq8Qq3IprZHfBl5S6htmwTXt4VEzvJgX1PJBLg4BauJtLxeEzYgMLY4VG3 |
|---|
| 48 | buJ2VDwlqwVPO/oVZwK3uXifXtxVx6VJO4pKUBdDSyjudPQTHxogos+8scaClx0X |
|---|
| 49 | Mh0eM7xw92j4SpA+mtzXnAKM4CqCSFH3w+/LbQIBIwKCAQBFXpZFaJvOdM8b/F8g |
|---|
| 50 | A0JIyhgw0ChZjWoYvy6eNbnFZ+gE7gCTRjQidP0yXW8njvKyo6Tu4J9TvUqqFEkS |
|---|
| 51 | s+dcjN6ey6tcvO+CURBcEblLAtcVTwPKx/kPf1FuyhDbqcgWYMXlW8DUt8oeAyRG |
|---|
| 52 | juyy8f4fEf5sjUaSLaFJKYnIXc4UNTiBckcM0ffk+achcuikZKtoJ7scJVHRzS// |
|---|
| 53 | cz2Y/L7ma/JOASDbybwIvnZ+C+iiOrDSwCgqBWw0R48p80W3kqrPjzxMOEVtI5ON |
|---|
| 54 | epaM+MqWWMvpjoMBWC0ZpGl35QOLfjFjFUXzdWENfWuyjE7a6iu3S5MOBk8E21zD |
|---|
| 55 | OV1LAoGBAO7Df7zVUXTBNunIOlxu1fqkkAXrPUWSr9hod/kiCDM5Gtjn1519xYM7 |
|---|
| 56 | IYtA6lipIVLVTPZlbTUd/QGxDXLloG/Ll7f0aQXz24PyF4TsVxp0cNURrsViaYEb |
|---|
| 57 | QCmqFqCd5fkrPGy5Qi11S+cgco2u2h6ZoyHEc4VxvMml+fv/JeG9AoGBAMg/GE56 |
|---|
| 58 | sau4oUNKMDZGBac07uFD/IvERA/o7ul8hyuFHHRGBPIaJr7DVzDkxC0h0DscK6nl |
|---|
| 59 | vq2xa54yI4JH3mAPpQyCjfcT8yqcBpmDhq44udaksno7RfY7Twdk6sUMKTEbtEzk |
|---|
| 60 | PzRqDSIVGuYxKK4p/lPWYRkNl9AyzlDK4LNxAoGBAIGdU/jLkp53hDXEd3QBp1wt |
|---|
| 61 | csFicbgNzSxV9/xFrK4Xr30QJJdS55Bh7aNdwQuPA3YcBTVNAMUQR4STUHGSmOw7 |
|---|
| 62 | UlyL/n+TAiMOZIn8pFAwlQXzqATAZSjUR2cTMNrZX5XkRV+XxNbZRnYnjqSvYHcC |
|---|
| 63 | 8ihGEtNpoP/AgGQ6DUAHAoGAESn6xOXx+MauvJ/1gP6wBwSJgQXT0Xc5CK2Q0i8+ |
|---|
| 64 | yTdLlPAPDW/0sUPxh9kYISAnyo1i1AxgzQ81HDAu7ejnLM4kFwPgSGDLszHx7+az |
|---|
| 65 | xcpZEmXjaZANT56vAKJAAkLe9ZSpDeetpWgtAuvdu/WVxckVzKv5sbC1Pbs2QXB5 |
|---|
| 66 | qPsCgYA1GcxkJk70lyMX9W1eZ97jorqeo1+wN0IM0dAtKhB+3OCLQXEaAibtlUDL |
|---|
| 67 | vfFTV+I4/tOelzdrADsjxugr1RMbfBqocfaI3xJpjwNXNtqgJk8u50aFz0SlJu6y |
|---|
| 68 | 4tizRIkoVlcZYqs9qtxEzHKryAecM8EcumKBJEJZdtdCV0O6vw== |
|---|
| 69 | -----END RSA PRIVATE KEY----- |
|---|
| 70 | """ |
|---|
| 71 | |
|---|
| 72 | RANDOM_DATA = ''.join([chr(i) for i in [random.randint(65, 123) for n in |
|---|
| 73 | range(500)]]) |
|---|
| 74 | |
|---|
| 75 | class PublicKeyCredentialsCheckerTest(unittest.TestCase): |
|---|
| 76 | def setUp(self): |
|---|
| 77 | self.jobdb = JobDatabaseSQLite(test=True) |
|---|
| 78 | self.monitordb = MonitorDatabase(test=True) |
|---|
| 79 | self.clientdb = ClientDatabase(test=True) |
|---|
| 80 | self.dsage_server = DSageServer(self.jobdb, |
|---|
| 81 | self.monitordb, |
|---|
| 82 | self.clientdb, |
|---|
| 83 | log_level=5) |
|---|
| 84 | self.realm = Realm(self.dsage_server) |
|---|
| 85 | self.p = _SSHKeyPortalRoot(portal.Portal(Realm(self.dsage_server))) |
|---|
| 86 | self.clientdb = ClientDatabase(test=True) |
|---|
| 87 | self.p.portal.registerChecker(PublicKeyCredentialsCheckerDB( |
|---|
| 88 | self.clientdb)) |
|---|
| 89 | self.client_factory = pb.PBServerFactory(self.p) |
|---|
| 90 | self.hostname = 'localhost' |
|---|
| 91 | self.r = reactor.listenTCP(0, self.client_factory) |
|---|
| 92 | self.port = self.r.getHost().port |
|---|
| 93 | |
|---|
| 94 | # public key authentication information |
|---|
| 95 | self.username = 'unit_test' |
|---|
| 96 | self.pubkey_file = TEST_PUB_KEY |
|---|
| 97 | self.privkey_file = TEST_PRIV_KEY |
|---|
| 98 | self.data = RANDOM_DATA |
|---|
| 99 | self.public_key_str = keys.getPublicKeyString(data=TEST_PUB_KEY) |
|---|
| 100 | self.private_key = keys.getPrivateKeyObject( |
|---|
| 101 | data=TEST_PRIV_KEY) |
|---|
| 102 | self.public_key = keys.getPublicKeyObject(self.public_key_str) |
|---|
| 103 | self.algorithm = 'rsa' |
|---|
| 104 | self.blob = keys.makePublicKeyBlob(self.public_key) |
|---|
| 105 | self.signature = keys.signData(self.private_key, self.data) |
|---|
| 106 | self.creds = credentials.SSHPrivateKey(self.username, |
|---|
| 107 | self.algorithm, |
|---|
| 108 | self.blob, |
|---|
| 109 | self.data, |
|---|
| 110 | self.signature) |
|---|
| 111 | pubkey = base64.encodestring(self.public_key_str).strip() |
|---|
| 112 | try: |
|---|
| 113 | self.clientdb.del_user(self.username) |
|---|
| 114 | self.clientdb.add_user(self.username, pubkey) |
|---|
| 115 | except: |
|---|
| 116 | self.clientdb.add_user(self.username, pubkey) |
|---|
| 117 | |
|---|
| 118 | def tearDown(self): |
|---|
| 119 | self.connection.disconnect() |
|---|
| 120 | self.jobdb._shutdown() |
|---|
| 121 | files = glob('*.db*') |
|---|
| 122 | for file in files: |
|---|
| 123 | os.remove(file) |
|---|
| 124 | return self.r.stopListening() |
|---|
| 125 | |
|---|
| 126 | def testLogin(self): |
|---|
| 127 | factory = PBClientFactory() |
|---|
| 128 | self.connection = reactor.connectTCP(self.hostname, self.port, |
|---|
| 129 | factory) |
|---|
| 130 | d = factory.login(self.creds, None) |
|---|
| 131 | d.addCallback(self._LoginConnected) |
|---|
| 132 | |
|---|
| 133 | return d |
|---|
| 134 | |
|---|
| 135 | def _LoginConnected(self, remoteobj): |
|---|
| 136 | self.assert_(isinstance(remoteobj, pb.RemoteReference)) |
|---|
| 137 | |
|---|
| 138 | def testBadLogin(self): |
|---|
| 139 | factory = PBClientFactory() |
|---|
| 140 | self.connection = reactor.connectTCP(self.hostname, |
|---|
| 141 | self.port, |
|---|
| 142 | factory) |
|---|
| 143 | self.assertRaises(TypeError, factory.login, None, None) |
|---|
| 144 | |
|---|
| 145 | def testBadLogin2(self): |
|---|
| 146 | factory = PBClientFactory() |
|---|
| 147 | self.connection = reactor.connectTCP(self.hostname, |
|---|
| 148 | self.port, |
|---|
| 149 | factory) |
|---|
| 150 | bad_creds = credentials.SSHPrivateKey('bad username', |
|---|
| 151 | self.algorithm, |
|---|
| 152 | self.blob, |
|---|
| 153 | self.data, |
|---|
| 154 | self.signature) |
|---|
| 155 | d = factory.login(bad_creds, None) |
|---|
| 156 | d.addErrback(self._BadLoginFailure) |
|---|
| 157 | |
|---|
| 158 | return d |
|---|
| 159 | |
|---|
| 160 | def _BadLoginFailure(self, failure): |
|---|
| 161 | self.assertEquals(failure.type, str(AuthenticationError)) |
|---|
| 162 | |
|---|
| 163 | def testBadLogin3(self): |
|---|
| 164 | factory = PBClientFactory() |
|---|
| 165 | self.connection = reactor.connectTCP(self.hostname, |
|---|
| 166 | self.port, |
|---|
| 167 | factory) |
|---|
| 168 | bad_creds = credentials.SSHPrivateKey(self.username, |
|---|
| 169 | self.algorithm, |
|---|
| 170 | None, |
|---|
| 171 | self.data, |
|---|
| 172 | self.signature) |
|---|
| 173 | |
|---|
| 174 | d = factory.login(bad_creds, None) |
|---|
| 175 | d.addErrback(self._BadLoginFailure) |
|---|
| 176 | |
|---|
| 177 | return d |
|---|
| 178 | |
|---|
| 179 | if __name__ == 'main': |
|---|
| 180 | unittest.main() |
|---|