· 7 years ago · Dec 11, 2018, 02:12 AM
1local channels = {
2 atm = 1,
3 db = 2,
4}
5
6os.loadAPI("/database/common.lua")
7os.loadAPI("/database/sha256.lua")
8
9local database = {}
10local dbpath = "/bank.db"
11local logpath = "/bank.log"
12local maintenance_pass = "test"
13local modem = peripheral.find("modem")
14
15local function loadDB(path)
16 local ret = {}
17 if fs.exists(path) then
18 local f = fs.open(path, "r")
19 local c = f.readAll()
20 ret = textutils.unserialise(c)
21 if not ret then return false end
22 end
23 return ret
24end
25local function saveDB(path, db)
26 local f = fs.open(path, "w")
27 local s = textutils.serialise(db)
28 f.write(s)
29 f.close()
30 return true
31end
32local function printHeader(str, ny)
33 local w, h = term.getSize()
34 local x, y = term.getCursorPos()
35 term.setCursorPos(math.ceil(w/2) - math.floor(#str/2), ny or 1)
36 term.clearLine()
37 term.write(str)
38 term.setCursorPos(x, y)
39end
40local logfile = fs.open(logpath, "a")
41local function log(top, str, nosave)
42 local form = (top and ("["..top.."] ") or "")..(str)
43 print(form)
44 printHeader("Database running with "..dbpath)
45 if not nosave then
46 logfile.writeLine(form)
47 logfile.flush()
48 end
49end
50
51database = loadDB(dbpath)
52saveDB(dbpath, database)
53
54-- modem.open(channels.db)
55term.clear()
56term.setCursorPos(1,2)
57logfile.writeLine()
58log("Init", "Database started")
59
60while true do
61 local event, side, chan, reply, msg = os.pullEvent()
62 if event == "modem_message" and chan == channels.db and reply == channels.atm and type(msg) == "table" then
63
64 if msg[1] == "auth" then
65 local user = msg[2]:lower()
66 local userdata = database[user]
67 if userdata then
68 modem.transmit(reply, channels.db, {true, userdata.salt})
69 local resp = common.waitForMsg(reply, 2)
70 if resp and resp[1] then
71 local ok = resp[2] == userdata.passwd
72 modem.transmit(reply, channels.db, {ok, not ok and "Wrong password" or nil})
73 log("Auth", (ok and "Valid" or "Invalid").." auth: "..user)
74 else log("Auth", user.." timed out")
75 end
76 else
77 log("Auth", "User not found: "..user)
78 modem.transmit(reply, channels.db, {false, "User not found"})
79 end
80
81 elseif msg[1] == "register" then
82 local user = msg[2]:lower()
83 local ok = true
84 if user:find("%s") then ok = false log("Register", "Tried registering with a space: "..user) modem.transmit(reply, channels.db, {false, "Username cannot contain spaces"}) end
85 if database[user] then ok = false log("Register", "User already exists: "..user) modem.transmit(reply, channels.db, {false, "User already exists"}) end
86 if ok then
87 local salt = common.genSalt(16)
88 modem.transmit(reply, channels.db, {true, salt})
89 local r = common.waitForMsg(reply, 2)
90 if r and r[1] and type(r[2]) == "string" and #r[2] == 64 then
91 database[user] = {}
92 database[user].balance = 0
93 database[user].passwd = r[2]
94 database[user].salt = salt
95 saveDB(dbpath, database)
96 log("Register", user.." created")
97 modem.transmit(reply, channels.db, {true})
98 else
99 log("Register", user.." timed out")
100 end
101 end
102
103 elseif msg[1] == "balance" then
104 local user = msg[2]:lower()
105 local userdata = database[user]
106 if msg[3] == userdata.passwd then
107 modem.transmit(reply, channels.db, {true, userdata.balance})
108 log("Balance", "Sent balance: "..user)
109 else
110 modem.transmit(reply, channels.db, {false, "Invalid hash"})
111 log("Balance", "Invalid hash from: "..user)
112 end
113
114 elseif msg[1] == "transfer" then
115 local user = msg[2]:lower()
116 local recip = msg[4]:lower()
117 local hash = msg[3]
118 local amt = tonumber(msg[5]) or math.huge
119 amt = math.abs(amt)
120 local userdat = database[user]
121 local recipdat = database[recip]
122 local ok = true
123
124 if not recipdat then ok = false log("Transfer", user.." tried to send to invalid: "..recip) modem.transmit(reply, channels.db, {false, "Recipient doesn't exist"}) end
125 if userdat.passwd ~= hash then ok = false log("Transfer", "Invalid hash from: "..user) modem.transmit(reply, channels.db, {false, "Invalid hash"}) end
126 if userdat.balance < amt then ok = false log("Transfer", user.." tried to send "..amt.." but has "..userdat.balance.." to "..recip) modem.transmit(reply, channels.db, {false, "Insufficent funds"}) end
127 if ok then
128 database[user].balance = userdat.balance - amt
129 database[recip].balance = recipdat.balance + amt
130 saveDB(dbpath, database)
131 modem.transmit(reply, channels.db, {true, userdat.balance})
132 log("Transfer", user.." transfered "..amt.." to "..recip)
133 end
134 end
135
136 elseif event == "key" and side == keys.enter then
137 log("Admin", "Enter password")
138 write("> ")
139 local pswd = read("*")
140 if pswd == maintenance_pass then
141 log("Admin", "Entering maintenance mode")
142 while true do
143 write("> ")
144 local cmd = read()
145 cmd = common.split(cmd, "%s")
146 if cmd[1] == "set" then
147 local user = cmd[2]:lower()
148 local amt = tonumber(cmd[3]) or 0
149 if database[user] then
150 database[user].balance = amt
151 saveDB(dbpath, database)
152 log("Admin", "Set "..user.." balance to "..amt)
153 else
154 log("Admin", "Cannot set balance of "..user)
155 end
156
157 elseif cmd[1] == "add" then
158 local user = cmd[2]:lower()
159 local amt = tonumber(cmd[3]) or 0
160 if database[user] then
161 database[user].balance = database[user].balance + amt
162 saveDB(dbpath, database)
163 log("Admin", (amt >= 0 and "Added " or "Removed ")..amt.." to "..user.."'s balance")
164 else
165 log("Admin", "Cannot add to balance of "..user)
166 end
167
168 elseif cmd[1] == "create" then
169 local user = cmd[2]:lower()
170 local ok = true
171 if user:find("%s") then ok = false log("Admin", "Tried registering with a space: "..user) end
172 if database[user] then ok = false log("Admin", "User already exists: "..user) end
173 if ok then
174 write("Password: ")
175 local pass = read()
176 local salt = common.genSalt(16)
177 local hash = sha256.pbkdf2(pass, salt, 64):toHex()
178 database[user] = {}
179 database[user].balance = 0
180 database[user].passwd = hash
181 database[user].salt = salt
182 saveDB(dbpath, database)
183 log("Admin", user.." created")
184 end
185
186 elseif cmd[1] == "delete" then
187 local user = cmd[2]:lower()
188 if database[user] then
189 database[user] = nil
190 saveDB(dbpath, database)
191 log("Admin", user.." deleted from database")
192 else
193 log("Admin", "Tried to delete invalid user: "..user)
194 end
195
196 elseif cmd[1] == "recover" then
197 local user = cmd[2]:lower()
198 if database[user] then
199 write("New Password: ")
200 local pass = read()
201 local salt = common.genSalt(16)
202 local hash = sha256.pbkdf2(pass, salt, 64):toHex()
203 database[user].passwd = hash
204 database[user].salt = salt
205 saveDB(dbpath, database)
206 log("Admin", user.." password changed")
207 else
208 log("Admin", user.." does not exists")
209 end
210
211 elseif cmd[1] == "list" then
212 local users = {}
213 for user, userdat in pairs(database) do
214 log(nil, user..": "..userdat.balance, true)
215 end
216 elseif cmd[1] == "help" then
217 log(nil, "set <user> <balance> -- Sets a users balance", true)
218 log(nil, "add <user> <+/- amount> -- Add or remove from user balance", true)
219 log(nil, "create <user> -- Add an account to the database", true)
220 log(nil, "delete <user> -- Removes an account from the database", true)
221 log(nil, "recover <user> -- Changes an account's password", true)
222 log(nil, "list -- Lists accounts", true)
223 log(nil, "quit -- Quits maintenance mode", true)
224 log(nil, "stop -- Stops the database", true)
225
226 elseif cmd[1] == "quit" then break
227 elseif cmd[1] == "stop" then return
228
229 else log("Command", "Invalid command: "..(cmd[1] or ""), true)
230 end
231 repeat event, side = os.pullEvent("key") until side == keys.enter
232 end
233 log("Admin", "Exiting maintenance")
234 else
235 log("Admin", "Wrong password")
236 end
237 end
238end
239log("Admin", "Database shutting down")