diff --git a/public/css/mango.css b/public/css/mango.css index e601b57..d7393bb 100644 --- a/public/css/mango.css +++ b/public/css/mango.css @@ -8,3 +8,6 @@ .acard:hover { text-decoration: none; } +.uk-list li { + cursor: pointer; +} diff --git a/src/auth_handler.cr b/src/auth_handler.cr index f026e80..4224dd5 100644 --- a/src/auth_handler.cr +++ b/src/auth_handler.cr @@ -13,13 +13,17 @@ class AuthHandler < Kemal::Handler def call(env) return call_next(env) if exclude_match?(env) - env.request.cookies.each do |c| - next if c.name != "token" - if @storage.verify_token c.value - return call_next env + cookie = env.request.cookies.find { |c| c.name == "token" } + if cookie.nil? || ! @storage.verify_token cookie.value + return env.redirect "/login" + end + + if env.request.path.starts_with? "/admin" + unless storage.verify_admin cookie.value + env.response.status_code = 401 end end - env.redirect "/login" + call_next env end end diff --git a/src/mango.cr b/src/mango.cr index 0d9ea07..8cffbc6 100644 --- a/src/mango.cr +++ b/src/mango.cr @@ -17,6 +17,17 @@ macro send_img(env, img) send_file {{env}}, {{img}}.data, {{img}}.mime end +macro get_username(env) + cookie = {{env}}.request.cookies.find { |c| c.name == "token" } + next if cookie.nil? + storage.verify_token cookie.value +end + +def hash_to_query(hash) + hash.map { |k, v| "#{k}=#{v}" } + .join("&") +end + get "/" do |env| titles = library.titles @@ -32,6 +43,84 @@ get "/book/:title" do |env| layout "title" end +get "/admin" do |env| + layout "admin" +end + +get "/admin/user" do |env| + users = storage.list_users + username = get_username env + layout "user" +end + +get "/admin/user/edit" do |env| + username = env.params.query["username"]? + admin = env.params.query["admin"]? + if admin + admin = admin == "true" + end + error = env.params.query["error"]? + current_user = get_username env + new_user = username.nil? && admin.nil? + layout "user-edit" +end + +post "/admin/user/edit" do |env| + # creating new user + begin + username = env.params.body["username"] + password = env.params.body["password"] + # if `admin` is unchecked, the body hash would not contain `admin` + admin = !env.params.body["admin"]?.nil? + + if username.size < 3 + raise "Username should contain at least 3 characters" + end + if password.size < 6 + raise "Password should contain at least 6 characters" + end + + storage.new_user username, password, admin + + env.redirect "/admin/user" + rescue e + puts e.message + redirect_url = URI.new \ + path: "/admin/user/edit",\ + query: hash_to_query({"error" => e.message}) + env.redirect redirect_url.to_s + end +end + +post "/admin/user/edit/:original_username" do |env| + # editing existing user + begin + username = env.params.body["username"] + password = env.params.body["password"] + # if `admin` is unchecked, the body hash would not contain `admin` + admin = !env.params.body["admin"]?.nil? + original_username = env.params.url["original_username"] + + if username.size < 3 + raise "Username should contain at least 3 characters" + end + if password.size != 0 && password.size < 6 + raise "Password should contain at least 6 characters" + end + + storage.update_user original_username, username, password, admin + + env.redirect "/admin/user" + rescue e + puts e.message + redirect_url = URI.new \ + path: "/admin/user/edit",\ + query: hash_to_query({"username" => original_username, \ + "admin" => admin, "error" => e.message}) + env.redirect redirect_url.to_s + end +end + get "/reader/:title/:entry" do |env| # We should save the reading progress, and ask the user if she wants to # start over or resume. For now we just start from page 0 @@ -72,18 +161,30 @@ get "/login" do |env| render "src/views/login.ecr" end -post "/login" do |env| - username = env.params.body["username"] - password = env.params.body["password"] - token = storage.verify_user username, password - if token.nil? +get "/logout" do |env| + begin + cookie = env.request.cookies.find { |c| c.name == "token" } + raise "" if cookie.nil? + storage.logout cookie.value + rescue + ensure env.redirect "/login" - next end +end - cookie = HTTP::Cookie.new "token", token - env.response.cookies << cookie - env.redirect "/" +post "/login" do |env| + begin + username = env.params.body["username"] + password = env.params.body["password"] + token = storage.verify_user username, password + raise "" if token.nil? + + cookie = HTTP::Cookie.new "token", token + env.response.cookies << cookie + env.redirect "/" + rescue + env.redirect "/login" + end end get "/api/page/:title/:entry/:page" do |env| @@ -128,7 +229,6 @@ get "/api/book" do |env| library.to_json end - add_handler AuthHandler.new storage Kemal.config.port = config.port diff --git a/src/storage.cr b/src/storage.cr index bfe6712..35c063b 100644 --- a/src/storage.cr +++ b/src/storage.cr @@ -70,4 +70,62 @@ class Storage end end end + + def verify_admin(token) + DB.open "sqlite3://#{@path}" do |db| + begin + return db.query_one "select admin from users where " \ + "token = (?)", token, as: Bool + rescue e : SQLite3::Exception | DB::Error + return false + end + end + end + + def list_users() + results = Array(Tuple(String, Bool)).new + DB.open "sqlite3://#{@path}" do |db| + db.query "select username, admin from users" do |rs| + rs.each do + results << {rs.read(String), rs.read(Bool)} + end + end + end + results + end + + def new_user(username, password, admin) + admin = (admin ? 1 : 0) + DB.open "sqlite3://#{@path}" do |db| + hash = hash_password password + db.exec "insert into users values (?, ?, ?, ?)", + username, hash, "", admin + end + end + + def update_user(original_username, username, password, admin) + admin = (admin ? 1 : 0) + DB.open "sqlite3://#{@path}" do |db| + if password.size == 0 + db.exec "update users set username = (?), admin = (?) "\ + "where username = (?)",\ + username, admin, original_username + else + hash = hash_password password + db.exec "update users set username = (?), admin = (?),"\ + "password = (?) where username = (?)",\ + username, admin, hash, original_username + end + end + end + + def logout(token) + DB.open "sqlite3://#{@path}" do |db| + begin + db.exec "update users set token = (?) where token = (?)", \ + "", token + rescue + end + end + end end diff --git a/src/views/admin.ecr b/src/views/admin.ecr new file mode 100644 index 0000000..0b5cc64 --- /dev/null +++ b/src/views/admin.ecr @@ -0,0 +1,24 @@ +