From be6dc557a3b819248b014992eb96c1cb21f8112b Mon Sep 17 00:00:00 2001 From: vnugent Date: Sun, 8 Jan 2023 14:44:01 -0500 Subject: Initial commit --- Plugins.Essentials/src/Sessions/SessionInfo.cs | 231 +++++++++++++++++++++++++ 1 file changed, 231 insertions(+) create mode 100644 Plugins.Essentials/src/Sessions/SessionInfo.cs (limited to 'Plugins.Essentials/src/Sessions/SessionInfo.cs') diff --git a/Plugins.Essentials/src/Sessions/SessionInfo.cs b/Plugins.Essentials/src/Sessions/SessionInfo.cs new file mode 100644 index 0000000..13e2a84 --- /dev/null +++ b/Plugins.Essentials/src/Sessions/SessionInfo.cs @@ -0,0 +1,231 @@ +/* +* Copyright (c) 2022 Vaughn Nugent +* +* Library: VNLib +* Package: VNLib.Plugins.Essentials +* File: SessionInfo.cs +* +* SessionInfo.cs is part of VNLib.Plugins.Essentials which is part of the larger +* VNLib collection of libraries and utilities. +* +* VNLib.Plugins.Essentials is free software: you can redistribute it and/or modify +* it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* VNLib.Plugins.Essentials is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Affero General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ + +using System; +using System.Net; +using System.Security.Authentication; +using System.Runtime.CompilerServices; + +using VNLib.Utils; +using VNLib.Net.Http; +using VNLib.Utils.Extensions; +using static VNLib.Plugins.Essentials.Statics; + +/* + * SessionInfo is a structure since it is only meant used in + * an HttpEntity context, so it may be allocated as part of + * the HttpEntity object, so have a single larger object + * passed by ref, and created once per request. It may even + * be cached and reused in the future. But for now user-apis + * should not be cached until a safe use policy is created. + */ + +#pragma warning disable CA1051 // Do not declare visible instance fields + +namespace VNLib.Plugins.Essentials.Sessions +{ + /// + /// When attached to a connection, provides persistant session storage and inforamtion based + /// on a connection. + /// + public readonly struct SessionInfo : IObjectStorage, IEquatable + { + /// + /// A value indicating if the current instance has been initiailzed + /// with a session. Otherwise properties are undefied + /// + public readonly bool IsSet; + + private readonly ISession UserSession; + /// + /// Key that identifies the current session. (Identical to cookie::sessionid) + /// + public readonly string SessionID; + /// + /// Session stored User-Agent + /// + public readonly string UserAgent; + /// + /// If the stored IP and current user's IP matches + /// + public readonly bool IPMatch; + /// + /// If the current connection and stored session have matching cross origin domains + /// + public readonly bool CrossOriginMatch; + /// + /// Flags the session as invalid. IMPORTANT: the user's session data is no longer valid and will throw an exception when accessed + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Invalidate(bool all = false) => UserSession.Invalidate(all); + /// + /// Marks the session ID to be regenerated during closing event + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void RegenID() => UserSession.RegenID(); + /// + public T GetObject(string key) + { + //Attempt to deserialze the object, or return default if it is empty + return this[key].AsJsonObject(SR_OPTIONS); + } + /// + public void SetObject(string key, T obj) + { + //Serialize and store the object, or set null (remove) if the object is null + this[key] = obj?.ToJsonString(SR_OPTIONS); + } + + /// + /// Was the original session cross origin? + /// + public readonly bool CrossOrigin; + /// + /// The origin header specified during session creation + /// + public readonly Uri SpecifiedOrigin; + /// + /// Privilages associated with user specified during login + /// + public readonly DateTimeOffset Created; + /// + /// Was this session just created on this connection? + /// + public readonly bool IsNew; + /// + /// Gets or sets the session's login hash, if set to a non-empty/null value, will trigger an upgrade on close + /// + public readonly string LoginHash + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => UserSession.GetLoginToken(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set => UserSession.SetLoginToken(value); + } + /// + /// Gets or sets the session's login token, if set to a non-empty/null value, will trigger an upgrade on close + /// + public readonly string Token + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => UserSession.Token; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set => UserSession.Token = value; + } + /// + /// + /// Gets or sets the user-id for the current session. + /// + /// + /// Login code usually sets this value and it should be read-only + /// + /// + public readonly string UserID + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => UserSession.UserID; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set => UserSession.UserID = value; + } + /// + /// Privilages associated with user specified during login + /// + public readonly ulong Privilages + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => UserSession.Privilages; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set => UserSession.Privilages = value; + } + /// + /// The IP address belonging to the client + /// + public readonly IPAddress UserIP; + /// + /// Was the session Initialy established on a secure connection? + /// + public readonly SslProtocols SecurityProcol; + /// + /// A value specifying the type of the backing session + /// + public readonly SessionType SessionType => UserSession.SessionType; + + /// + /// Accesses the session's general storage + /// + /// Key for specifie data + /// Value associated with the key from the session's general storage + public readonly string this[string index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => UserSession[index]; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set => UserSession[index] = value; + } + + internal SessionInfo(ISession session, IConnectionInfo ci, IPAddress trueIp) + { + UserSession = session; + //Calculate and store + IsNew = session.IsNew; + SessionID = session.SessionID; + Created = session.Created; + UserIP = session.UserIP; + //Ip match + IPMatch = trueIp.Equals(session.UserIP); + //If the session is new, we can store intial security variables + if (session.IsNew) + { + session.InitNewSession(ci); + //Since all values will be the same as the connection, cache the connection values + UserAgent = ci.UserAgent; + SpecifiedOrigin = ci.Origin; + CrossOrigin = ci.CrossOrigin; + SecurityProcol = ci.SecurityProtocol; + } + else + { + //Load/decode stored variables + UserAgent = session.GetUserAgent(); + SpecifiedOrigin = session.GetOriginUri(); + CrossOrigin = session.IsCrossOrigin(); + SecurityProcol = session.GetSecurityProtocol(); + } + CrossOriginMatch = ci.Origin != null && ci.Origin.Equals(SpecifiedOrigin); + IsSet = true; + } + + /// + public bool Equals(SessionInfo other) => SessionID.Equals(other.SessionID, StringComparison.Ordinal); + /// + public override bool Equals(object obj) => obj is SessionInfo si && Equals(si); + /// + public override int GetHashCode() => SessionID.GetHashCode(StringComparison.Ordinal); + /// + public static bool operator ==(SessionInfo left, SessionInfo right) => left.Equals(right); + /// + public static bool operator !=(SessionInfo left, SessionInfo right) => !(left == right); + + } +} \ No newline at end of file -- cgit