require "yaml" require_relative "./auth.rb" require_relative "./csrf.rb" class LifePex::Systems::UserSystem < LifePex::Systems::AuthSystem include LifePex::Systems::CrlfHelper DEFAULT_PEXS_FOR_NEW_USERS = YAML.load_file "config/default_pexs_for_new_users.yaml" get "/login" do slim :login end def login(params) user = LifePex::User.where(username: params["username"]).first if user && BCrypt::Password.new(user[:hashed_password]) == params["password"] setup_user_cookie!(user[:id]) user else nil end end post "/login", provides: 'html' do if user = login(params) redirect "/" else slim :login, locals: { flash: { danger: "Failed to login" } } end end get "/register" do slim :register end def register(params) user = LifePex::User.where(username: params["username"]).first if !user hashed_password = BCrypt::Password.create params["password"] user = LifePex::User.new(username: params["username"], hashed_password: hashed_password).save cookies["auth"] = JWT.encode({ "user_id" => user.id }, LifePex::SECRET) DEFAULT_PEXS_FOR_NEW_USERS.each do |pex| LifePex::Pex.new(**pex, user_id: user.id).save end user.id else nil end end post "/register", provides: 'html' do if user_id = register(params) redirect "/" else slim :register, locals: { flash: { danger: "Failed to register as #{params['username']}" } } end end post "/logout", auth: [] do cookies.delete "auth" slim :logout, locals: { flash: { success: "Logged out" } } end get "/password", auth: [] do slim :password end def change_password(params) hashed_password = BCrypt::Password.create params["password"] LifePex::User.where(id: current_user_id).update(hashed_password: hashed_password) end post "/password", auth: [], provides: 'html' do if params["password"] != params["password_confirmation"] slim :password, locals: { flash: { warning: "Password don't match" } } else change_password(params) slim :password, locals: { flash: { success: "Password updated" } } end end get "/about" do slim :about end get "/preferences" do show_preferences end UserPreferenceCookie = Struct.new(:cookie, :allow_blank, :convert, :html, :description) USER_PREFERENCES = { "inputRecapDays" => UserPreferenceCookie.new("recap_days", false, :to_i, { type: "text" }, "Amount of days to show in the recap"), "inputLateDayOffset" => UserPreferenceCookie.new("late_day_offset", false, :to_f, { type: "number", min: "0", max: "24", step: "0.1" }, "Offset for today (so a few hours after midnight is still today)"), "inputShowFullDate" => UserPreferenceCookie.new("show_full_date", true, :to_s, { type: "checkbox" }, "Show or hide the date of the current tab"), } post "/preferences" do USER_PREFERENCES.each do |param_name, upc| current_param = params[param_name] next if !upc.allow_blank && current_param.blank? cookies[upc.cookie] = case upc.convert when Symbol current_param.send(upc.convert) when Proc upc.convert.call(current_param) else current_param end end show_preferences end def show_preferences slim :preferences end extend DocMyRoutes::Annotatable register Sinatra::Namespace namespace '/api/user/v1' do summary 'Generate a authentication cookie for a registrated account' notes '' produces 'application/json' status_codes [200] parameter :username, required: true, type: 'string', in: 'body' parameter :password, required: true, type: 'string', in: 'body' post "/login", provides: 'json' do if user = login(json_params) { message: 'Logged in' }.to_json else halt 400, { message: 'Invalid credentials' }.to_json end end summary 'Register a new account if the username does not exists already' notes '' produces 'application/json' status_codes [200] parameter :username, required: true, type: 'string', in: 'body' parameter :password, required: true, type: 'string', in: 'body' post "/register", provides: 'json' do if user_id = register(json_params) { message: "Registered as user_id=#{user_id}", user: { id: user_id, username: json_params["username"] } }.to_json else halt 400, { message: "Failed to register as #{json_params['username']}" }.to_json end end summary 'Change the authentication password of the account' produces 'application/json' status_codes [200] parameter :password, required: true, type: 'string', in: 'body' parameter :auth, required: true, type: 'string', in: 'cookies' post "/password", auth: [], provides: 'html' do change_password(json_params) { message: 'password changed' }.to_json end end include LifePex::Systems::ApiList end