This commit is contained in:
Dominik Picheta 2018-05-20 22:53:32 +01:00
commit 8e05dab331
3 changed files with 31 additions and 71 deletions

View file

@ -37,13 +37,15 @@ proc makeSalt*(): string =
newResult.add result[i]
return newResult
proc makeSessionKey*(): string =
## Creates a random key to be used to authorize a session.
let random = makeSalt()
return bcrypt.hash(random, genSalt(8))
proc makePassword*(password, salt: string, comparingTo = ""): string =
## Creates an MD5 hash by combining password and salt.
when defined(windows):
result = getMD5(salt & getMD5(password))
else:
let bcryptSalt = if comparingTo != "": comparingTo else: genSalt(8)
result = hash(getMD5(salt & getMD5(password)), bcryptSalt)
let bcryptSalt = if comparingTo != "": comparingTo else: genSalt(8)
result = hash(getMD5(salt & getMD5(password)), bcryptSalt)
proc makeIdentHash*(user, password: string, epoch: int64, secret: string,
comparingTo = ""): string =
@ -52,8 +54,5 @@ proc makeIdentHash*(user, password: string, epoch: int64, secret: string,
## The ``epoch`` determines the creation time of this hash, it will be checked
## during verification to ensure the hash hasn't expired.
## The ``secret`` is the 'salt' field in the ``person`` table.
when defined(windows):
result = getMD5(user & password & $epoch & secret)
else:
let bcryptSalt = if comparingTo != "": comparingTo else: genSalt(8)
result = hash(user & password & $epoch & secret, bcryptSalt)
let bcryptSalt = if comparingTo != "": comparingTo else: genSalt(8)
result = hash(user & password & $epoch & secret, bcryptSalt)

View file

@ -23,45 +23,26 @@ import frontend/[
category, postlist, error, header, post, profile, user, karaxutils
]
when not defined(windows):
import bcrypt # TODO
from htmlgen import tr, th, td, span, input
const
unselectedThread = -1
transientThread = 0
ThreadsPerPage = 15
PostsPerPage = 10
MaxPagesFromCurrent = 8
noPageNums = ["/login", "/register", "/dologin", "/doregister", "/profile"]
noHomeBtn = ["/", "/login", "/register", "/dologin", "/doregister", "/profile"]
type
TCrud = enum crCreate, crRead, crUpdate, crDelete
TSession = object of RootObj
threadid: int
postid: int
Session = object of RootObj
userName, userPass, email: string
rank: Rank
TPost = tuple[subject, content: string]
TForumData = ref object of TSession
TForumData = ref object of Session
req: Request
userid: string
actionContent: string
errorMsg, loginErrorMsg: string
invalidField: string
currentPost: TPost ## Only used for reply previews
startTime: float
isThreadsList: bool
pageNum: int
totalPosts: int
search: string
noPagenumumNav: bool
config: Config
var
@ -74,17 +55,8 @@ var
proc init(c: TForumData) =
c.userPass = ""
c.userName = ""
c.threadId = unselectedThread
c.postId = -1
c.userid = ""
c.actionContent = ""
c.errorMsg = ""
c.loginErrorMsg = ""
c.invalidField = ""
c.currentPost = (subject: "", content: "")
c.search = ""
proc loggedIn(c: TForumData): bool =
result = c.userName.len > 0
@ -93,7 +65,7 @@ proc loggedIn(c: TForumData): bool =
proc genThreadUrl(c: TForumData, postId = "", action = "", threadid = "", pageNum = ""): string =
result = "/t/" & (if threadid == "": $c.threadId else: threadid)
result = "/t/" & threadid
if pageNum != "":
result.add("/" & pageNum)
if action != "":
@ -115,10 +87,6 @@ proc getGravatarUrl(email: string, size = 80): string =
# -----------------------------------------------------------------------------
template `||`(x: untyped): untyped = (if not isNil(x): x else: "")
proc setError(c: TForumData, field, msg: string): bool {.inline.} =
c.invalidField = field
c.errorMsg = "Error: " & msg
return false
proc resetPassword(
c: TForumData,
@ -142,22 +110,21 @@ proc resetPassword(
)
proc logout(c: TForumData) =
const query = sql"delete from session where ip = ? and password = ?"
const query = sql"delete from session where ip = ? and key = ?"
c.username = ""
c.userpass = ""
exec(db, query, c.req.ip, c.req.cookies["sid"])
proc checkLoggedIn(c: TForumData) =
if not c.req.cookies.hasKey("sid"): return
let pass = c.req.cookies["sid"]
let sid = c.req.cookies["sid"]
if execAffectedRows(db,
sql("update session set lastModified = DATETIME('now') " &
"where ip = ? and password = ?"),
c.req.ip, pass) > 0:
c.userpass = pass
"where ip = ? and key = ?"),
c.req.ip, sid) > 0:
c.userid = getValue(db,
sql"select userid from session where ip = ? and password = ?",
c.req.ip, pass)
sql"select userid from session where ip = ? and key = ?",
c.req.ip, sid)
let row = getRow(db,
sql"select name, email, status from person where id = ?", c.userid)
@ -170,7 +137,7 @@ proc checkLoggedIn(c: TForumData) =
c.userid)
else:
echo("SID not found in sessions. Assuming logged out.")
warn("SID not found in sessions. Assuming logged out.")
proc incrementViews(threadId: int) =
const query = sql"update thread set views = views + 1 where id = ?"
@ -181,7 +148,7 @@ proc validateRst(c: TForumData, content: string): bool =
try:
discard rstToHtml(content)
except EParseError:
result = setError(c, "", getCurrentExceptionMsg())
result = false
proc crud(c: TCrud, table: string, data: varargs[string]): SqlQuery =
case c
@ -273,10 +240,6 @@ template createTFD() =
new(c)
init(c)
c.req = request
c.startTime = epochTime()
c.isThreadsList = false
c.pageNum = 1
c.config = config
if request.cookies.len > 0:
checkLoggedIn(c)
@ -534,7 +497,7 @@ proc executeNewThread(c: TForumData, subject, msg: string): (int64, int64) =
raise newForumError("Subject already exists", @["subject"])
discard tryExec(db, crud(crCreate, "thread_fts", "id", "name"),
c.threadID, subject)
result[0], subject)
result[1] = executeReply(c, result[0].int, msg, none[int]())
discard tryExec(db, sql"insert into post_fts(post_fts) values('optimize')")
discard tryExec(db, sql"insert into post_fts(thread_fts) values('optimize')")
@ -553,12 +516,13 @@ proc executeLogin(c: TForumData, username, password: string): string =
for row in fastRows(db, query, username, username):
if row[2] == makePassword(password, row[4], row[2]):
let key = makeSessionKey()
exec(
db,
sql"insert into session (ip, password, userid) values (?, ?, ?)",
c.req.ip, row[2], row[0]
sql"insert into session (ip, key, userid) values (?, ?, ?)",
c.req.ip, key, row[0]
)
return row[2]
return key
raise newForumError("Invalid username or password")
@ -574,9 +538,8 @@ proc validateEmail(email: string, checkDuplicated: bool) =
raise newForumError("Email already exists", @["email"])
proc executeRegister(c: TForumData, name, pass, antibot, userIp,
email: string): Future[string] {.async.} =
## Registers a new user and returns a new session key for that user's
## session if registration was successful. Exceptions are raised otherwise.
email: string) {.async.} =
## Registers a new user.
# email validation
validateEmail(email, checkDuplicated=true)
@ -619,8 +582,6 @@ proc executeRegister(c: TForumData, name, pass, antibot, userIp,
VALUES (?, ?, ?, ?, ?, DATETIME('now'))
""", name, password, email, salt, $EmailUnconfirmed)
return password
proc executeLike(c: TForumData, postId: int) =
# Verify the post exists and doesn't belong to the current user.
const postQuery = sql"""
@ -986,7 +947,7 @@ routes:
else:
""
try:
discard await executeRegister(
await executeRegister(
c,
username,
password,
@ -1349,7 +1310,6 @@ routes:
get "/threadActivity.xml":
createTFD()
c.isThreadsList = true
resp genThreadsRSS(c), "application/atom+xml"
get "/postActivity.xml":
@ -1369,6 +1329,7 @@ routes:
# # if ban == EmailUnconfirmed:
# # success = setStatus(c, @"nick", Moderated, "")
when false:
post "/search/?@page?":
cond isFTSAvailable
createTFD()

View file

@ -35,7 +35,7 @@ proc initialiseDb(admin: tuple[username, password, email: string],
const
userNameType = "varchar(20)"
passwordType = "varchar(50)"
passwordType = "varchar(300)"
emailType = "varchar(254)" # https://stackoverflow.com/a/574698/492186
# -- Category
@ -141,7 +141,7 @@ proc initialiseDb(admin: tuple[username, password, email: string],
create table session(
id integer primary key,
ip inet not null,
password $# not null,
key $# not null,
userid integer not null,
lastModified timestamp not null default (DATETIME('now')),
foreign key (userid) references person(id)