Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions orbit/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@

# duration of authentication token validity period
minutes_each_session_token_is_valid = 180

# length in bytes of amount of random data used to generate passwords
num_bytes_entropy_for_pw = 24
8 changes: 1 addition & 7 deletions orbit/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class Meta:

class User(BaseModel):
username = peewee.TextField(unique=True)
pwdhash = peewee.TextField()
pwdhash = peewee.TextField(null=True)
student_id = peewee.TextField(unique=True, null=True)


Expand All @@ -23,11 +23,5 @@ class Session(BaseModel):
expiry = peewee.FloatField()


class Registration(BaseModel):
student_id = peewee.TextField(unique=True)
username = peewee.TextField(unique=True)
password = peewee.TextField()


if __name__ == '__main__':
DB.create_tables(BaseModel.__subclasses__())
22 changes: 16 additions & 6 deletions orbit/hyperspace.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,15 @@ def do_change_password(args):
nou(args.username)


def do_reset_password(args):
need(args, u=True)
query = (db.User
.update({db.User.pwdhash: None})
.where(db.User.username == args.username))
if query.execute() < 1:
nou(args.username)


def do_delete_user(args):
need(args, u=True)
query = (db.User
Expand All @@ -65,15 +74,13 @@ def do_bcrypt_hash(args):


def do_newuser(args):
need(args, u=True, p=True)
new_hash = do_bcrypt_hash(args)
need(args, u=True)
new_hash = None
if args.password is not None:
new_hash = do_bcrypt_hash(args)
try:
db.User.create(username=args.username, pwdhash=new_hash,
student_id=args.studentid)
if args.studentid:
db.Registration.create(username=args.username,
password=args.password,
student_id=args.studentid)
except db.peewee.IntegrityError as e:
errx(f'cannot create user with duplicate field: "{e}"')

Expand Down Expand Up @@ -110,6 +117,9 @@ def hyperspace_main(raw_args):
actions.add_argument('-m', '--mutatepassword', action='store_const',
help='Change password for supplied username to supplied password', # NOQA: E501
dest='do', const=do_change_password)
actions.add_argument('-c', '--clearpassword', action='store_const',
help='clear password for supplied username so they canot login', # NOQA: E501
dest='do', const=do_reset_password)
actions.add_argument('-w', '--withdrawuser', action='store_const',
help='Delete ("withdraw") the supplied username',
dest='do', const=do_delete_user)
Expand Down
29 changes: 22 additions & 7 deletions orbit/radius.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
def check_credentials(username, password):
if not (user := db.User.get_or_none(db.User.username == username)):
return False
if not user.pwdhash:
return False
return bcrypt.checkpw(password.encode(), user.pwdhash.encode())


Expand Down Expand Up @@ -382,6 +384,22 @@ def handle_dashboard(rocket):
return handle_stub(rocket, ['dashboard in development, check back later'])


def find_creds_for_registration(student_id):
password = secrets.token_urlsafe(nbytes=config.num_bytes_entropy_for_pw)
salt = bcrypt.gensalt()
pwdhash = bcrypt.hashpw(password.encode(), salt).decode()

query = (db.User
.update({db.User.pwdhash: pwdhash})
.where((db.User.student_id == student_id) &
db.User.pwdhash.is_null())
.returning(db.User))
if (user := next(iter(query.execute()), None)):
return user.username, password

return None


def handle_register(rocket):
def form_respond():
return rocket.respond('''
Expand All @@ -396,18 +414,15 @@ def form_respond():
if not (student_id := rocket.body_args_query('student_id')):
rocket.msg('you must provide a student id')
return form_respond()
query = (db.Registration
.delete()
.where(db.Registration.student_id == student_id)
.returning(db.Registration))
if not (registration := next(iter(query.execute()), None)):
if not (creds := find_creds_for_registration(student_id)):
rocket.msg('no such student')
return form_respond()
username, password = creds
rocket.msg('welcome to the classroom')
return rocket.respond(f'''
<h1>Save these credentials, you will not be able to access them again</h1><br>
<h3>Username: {registration.username}</h3><br>
<h3>Password: {registration.password}</h3><br>''')
<h3>Username: {username}</h3><br>
<h3>Password: {password}</h3><br>''')


def handle_cgit(rocket):
Expand Down
92 changes: 42 additions & 50 deletions test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -80,50 +80,40 @@ ${DOCKER} volume export singularity_orbit-db > test/orbit_orig.tar

# Import an empty orbit db with no users or sessions
xxd -r <<- 'EOF' | gunzip | ${DOCKER} volume import singularity_orbit-db -
00000000: 1f8b 0800 0000 0000 0003 edda 5d4f d350 ............]O.P
00000010: 18c0 f196 b76e c826 5e2c 8d72 735c 24ba .....n.&^,.rs\$.
00000020: 2064 2881 186e 18d8 e8e2 1c32 ba04 6e5c d(..n.....2..n\
00000030: 4a56 a511 366c bb80 37c4 f151 fc28 c60f JV..6l..7..Q.(..
00000040: 665f f6d2 1554 bc40 49f3 ff25 5b7b 5efa f_...T.@I..%[{^.
00000050: 9c9d e774 4b4e bab6 7d60 b94b cd03 e906 ...tKN..}`.K....
00000060: 153d ab2b 2bc1 7139 762c 2e3f f3df c3f3 .=.++.q9v,.?....
00000070: 9077 beb6 525c 9544 f126 3f54 5fc7 710d .w..R\.D.&?T_.q.
00000080: db1b f25f 8c75 0bed ee54 2cd7 141f daf6 ..._.u...T,.....
00000090: b1e1 8ae7 d2ac 24cb d286 105e 53ca 7b4d ......$....^S.{M
000000a0: 47ba fae5 8948 59be 46f8 94b4 647f cb64 G....HY.F...d..d
000000b0: 37a4 f49d 1fd2 ddcd ecf7 eceb ccd7 ecbd 7...............
000000c0: cc83 99f3 9947 5e15 0000 0080 bff4 3ead .....G^.......>.
000000d0: a80b 39b9 9bb6 5a4d f3ac e398 76c3 713b ..9...ZM....v.q;
000000e0: 4db3 e536 aca6 5f9c deaa 6925 5d13 f56a M..6.._...i%]..j
000000f0: 79a7 ae89 72f5 a5b6 27f2 b18e 79b1 5d0d y...r...'...y.].
00000100: 2bf3 e249 3e52 5fd8 4b29 ea63 2fbe 3c8c +..I>R_.K).c/.<.
00000110: efbf b58c 63d3 3fa6 7f1d bddf 6d34 f6a0 ....c.?.....m4..
00000120: b6d0 bdaf 286a 2e27 5fe4 5ce3 e028 88e6 ....(j.'_.\..(..
00000130: bf52 bd88 7a69 b3a2 0daf f33f 63b9 aa6b .R..zi.....?c..k
00000140: afb4 9aa8 6eeb a25a af54 c4bb 5af9 6da9 ....n..Z.T..Z.m.
00000150: b62f de68 fb4f c530 b4d0 b53d 7dd0 cb6b ./.h.O.0...=}..k
00000160: 3939 6d1e 1ace e1e5 8668 06fc b682 d8d5 99m......h......
00000170: 6be5 2dbd 39a5 a88b 7372 3713 ccda 311d k.-.9...sr7...1.
00000180: c76a b706 33ea 9595 2be7 1eef 1c4e bf57 .j..3...+....N.W
00000190: 1bcb 4076 4251 e7bc 619c 2003 bd3e bdc3 ..@vBQ..a. ..>..
000001a0: e468 1e22 01dc f627 b315 9bcb b553 619e .h."...'.....Sa.
000001b0: 9d58 f697 bcf0 8257 06f5 fd69 2f4e 4ea9 .X.....W...i/NN.
000001c0: a539 590a 67fd f9c8 db1d 368c 8edb 0eca .9Y.g.....6.....
000001d0: 8dfe cc96 7b27 5352 b82f 94ce c615 756d ....{'SR./....um
000001e0: 5eee 3e0c fad9 e647 cb71 6dc3 8d66 215a ^.>....G.qm..f!Z
000001f0: 3971 65de aebc 2c4c 5eb4 6934 83e7 638a 9qe...,L^.i4..c.
00000200: baee 8d5c b83c f270 69a3 d5e3 7f1e 3bfe ...\.<.pi.....;.
00000210: ad88 8f1e fd76 74d7 6545 9d9f 972f 5e04 .....vt.eE.../^.
00000220: 2b18 ed1a 3d1f 1b5d cb78 c46b dcdb f11b +...=..].x.k....
00000230: 35ba a4bf b9ef 0dc7 396d dbf1 6bfa cb9d 5.......9m..k...
00000240: f117 6ff6 bffe 7c01 0000 0000 801b 163c ..o...|........<
00000250: e067 ff0f 0000 0000 40a2 b1ff 0700 0000 .g......@.......
00000260: 0020 f9f8 ff3f 0000 0000 00c9 c7f3 7f00 . ...?..........
00000270: 0000 0000 928f fd3f 0000 0000 00c9 c7ff .......?........
00000280: ff01 0000 0000 483e 9eff 0300 0000 0090 ......H>........
00000290: 7cec ff01 0000 0000 0000 0000 0000 0000 |...............
000002a0: 0000 0000 0000 e076 fa09 3ff3 0562 00c8 .......v..?..b..
000002b0: 0000 ..
00000000: 1f8b 0800 0000 0000 0003 edda 4d8f d240 ............M..@
00000010: 18c0 f176 79a9 9af0 7221 3d70 9970 51a2 ...vy...r!=p.pQ.
00000020: bb01 25ae 37c5 b531 44ec ba6c 49d8 8ba4 ..%.7..1D..lI...
00000030: a4dd 6ce3 2eac 6d89 eb11 13bf 88df c6ab ..l...m.........
00000040: dfc1 efe0 d119 282f 4b34 d90b 6af0 ff4b ......(/K4..j..K
00000050: 3a33 cf74 78a6 0fe9 6502 a370 10c4 7bde :3.tx...e..p..{.
00000060: 40db a09a f4b8 d198 f6f5 b5be 56db 574d @...........V.WM
00000070: 32ae 25e3 fd46 fda1 266a 9b7c a8b9 7114 2.%..F..&j.|..q.
00000080: bba1 dcf2 4fec f50f 3a3e 6a07 b12f 4e47 ....O...:>j../NG
00000090: e185 1b8b 475a 51d3 75ed 9910 f256 465e ....GZQ.u....VF^
000000a0: c6ca 5215 a757 62fd 06e9 33da 5ef8 2557 ..R..Wb...3.^.%W
000000b0: f8a1 6573 9fb5 c293 c2d7 fcb7 fcd3 dc77 ..es...........w
000000c0: 1900 0000 00c0 ffe8 6dd6 30ef 97f4 c9ed ........m.0.....
000000d0: 60e8 f957 e3c8 0ffb 513c f6fc 61dc 0f3c `..W....Q<..a..<
000000e0: 151a 071d abe9 58a2 6bb7 8eba 9668 d92f ......X.k....h./
000000f0: ac9e a8ac 2dac 8843 7b36 5911 f72a 2bf3 ....-..C{6Y..*+.
00000100: d55e c630 efca fcfa 32bf 6a86 ee85 affa .^.0....2.j.....
00000110: ecef b3cf 975d cfbd 98ad 4e8a 69c3 2c95 .....]....N.i.,.
00000120: f44f a9d8 1d9c 4fb3 a92b 9364 749a cfdb .O....O..+.dt...
00000130: d6f2 73ea 195b b663 bdb4 3ac2 3e74 84dd ..s..[.c..:.>t..
00000140: 6db7 c59b 4eeb 75b3 7322 5e59 270f c432 m...N.u.s"^Y'..2
00000150: b570 ac9e b358 25ef 5c7e f0ce dce8 6c76 .p...X%.\~....lv
00000160: 43c6 ab85 aba9 aa38 763a ad03 c74b 19e6 C......8v:...K..
00000170: 6e59 9fe4 a6c5 467e 1405 a3e1 a290 244e nY....F~......$N
00000180: ffb2 e4f5 c5b3 aa93 d9b5 c2f3 ba61 96e5 .............a..
00000190: 36d1 b4f0 644d d2ed 5c2f 7f25 413c 7ae7 6...dM..\/.%A<z.
000001a0: 0fd7 6abb f137 e05f 5d06 e1c7 8a90 c9db ..j..7._].......
000001b0: 8bf9 79d9 bb3b 59b3 59d6 b559 d5ef cfe5 ..y..;Y.Y..Y....
000001c0: a9be ef8e e3d1 34ee cf2b ab27 8394 7ceb ......4..+.'..|.
000001d0: 6ea9 572f a79a e25f 7dfb 0100 0000 00c0 n.W/..._}.......
000001e0: 86dd 510d e77f 0000 0000 00b6 1ae7 7f00 ..Q.............
000001f0: 0000 0000 b61f ffff 0700 0000 0060 fbf1 .............`..
00000200: fb3f 0000 0000 00db 8ff3 3f00 0000 0000 .?........?.....
00000210: 0000 6ca7 9f2d 67a0 8900 7800 00 ..l..-g...x..
EOF

# Restore the old orbit db after testing completes
Expand All @@ -146,7 +136,7 @@ curl --url "https://$SINGULARITY_HOSTNAME/login" \
| grep "msg = authentication failure"

# Check that we can create a user
orbit/warpdrive.sh -u user -p pass -i 1234 -n
orbit/warpdrive.sh -u user -i 1234 -n

add_cleanup "orbit/warpdrive.sh -u user -w"

Expand All @@ -166,6 +156,8 @@ curl --url "https://$SINGULARITY_HOSTNAME/register" \
| tee test/register_success \
| grep "msg = welcome to the classroom"

REGISTER_PASS="$(sed -nr 's/.*Password: ([^<]*).*/\1/p' < test/register_success | tr -d '\n')"

# Check that registration fails when student id is used for a second time
curl --url "https://$SINGULARITY_HOSTNAME/register" \
--unix-socket ./socks/https.sock \
Expand All @@ -186,15 +178,15 @@ curl --url "https://$SINGULARITY_HOSTNAME/login" \
curl --url "https://$SINGULARITY_HOSTNAME/login" \
--unix-socket ./socks/https.sock \
"${CURL_OPTS[@]}" \
--data "username=user&password=pass" \
--data "username=user&password=${REGISTER_PASS}" \
| tee test/login_success \
| grep "msg = user authenticated by password"

# Check that the user can get the empty list of email on the server
curl --url "pop3s://$SINGULARITY_HOSTNAME" \
--unix-socket ./socks/pop3s.sock \
"${CURL_OPTS[@]}" \
--user user:pass \
--user "user:${REGISTER_PASS}" \
| tee test/pop_get_empty \
| diff <(printf '\r\n') /dev/stdin

Expand All @@ -207,7 +199,7 @@ curl --url "smtps://$SINGULARITY_HOSTNAME" \
--mail-from "user@$SINGULARITY_HOSTNAME" \
--mail-rcpt "other@$SINGULARITY_HOSTNAME" \
--upload-file - \
--user 'user:pass' <<EOF
--user "user:${REGISTER_PASS}" <<EOF
Subject: Message Subject$CR
$CR
To whom it may concern,$CR
Expand All @@ -224,7 +216,7 @@ add_cleanup nuke_mail
curl --url "pop3s://$SINGULARITY_HOSTNAME/1" \
--unix-socket ./socks/pop3s.sock \
"${CURL_OPTS[@]}" \
--user user:pass \
--user "user:${REGISTER_PASS}" \
| tee test/pop_get_message \
| grep "Bottom text"

Expand All @@ -239,7 +231,7 @@ curl --url "https://$SINGULARITY_HOSTNAME/_matrix/client/r0/login" \
--data "{
\"type\": \"m.login.password\",
\"user\": \"@user:$SINGULARITY_HOSTNAME\",
\"password\": \"pass\"
\"password\": \"${REGISTER_PASS}\"
}" \
| tee test/matrix_login_success \
| grep "access_token"
Expand Down