Login with Roaming Profiles
This tutorial covers how to make a password server and a password client. The server will host a Lua table of usernames and passwords and the client will remotely connect each start up, ask the user for their username and password and compare it to that on the password server.
Setting Up
First of all, you must know how to craft a wireless modem and a computer. You need to craft at least 2 wireless modems and 2 computers if you want to have a practical use for this system. Then, attach 1 modem to each computer you want to add to the system.
Second, since typing on normal computers is relatively slow and annoying, I recommend to write the programs in advanced computers(because in advanced computers you can click to move the cursor around, and there are colors to distinguis between different statements), copying them into a disk and moving them into each computer you want to include(with their respective programs of course). Alternatively, if you are the server owner(this tutorial assumes you are in Multiplayer because this system does not have any practical use in Singleplayer), you can go into the world folder and write copy/paste the source code into the folders of each computer you want to use. The folders you should use are the named the IDs of the computers(so if you have 3 computers, with the IDs 5, 7 and 9, you should copy them into the folders 5, 7 and 9).
It does not matter wether you are using Normal or Advanced computers for this tutorial. I recommend to use Normal ones for the server.
When writing the programs, I HIGHLY recommend using a disk drive like stated above, so you dont have to write in prograns for every computer you add. Remember to read the text behind the --. These are comments, and you can safely leave them in the code.
Server
print("Initializing...") local validSender = false valid = false tserved, vserved, dserved = 0 print("Loading startup settings...") count = true -- If you dont want the server to count request statics, set the value of this variable to "false" without qoutes. modemSide = "left" -- change to the side of the computer your modem is on whitelist = true -- If you dont want to use a whitelist, change the value to "false" without quotes. This is not recommended on Multiplayer, but it does not make any difference on a Singleplayer World. print("Loading user database...") users = {"username1", "username2" } -- Change this to the names for the users you want to have. Make sure the usernames are in the same order as in the password table. Changing the order is fine, but you must maintain the same order in both the users and passwords tables. Incorrect orders WILL cause 2 or more users not being able to log in! passwords = {"password1", "password2" } -- Change this to the passwords for the users defined in the variable above. Again, make sure the passwords for users are in the same order as the usernames. senders = { 1, 2, 3, 4 } -- change this to the ID's of the all computers you want to accept requests from if you use a whitelist. If you do not use a whitelist, DO NOT REMOVE THIS LINE! Doing so will cause the program to crash everytime it is run, even though the variable is not used. print("Loading core functions...") function clear() term.clear() term.setCursorPos(1, 1) print("CCLS Server 1.0 for CC 1.53 (Copyright (c) Dirk Kok 2013)") print("There is no user interaction here. Please contact your system administrator for assistance regarding the system.") if count == true then print(tserved.." total requests received this session.") print(vserved.." requests completed this session.") print(dserved.." requests denied this session.") elseif count == false then print("Request statics have been disabled.") else rednet.close(modemSide) term.clear() term.setCursorPos(1, 1) print("Error: Counter setting is not boolean and the server can not continue.") print("The server program will exit automatically in 10 minutes. Or you can hold down Ctrl+T for 1 second.") print("After that, please set the variable \"count\" in the source code to either \"true\" or \"false\" WITHOUT QUOTATION MARKS. Doing this with quotation marks will cause the same error.") sleep(600) shell.exit() end print("Starting up...") rednet.open(modemSide) -- Opens the modem to wait for incoming login requests clear() while true do validSender = false senderId, message, distance = rednet.receive() for i,v in ipairs(senders) do tserved = tserved + 1 if v == senderId then validSender = true break else rednet.send(senderId, "301", true) -- Send response code 301 - which means Not Whitelisted. dserved = dserved + 1 clear() end end if validSender then for i,v in ipairs(users) do if message == v then password = passwords[i] rednet.send(senderId, password, true) vserved = vserved + 1 clear() break else rednet.send(senderId, "300", true) -- Send response code 300 - which means Bad Auth. dserved = dserved + 1 clear() end end end end
Client
os.pullEvent = os.pullEventRaw local failed = true busr = "nsliq" -- If you ever happen to be unable to log in for some reason, you can use this username as a backup. bpass = "zuFUkVEjQKjkLQhTdpFMheNF" -- If you ever happen to be unable to log in for some reason, you can use this password as a backup. password_server = 0 -- change to the ID of your password server computer rednet.open("left") -- change to the side your rednet modem is on while true do term.clear() term.setCursorPos(1,1) print("Welcome to CCLS 1.0.") print("Please select an option.") print("[1] Login") print("[2] Shutdown") write("> ") input = read() if input == "2" then os.shutdown() elseif input == "1" then print("Please login.") write("Username: ") username = read() write("Password: ") password = read("*") if username == busr then if password == bpass then print("Access granted") break end end rednet.send(password_server, username, true) senderId, message, distance = rednet.receive(5) if message == "300" then print("Invalid Username or Password.") sleep(3) elseif password == message then failed = false term.clear() term.setCursorPos(1,1) print("Welcome ", username) break; else print("Invalid Username or Password.") sleep(3) end else print("Invalid Command.") sleep(2) end end
Old Server
Note: This is here for reference for people who need the older version. I recommend using the programs above.
os.pullEvent = os.pullEventRaw term.clear() term.setCursorPos(1,1) print("This is a password server. There is no user interaction here.") print("Please find a computer and login there.") local firstCycle = true local validSender = false local modemSide = "left" -- change to the side of the computer your modem is on local valid = false users = {"username1", "username2" } --make sure users and passwords line up passwords = {"password1", "password2" } senders = { 1, 2, 3, 4 } -- computer ID's of the computers you want to accept requests from function bootUp() rednet.open(modemSide) end while true do validSender = false if firstCycle then bootUp() firstCycle = false end senderId, message, distance = rednet.receive() for i,v in ipairs(senders) do if v == senderId then validSender = true break end end if validSender then for i,v in ipairs(users) do if message == v then valid = true password = passwords[i] break else valid = false end end if valid then rednet.send(senderId, password, true) else rednet.send(senderId, "Not Valid", true) end end end
Old Client
os.pullEvent = os.pullEventRaw local locker = true local failed = true local attempted_login = true local password_server = 0 -- change to the ID of your password server computer rednet.open("left") -- change to the side your rednet modem is on while locker do attempted_login = false term.clear() term.setCursorPos(1,1) print("Welcome to a USERS PC : Roaming Profile Enabled") print("What would you like to do?") print("[1] Login (*)") print("[2] Shutdown") write("> ") local input = read() if input == "2" then os.shutdown() elseif input == "1" then attempted_login = true print("Please login...") write("Username: ") local username = read() write("Password: ") local password = read("*") rednet.send(password_server, username, true) senderId, message, distance = rednet.receive(5) if password == message then failed = false locker = false term.clear() term.setCursorPos(1,1) print("Welcome ", username) else print("Invalid Username or Password.") sleep(3) end else print("Command not recognised...") sleep(2) end end
Thoughts and Notes
Note: You do not have to read these if you use the new system.
Although this may work very well as a login system, it is insecure by the flaw that a malicious sniffer can unnoticeably obtain a copy of the password while it is being sent as described on the Rednet API page, and use the password to login.
If you change the client code, to "startup" and put it in the root directory, it works very well as a login system. You may also want to put in the line
os.pullEvent = os.pullEventRaw -- disables Ctrl+T
as if it doesn't work first time it can be difficult to get out of the program.
If you have either Railcraft or Additional Pipes, find the 'World Anchor' or 'Teleport Tether' block. If your server can afford the resources, place the block within a 3x3 grid of your password server. It will ensure that even if you leave the chunk, the password server still is operational. Otherwise you may find that you cannot login because you've left the chunk and the password server is no longer dealing with requests.
This is a minor security flaw but it is only if someone has access to a computer that is 'whitelisted' to access the password server. The security flaw is that if they use the password Not Valid on that computer they are allowed access;
if valid then --send client password else --send client Not Valid end
to fix remove the else statement