diff --git a/config.example.toml b/config.example.toml
index 4873576..243ddb6 100644
--- a/config.example.toml
+++ b/config.example.toml
@@ -37,6 +37,17 @@ spam_thres = -3
# allow new accounts to be created
allow_registration = true
+[sessions]
+# allow sessions to be created (for 1.9, as it
+# doesn't send the password for authentication
+# in any shape or form, making relying on ip
+# addresses the only secure way of accessing
+# the server)
+allow = true
+
+# how long the session should last for
+expiry_time = 604800
+
[reuploads]
# allow reuploading levels from other servers
allowed = true
@@ -240,4 +251,4 @@ allow = true
[songs.sources.generic]
# direct URLs and similar
-allow = true
\ No newline at end of file
+allow = true
diff --git a/public/template/create_session.ecr b/public/template/create_session.ecr
new file mode 100644
index 0000000..db03ec8
--- /dev/null
+++ b/public/template/create_session.ecr
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+ Session Creation
+
+
+ <%= Templates.dir_header %>
+ Session Creation
+
+ <%- if disabled -%>
+ Sessions have been disabled
+ <%- end -%>
+
+
+
+ <%- if result == false -%>
+ Could not create session
+ <%- elsif result == true -%>
+ Session created successfully
+ <%- end -%>
+
+
diff --git a/public/template/index.ecr b/public/template/index.ecr
index 6a10520..b62036c 100644
--- a/public/template/index.ecr
+++ b/public/template/index.ecr
@@ -38,6 +38,9 @@
The Git repository
Song reuploading and searching
Levels and level reuploading
+ <%- if config_get("sessions.allow").as(Bool | Nil) -%>
+ The session creation page (for accessing features in 1.9)
+ <%- end -%>
Completely legal executable download
diff --git a/src/endpoints/comments/addLevelComment.cr b/src/endpoints/comments/addLevelComment.cr
index 3642733..eb346e9 100644
--- a/src/endpoints/comments/addLevelComment.cr
+++ b/src/endpoints/comments/addLevelComment.cr
@@ -8,7 +8,10 @@ CrystalGauntlet.endpoints["/uploadGJComment21.php"] = ->(context : HTTP::Server:
user_id, account_id = Accounts.auth(params)
if !(user_id && account_id)
- return "-1"
+ user_id, account_id = Accounts.auth_old(context.request, params)
+ if !(user_id && account_id)
+ return "-1"
+ end
end
comment = params["comment"]?
@@ -20,7 +23,12 @@ CrystalGauntlet.endpoints["/uploadGJComment21.php"] = ->(context : HTTP::Server:
end
if comment && !comment.blank?
- comment_value = Base64.decode_string(comment)[..100-1]
+ comment_value = comment
+ if params.has_key?("gameVersion")
+ comment_value = Base64.decode_string(comment_value)[..100-1]
+ else
+ comment_value = comment_value[..100-1]
+ end
next_id = IDs.get_next_id("comments")
DATABASE.exec("insert into comments (id, level_id, user_id, comment, percent) values (?, ?, ?, ?, ?)", next_id, level_id, user_id, comment_value, percent)
return "1"
@@ -31,6 +39,5 @@ CrystalGauntlet.endpoints["/uploadGJComment21.php"] = ->(context : HTTP::Server:
CrystalGauntlet.endpoints["/uploadGJComment20.php"] = CrystalGauntlet.endpoints["/uploadGJComment21.php"]
-CrystalGauntlet.endpoints["/uploadGJComment19.php"] = ->(context : HTTP::Server::Context): String {
- "-1"
-}
+CrystalGauntlet.endpoints["/uploadGJComment19.php"] = CrystalGauntlet.endpoints["/uploadGJComment21.php"]
+
diff --git a/src/endpoints/comments/deleteLevelComment.cr b/src/endpoints/comments/deleteLevelComment.cr
index 9af9cbf..3295d03 100644
--- a/src/endpoints/comments/deleteLevelComment.cr
+++ b/src/endpoints/comments/deleteLevelComment.cr
@@ -8,7 +8,10 @@ CrystalGauntlet.endpoints["/deleteGJComment20.php"] = ->(context : HTTP::Server:
user_id, account_id = Accounts.auth(params)
if !(user_id && account_id)
- return "-1"
+ user_id, account_id = Accounts.auth_old(context.request, params)
+ if !(user_id && account_id)
+ return "-1"
+ end
end
comment_user_id, level_user_id = DATABASE.query_one("select comments.user_id, levels.user_id from comments join levels on levels.id = comments.id where comments.id = ?", params["commentID"].to_i, as: {Int32, Int32})
@@ -22,6 +25,4 @@ CrystalGauntlet.endpoints["/deleteGJComment20.php"] = ->(context : HTTP::Server:
return "1"
}
-CrystalGauntlet.endpoints["/deleteGJComment19.php"] = ->(context : HTTP::Server::Context): String {
- "-1"
-}
+CrystalGauntlet.endpoints["/deleteGJComment19.php"] = CrystalGauntlet.endpoints["/deleteGJComment20.php"]
diff --git a/src/endpoints/levels/deleteLevel.cr b/src/endpoints/levels/deleteLevel.cr
index d1abd84..2fe9f91 100644
--- a/src/endpoints/levels/deleteLevel.cr
+++ b/src/endpoints/levels/deleteLevel.cr
@@ -7,7 +7,10 @@ CrystalGauntlet.endpoints["/deleteGJLevelUser20.php"] = ->(context : HTTP::Serve
user_id, account_id = Accounts.auth(params)
if !(user_id && account_id)
- return "-1"
+ user_id, account_id = Accounts.auth_old(context.request, params)
+ if !(user_id && account_id)
+ return "-1"
+ end
end
level_id = params["levelID"].to_i
@@ -19,3 +22,5 @@ CrystalGauntlet.endpoints["/deleteGJLevelUser20.php"] = ->(context : HTTP::Serve
return "1"
}
+
+CrystalGauntlet.endpoints["/deleteGJLevelUser19.php"] = CrystalGauntlet.endpoints["/deleteGJLevelUser20.php"]
diff --git a/src/endpoints/levels/rateLevel.cr b/src/endpoints/levels/rateLevel.cr
index eb8c06c..4bc6670 100644
--- a/src/endpoints/levels/rateLevel.cr
+++ b/src/endpoints/levels/rateLevel.cr
@@ -87,9 +87,7 @@ CrystalGauntlet.endpoints["/rateGJDemon21.php"] = ->(context : HTTP::Server::Con
CrystalGauntlet.endpoints["/rateGJStars20.php"] = CrystalGauntlet.endpoints["/rateGJStars211.php"]
-CrystalGauntlet.endpoints["/rateGJStars.php"] = ->(context : HTTP::Server::Context): String {
- "-1"
-}
+CrystalGauntlet.endpoints["/rateGJStars.php"] = CrystalGauntlet.endpoints["/rateGJStars211.php"]
CrystalGauntlet.endpoints["/rateGJLevel.php"] = ->(context : HTTP::Server::Context): String {
"-1"
diff --git a/src/endpoints/levels/uploadLevel.cr b/src/endpoints/levels/uploadLevel.cr
index f29bb7a..044e074 100644
--- a/src/endpoints/levels/uploadLevel.cr
+++ b/src/endpoints/levels/uploadLevel.cr
@@ -9,7 +9,10 @@ CrystalGauntlet.endpoints["/uploadGJLevel21.php"] = ->(context : HTTP::Server::C
# todo: green user fixes? pretty please?
user_id, account_id = Accounts.auth(params)
if !(user_id && account_id)
- return "-1"
+ user_id, account_id = Accounts.auth_old(context.request, params)
+ if !(user_id && account_id)
+ return "-1"
+ end
end
song_id = params["songID"] == "0" ? params["audioTrack"] : params["songID"]
@@ -112,7 +115,7 @@ CrystalGauntlet.endpoints["/uploadGJLevel21.php"] = ->(context : HTTP::Server::C
# todo: check seed2?
- requested_stars = params["requestedStars"].to_i.clamp(0..10)
+ requested_stars = (params["requestedStars"]? || "0").to_i.clamp(0..10)
if requested_stars == 0
requested_stars = nil
end
@@ -134,7 +137,7 @@ CrystalGauntlet.endpoints["/uploadGJLevel21.php"] = ->(context : HTTP::Server::C
# create new level
next_id = IDs.get_next_id("levels")
- DATABASE.exec("insert into levels (id, name, user_id, description, original, game_version, binary_version, password, requested_stars, unlisted, version, extra_data, level_info, editor_time, editor_time_copies, song_id, length, objects, coins, has_ldm, two_player) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", next_id, Clean.clean_special(params["levelName"])[..20-1], user_id, description[..140-1], params["original"].to_i, params["gameVersion"].to_i, params["binaryVersion"].to_i, params["password"] == "0" ? nil : params["password"].to_i, requested_stars, (params["unlisted"]? || "0").to_i == 1, params["levelVersion"].to_i, Clean.clean_special(extraString), Clean.clean_b64(params["levelInfo"]), (params["wt"]? || "0").to_i, (params["wt2"]? || "0").to_i, song_id.to_i, level_length, objects, coins, (params["ldm"]? || "0").to_i == 1, two_player)
+ DATABASE.exec("insert into levels (id, name, user_id, description, original, game_version, binary_version, password, requested_stars, unlisted, version, extra_data, level_info, editor_time, editor_time_copies, song_id, length, objects, coins, has_ldm, two_player) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", next_id, Clean.clean_special(params["levelName"])[..20-1], user_id, description[..140-1], params["original"].to_i, params["gameVersion"].to_i, (params["binaryVersion"]? || "0").to_i, params["password"] == "0" ? nil : params["password"].to_i, requested_stars, (params["unlisted"]? || "0").to_i == 1, params["levelVersion"].to_i, Clean.clean_special(extraString), Clean.clean_b64(params["levelInfo"]? || ""), (params["wt"]? || "0").to_i, (params["wt2"]? || "0").to_i, song_id.to_i, level_length, objects, coins, (params["ldm"]? || "0").to_i == 1, two_player)
File.write(DATA_FOLDER / "levels" / "#{next_id.to_s}.lvl", Base64.decode(params["levelString"]))
@@ -144,6 +147,5 @@ CrystalGauntlet.endpoints["/uploadGJLevel21.php"] = ->(context : HTTP::Server::C
CrystalGauntlet.endpoints["/uploadGJLevel20.php"] = CrystalGauntlet.endpoints["/uploadGJLevel21.php"]
-CrystalGauntlet.endpoints["/uploadGJLevel19.php"] = ->(context : HTTP::Server::Context): String {
- "-1"
-}
+CrystalGauntlet.endpoints["/uploadGJLevel19.php"] = CrystalGauntlet.endpoints["/uploadGJLevel21.php"]
+
diff --git a/src/endpoints/misc/likeItem.cr b/src/endpoints/misc/likeItem.cr
index fdbe238..5fc6d6e 100644
--- a/src/endpoints/misc/likeItem.cr
+++ b/src/endpoints/misc/likeItem.cr
@@ -44,7 +44,5 @@ CrystalGauntlet.endpoints["/likeGJItem211.php"] = ->(context : HTTP::Server::Con
}
CrystalGauntlet.endpoints["/likeGJItem20.php"] = CrystalGauntlet.endpoints["/likeGJItem211.php"]
+CrystalGauntlet.endpoints["/likeGJItem.php"] = CrystalGauntlet.endpoints["/likeGJItem211.php"]
-CrystalGauntlet.endpoints["/likeGJItem.php"] = ->(context : HTTP::Server::Context): String {
- "-1"
-}
diff --git a/src/lib/accounts.cr b/src/lib/accounts.cr
index 56bdc55..514e4d6 100644
--- a/src/lib/accounts.cr
+++ b/src/lib/accounts.cr
@@ -26,6 +26,7 @@ module CrystalGauntlet::Accounts
# todo: clean this periodically
AUTH_CACHE = Hash(Tuple(String | Nil, String | Nil, String | Nil), Tuple(Int32, Int32) | Tuple(Nil, Nil)).new
+ SESSIONS = Hash(Tuple(String | Nil, String | Nil), Tuple(Int32, Int32, Int64)).new
# returns userid, accountid
def auth(params : URI::Params) : (Tuple(Int32, Int32) | Tuple(Nil, Nil))
@@ -52,6 +53,51 @@ module CrystalGauntlet::Accounts
return user_id, ext_id.to_i
end
+ def auth_old(req : HTTP::Request, params : URI::Params) : (Tuple(Int32, Int32) | Tuple(Nil, Nil))
+ account_id = params["accountID"]
+ ip = IPs.get_real_ip(req)
+
+ if SESSIONS.has_key?({account_id, ip})
+ LOG.debug {"#{account_id || "???"}: session exists"}
+ user_id, ext_id, expiry_time = SESSIONS[{account_id, ip}]
+ if Time.utc.to_unix > expiry_time
+ LOG.debug {"#{account_id || "???"}: session expired"}
+ SESSIONS.delete({account_id, ip})
+ return nil, nil
+ else
+ LOG.debug {"#{account_id || "???"}: session valid"}
+ return user_id, ext_id
+ end
+ end
+
+ LOG.debug {"#{account_id || "???"}: session does not exist"}
+ return nil, nil
+ end
+
+ def new_session(req : HTTP::Request, username : String, password : String) : Bool
+ if !config_get("sessions.allow").as(Bool | Nil)
+ return false
+ end
+
+ ip = IPs.get_real_ip(req)
+ result = DATABASE.query_all("select id, password from accounts where username = ?", username, as: {Int32, String})
+ if result.size > 0
+ account_id, hash = result[0]
+ bcrypt = Crypto::Bcrypt::Password.new(hash)
+
+ if bcrypt.verify(password)
+ user_id = Accounts.get_user_id(account_id)
+ expiry_time = Time.utc.to_unix + (config_get("sessions.expiry_time").as(Int64 | Nil) || 604800)
+ SESSIONS[{account_id.to_s, ip}] = { user_id, account_id, expiry_time }
+ return true
+ else
+ return false
+ end
+ else
+ return false
+ end
+ end
+
def get_user_id(ext_id : Int32) : Int32
DATABASE.query("select id from users where udid = ? or account_id = ?", ext_id, ext_id) do |rs|
if rs.move_next
diff --git a/src/template_endpoints/create_session.cr b/src/template_endpoints/create_session.cr
new file mode 100644
index 0000000..407cd6f
--- /dev/null
+++ b/src/template_endpoints/create_session.cr
@@ -0,0 +1,22 @@
+require "uri"
+require "compress/gzip"
+
+include CrystalGauntlet
+
+CrystalGauntlet.template_endpoints["/tools/create_session"] = ->(context : HTTP::Server::Context) {
+ disabled = !config_get("sessions.allow").as(Bool | Nil)
+ result = nil
+ body = context.request.body
+ if body && !disabled
+ begin
+ params = URI::Params.parse(body.gets_to_end)
+ result = Accounts.new_session(context.request, params["username"], params["password"])
+
+ rescue error
+ LOG.error {"whar.... #{error}"}
+ end
+ end
+
+ context.response.content_type = "text/html"
+ ECR.embed("./public/template/create_session.ecr", context.response)
+}