Implement user bans and deactivations. Fix registration issues.

This commit is contained in:
Dominik Picheta 2015-02-14 14:13:36 +00:00
commit 57ee631735
2 changed files with 128 additions and 4 deletions

View file

@ -42,6 +42,11 @@ create table if not exists person(
);""" % [TUserName, TPassword, TEmail]), []) );""" % [TUserName, TPassword, TEmail]), [])
# echo "person table already exists" # echo "person table already exists"
db.exec(sql("""
alter table person
add ban varchar(128) not null default ''
"""))
db.exec(sql""" db.exec(sql"""
create unique index if not exists UserNameIx on person (name); create unique index if not exists UserNameIx on person (name);
""", []) """, [])

127
forum.nim
View file

@ -24,6 +24,7 @@ const
MaxPagesFromCurrent = 8 MaxPagesFromCurrent = 8
noPageNums = ["/login", "/register", "/dologin", "/doregister", "/profile"] noPageNums = ["/login", "/register", "/dologin", "/doregister", "/profile"]
noHomeBtn = ["/", "/login", "/register", "/dologin", "/doregister", "/profile"] noHomeBtn = ["/", "/login", "/register", "/dologin", "/doregister", "/profile"]
banReasonDeactivated = "DEACTIVATED"
type type
TCrud = enum crCreate, crRead, crUpdate, crDelete TCrud = enum crCreate, crRead, crUpdate, crDelete
@ -65,6 +66,7 @@ type
threads: int threads: int
lastOnline: int lastOnline: int
email: string email: string
ban: string
var var
db: TDbConn db: TDbConn
@ -223,11 +225,19 @@ proc devRandomSalt(): string =
proc makeSalt(): string = proc makeSalt(): string =
## Creates a salt using a cryptographically secure random number generator. ## Creates a salt using a cryptographically secure random number generator.
##
## Ensures that the resulting salt contains no ``\0``.
try: try:
result = devRandomSalt() result = devRandomSalt()
except IOError: except IOError:
result = randomSalt() result = randomSalt()
var newResult = ""
for i in 0 .. <result.len:
if result[i] != '\0':
newResult.add result[i]
return newResult
proc makePassword(password, salt: string, comparingTo = ""): string = proc makePassword(password, salt: string, comparingTo = ""): string =
## Creates an MD5 hash by combining password and salt. ## Creates an MD5 hash by combining password and salt.
when defined(windows): when defined(windows):
@ -287,8 +297,9 @@ proc register(c: var TForumData, name, pass, antibot, email: string): bool =
# perform registration: # perform registration:
var salt = makeSalt() var salt = makeSalt()
exec(db, sql("INSERT INTO person(name, password, email, salt, status, lastOnline) " & exec(db,
"VALUES (?, ?, ?, ?, 'user', DATETIME('now'))"), name, sql("INSERT INTO person(name, password, email, salt, status, lastOnline, " &
"ban) VALUES (?, ?, ?, ?, 'user', DATETIME('now'), '')"), name,
makePassword(pass, salt), email, salt) makePassword(pass, salt), email, salt)
# return setError(c, "", "Could not create your account!") # return setError(c, "", "Could not create your account!")
return true return true
@ -480,12 +491,18 @@ proc newThread(c: var TForumData): bool =
proc login(c: var TForumData, name, pass: string): bool = proc login(c: var TForumData, name, pass: string): bool =
# get form data: # get form data:
const query = const query =
sql"select id, name, password, email, salt, admin from person where name = ?" sql"select id, name, password, email, salt, admin, ban from person where name = ?"
if name.len == 0: if name.len == 0:
return c.setError("name", "Username cannot be nil.") return c.setError("name", "Username cannot be nil.")
var success = false var success = false
for row in fastRows(db, query, name): for row in fastRows(db, query, name):
if row[2] == makePassword(pass, row[4], row[2]): if row[2] == makePassword(pass, row[4], row[2]):
case row[6]
of "": discard
of banReasonDeactivated:
return c.setError("name", "Your account has been deactivated.")
else:
return c.setError("name", "You have been banned: " & row[6])
c.userid = row[0] c.userid = row[0]
c.username = row[1] c.username = row[1]
c.userpass = row[2] c.userpass = row[2]
@ -495,13 +512,18 @@ proc login(c: var TForumData, name, pass: string): bool =
break break
if success: if success:
# create session: # create session:
exec(db, exec(db,
sql"insert into session (ip, password, userid) values (?, ?, ?)", sql"insert into session (ip, password, userid) values (?, ?, ?)",
c.req.ip, c.userpass, c.userid) c.req.ip, c.userpass, c.userid)
return true return true
else: else:
return c.setError("password", "Login failed!") return c.setError("password", "Login failed!")
proc setBan(c: var TForumData, nick, reason: string): bool =
const query =
sql("update person set ban = ? where name = ?")
return tryExec(db, query, reason, nick)
proc hasReplyBtn(c: var TForumData): bool = proc hasReplyBtn(c: var TForumData): bool =
result = c.req.pathInfo != "/donewthread" and c.req.pathInfo != "/doreply" result = c.req.pathInfo != "/donewthread" and c.req.pathInfo != "/doreply"
result = result and c.req.params["action"] != "reply" result = result and c.req.params["action"] != "reply"
@ -687,6 +709,10 @@ proc gatherUserInfo(c: var TForumData, nick: string, ui: var TUserInfo): bool =
let lastOnlineDBVal = getValue(db, lastOnlineQuery, uid) let lastOnlineDBVal = getValue(db, lastOnlineQuery, uid)
ui.lastOnline = if lastOnlineDBVal != "": lastOnlineDBVal.parseInt else: -1 ui.lastOnline = if lastOnlineDBVal != "": lastOnlineDBVal.parseInt else: -1
ui.email = getValue(db, sql"select email from person where id = ?", uid) ui.email = getValue(db, sql"select email from person where id = ?", uid)
ui.ban = getValue(db, sql"select ban from person where id = ?", uid)
proc genSetUserStatusUrl(c: var TForumData, nick: string, typ: string): string =
c.req.makeUri("/setUserStatus?nick=$1&type=$2" % [nick, typ])
proc genProfile(c: var TForumData, ui: TUserInfo): string = proc genProfile(c: var TForumData, ui: TUserInfo): string =
result = "" result = ""
@ -724,6 +750,41 @@ proc genProfile(c: var TForumData, ui: TUserInfo): string =
th("Last Online"), th("Last Online"),
td(if ui.lastOnline != -1: t2.format("dd/MM/yy HH':'mm 'UTC'") td(if ui.lastOnline != -1: t2.format("dd/MM/yy HH':'mm 'UTC'")
else: "Never") else: "Never")
),
tr(
th("Status"),
td(case ui.ban
of banReasonDeactivated:
"Deactivated"
of "":
"Active"
else:
"Banned: " & ui.ban
)
),
tr(
th(""),
td(if c.isAdmin and ui.ban != banReasonDeactivated:
if ui.ban == "":
htmlgen.a(
href=c.genSetUserStatusUrl(ui.nick, "ban"),
"Ban user")
else:
htmlgen.a(href=c.genSetUserStatusUrl(ui.nick, "unban"),
"Unban user")
else: "")
),
tr(
th(""),
td(if c.userName == ui.nick or c.isAdmin:
if ui.ban == "":
htmlgen.a(href=c.genSetUserStatusUrl(ui.nick, "deactivate"),
"Deactivate user")
elif ui.ban == banReasonDeactivated:
htmlgen.a(href=c.genSetUserStatusUrl(ui.nick, "activate"),
"Activate user")
else: ""
else: "")
) )
) )
)) ))
@ -923,6 +984,64 @@ routes:
resp genMain(c, genFormPost(c, "donewthread", "New thread", "", "", false), resp genMain(c, genFormPost(c, "donewthread", "New thread", "", "", false),
"New Thread - Nimrod Forum") "New Thread - Nimrod Forum")
get "/setUserStatus/?":
createTFD()
cond (@"nick" != "")
cond (@"type" != "")
var formBody = "<input type=\"hidden\" name=\"nick\" value=\"" &
@"nick" & "\">"
var del = false
var content = ""
echo("Got type: ", @"type")
case @"type"
of "ban":
formBody.add "<input type='text' name='reason' />" &
"<input type='submit' name='banBtn' value='Ban' />"
content =
htmlgen.p("Please enter a reason for banning this user:")
of "unban":
formBody.add "<input type='submit' name='unbanBtn' value='Unban' />"
content =
htmlgen.p("Are you sure you wish to unban ", htmlgen.b(@"nick"),
"?")
del = true
of "deactivate":
formBody.add "<input type='hidden' name='reason' value='" &
banReasonDeactivated & "' />" &
"<input type='submit' name='actBtn' value='Deactivate' />"
content =
htmlgen.p("Are you sure you wish to deactivate ", htmlgen.b(@"nick"),
"?")
of "activate":
formBody.add "<input type='submit' name='actBtn' value='Activate' />"
content =
htmlgen.p("Are you sure you wish to activate ", htmlgen.b(@"nick"),
"?")
del = true
formBody.add "<input type='hidden' name='del' value='" & $del & "'/>"
content = htmlgen.form(action = c.req.makeUri("/dosetban"),
`method` = "POST", formBody) & content
resp genMain(c, content, "Set user status - Nim Forum")
post "/dosetban":
createTFD()
cond (@"nick" != "")
if not c.isAdmin and @"nick" != c.userName:
resp genMain(c, "You cannot ban this user.", "Error - Nim Forum")
if @"reason" == "" and @"del" != "true":
resp genMain(c, "Invalid ban reason.", "Error - Nim Forum")
let result =
if @"del" == "true":
# Remove the ban.
setBan(c, @"nick", "")
else:
setBan(c, @"nick", @"reason")
if result:
redirect(c.req.makeUri("/profile/" & @"nick"))
else:
resp genMain(c, "Failed to change the ban status of user.",
"Error - Nim Forum")
const licenseRst = slurp("static/license.rst") const licenseRst = slurp("static/license.rst")
get "/license": get "/license":
createTFD() createTFD()