1 line
3.7 KiB
JavaScript
1 line
3.7 KiB
JavaScript
|
|
import cookie from"cookie";import{randomString,testPassword,getSalt}from"./core/auth.mjs";import{USER_ROLES}from"../common/constants.mjs";class ClientSessions{constructor(){this.sessions=new Map}static COOKIE_MAX_AGE=864e5;assign(s,e){s.cookie("session",e.id,{maxAge:this.constructor.COOKIE_MAX_AGE,sameSite:"strict"})}expire(s,e){const t=this.get(s,e);global.logger.info(`Expiring client session ${t.id}`),this.sessions.delete(t.id),e.clearCookie("session")}get(s,e){const t=s.headers.cookie?cookie.parse(s.headers.cookie):null;if(!t||!t.session)return null;const o=this.sessions.get(t.session);if(!o)return null;return Date.now()>o.expires?(this.expire(s,e),null):o}getOrCreate(s,e){if(s.session)return s.session;let t=this.get(s,e);return t||(t=this._create(),this.assign(e,t),global.logger.info(`Created client session ${t.id}`)),s.session=t}_create({world:s=null,user:e=null,admin:t=!1}={}){const o=randomString(24),i=new Date,r=i.setDate(i.getDate()+1),n={};s&&e&&(n[s]=e);const a={id:o,admin:t,expires:r,worlds:n,messages:[]};this.sessions.set(o,a);for(let s of global.startupMessages)["error","warn"].includes(s.level)&&a.messages.push({type:s.level,message:s.message,options:{permanent:!0}});return a}async authenticateUser(s,e){const{game:t}=global;if(!t.ready||!t.world)throw new Error("You cannot log until a game World is ready for play.");const o=this.getOrCreate(s,e),{userid:i,password:r}=s.body,n=await db.User.get(i);if(!n)return e.status(401),e.send("JOIN.ErrorUserDoesNotExist"),{request:"join",status:"failed"};let a;try{a=testPassword(r,n.password,n.passwordSalt)}catch(s){a=!1}return a?n.role===USER_ROLES.NONE?(e.status(401),e.send("JOIN.ErrorBanned"),{request:"join",status:"failed"}):(o.worlds[t.world.id]=n.id,logger.info(`User authentication successful for user ${n.name}`,{session:o.id,ip:s.ip}),t.world.onUserLogin(n),{request:"join",status:"success",message:"JOIN.LoginSuccess",redirect:s.baseUrl+"/game"}):(logger.warn(`User authentication failed for user ${n.name}; invalid password`,{status:401,session:o.id,ip:s.ip}),e.status(401),e.send("JOIN.ErrorInvalidPassword"),{request:"join",status:"failed"})}authenticateAdmin(s,e){const{logger:t}=config,o=this.getOrCreate(s,e),i=config.adminPassword;if(o.admin||!i)return{success:!0,session:o};let r=!1;return s.body?.hasOwnProperty("adminPassword")&&(r=testPassword(s.body.adminPassword,i,getSalt(config.passwordSalt)),r?(t.info(`Administrator authentication successful for session ${o.id}`),o.messages.push({type:"info",message:"Admin authentication successful"})):(e.status(403),t.warn(`Administrator authentication failed for session ${o.id}; invalid password`,{status:403,ip:s.ip}),o.messages.push({type:"error",message:"ERROR.InvalidAdminKey",options:{permanent:!0}}))),o.admin=r,{success:r,session:o}}getMessages(s,{clear:e=!0}={}){if(!s.session)return null;const t=s.session.messages;return e&&(s.session.messages=[]),t.length?JSON.stringify(t):null}setMessage(s,e,t,o){const i=this.get(s,e);i&&i.messages.push({type:t,message:o})}logoutAdmin(s,e){const t=this.get(s,e);t&&(t.admin=!1,global.logger.info(`Revoked admin access for session ${t.id}`))}async logoutWorld(s,e){const{game:t,logger:o}=globalThis;if(!t.active)return;const i=this.get(s,e);if(!i)return;const r=i.worlds[t.world.id];if(delete i.worlds[t.world.id],r){const s=await db.User.get(r);if(!s)return;t.activity.deactivateUser(s)&&(o.info(`Logged user ${r} out of active World`),t.world.onUserLogout(s))}}deactivateUserSession(s){for(let e of this.getUserSessions(s))global.logger.info(`Deactivating session ${e.id} for User ${s}`),this.sessions.delete(e.id)}getUserSessions(s){const e=[];for(let t of this.sessions.values())t.worlds[game.world.id]===s&&e.push(t);return e}}export default new ClientSessions;
|