/
app.rb
146 lines (108 loc) · 4.28 KB
/
app.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
require 'digest/sha1'
require 'rack/contrib'
require 'sinatra/base'
require 'sinatra/json'
require 'sqlite3'
STRETCH = 1000
LIMIT = 1000
class App < Sinatra::Base
DB = SQLite3::Database.new 'data/db.sqlite3'
DB.execute <<-SQL
CREATE TABLE IF NOT EXISTS account (
user TEXT PRIMARY KEY,
pass TEXT,
balance INTEGER
);
SQL
use Rack::PostBodyContentTypeParser
enable :sessions
def err(code, message)
[code, json({message: message})]
end
not_found do
redirect '/index.html', 302
end
get '/source' do
content_type :text
IO.binread __FILE__
end
get '/api/flag' do
return err(401, 'login first') unless user = session[:user]
hashed_user = STRETCH.times.inject(user){|s| Digest::SHA1.hexdigest(s)}
res = DB.query 'SELECT balance FROM account WHERE user = ?', hashed_user
row = res.next
balance = row && row[0]
res.close
return err(401, 'login first') unless balance
return err(403, 'earn more coins!!!') unless balance >= 10_000_000_000
json({flag: IO.binread('data/flag.txt')})
end
post '/api/balance' do
return err(401, 'login first') unless user = session[:user]
hashed_user = STRETCH.times.inject(user){|s| Digest::SHA1.hexdigest(s)}
res = DB.query('SELECT balance FROM account WHERE user = ?', hashed_user)
row = res.next
res.close
return err(401, 'login first') unless row
json({balance: row[0]})
end
post '/api/register' do
return err(400, 'bad request') unless user = params[:user] and String === user
return err(400, 'bad request') unless pass = params[:pass] and String === pass
return err(400, 'too short username') unless 4 <= user.size
return err(400, ':thinking_face: 🤔') unless 6 <= pass.size
return err(400, 'too long request') unless user.size <= LIMIT and pass.size <= LIMIT
sleep 1
hashed_user = STRETCH.times.inject(user){|s| Digest::SHA1.hexdigest(s)}
hashed_pass = STRETCH.times.inject(pass){|s| Digest::SHA1.hexdigest(s)}
begin
DB.execute 'INSERT INTO account (user, pass, balance) VALUES (?, ?, 100)', hashed_user, hashed_pass
rescue SQLite3::ConstraintException
return err(422, 'the username has already been taken')
end
return 200
end
post '/api/login' do
return err(400, 'bad request') unless user = params[:user] and String === user
return err(400, 'bad request') unless pass = params[:pass] and String === pass
return err(400, 'too short username') unless 4 <= user.size
return err(400, ':thinking_face: 🤔') unless 6 <= pass.size
return err(400, 'too long request') unless user.size <= LIMIT and pass.size <= LIMIT
hashed_user = STRETCH.times.inject(user){|s| Digest::SHA1.hexdigest(s)}
hashed_pass = STRETCH.times.inject(pass){|s| Digest::SHA1.hexdigest(s)}
res = DB.query 'SELECT 1 FROM account WHERE user = ? AND pass = ?', hashed_user, hashed_pass
row = res.next
res.close
return err(401, 'username and password did not match') unless row
session[:user] = user
return 200
end
post '/api/logout' do
session[:user] = nil
return 200
end
post '/api/transfer' do
return err(401, 'login first') unless src = session[:user]
return err(400, 'bad request') unless dst = params[:target] and String === dst and dst != src
return err(400, 'bad request') unless amount = params[:amount] and String === amount
return err(400, 'bad request') unless amount = amount.to_i and amount > 0
sleep 1
hashed_src = STRETCH.times.inject(src){|s| Digest::SHA1.hexdigest(s)}
hashed_dst = STRETCH.times.inject(dst){|s| Digest::SHA1.hexdigest(s)}
res = DB.query 'SELECT balance FROM account WHERE user = ?', hashed_src
row = res.next
balance_src = row && row[0]
res.close
return err(422, 'no enough coins') unless balance_src >= amount
res = DB.query 'SELECT balance FROM account WHERE user = ?', hashed_dst
row = res.next
balance_dst = row && row[0]
res.close
return err(422, 'no such user') unless balance_dst
balance_src -= amount
balance_dst += amount
DB.execute 'UPDATE account SET balance = ? WHERE user = ?', balance_src, hashed_src
DB.execute 'UPDATE account SET balance = ? WHERE user = ?', balance_dst, hashed_dst
json({amount: amount, balance: balance_src})
end
end