diff options
Diffstat (limited to 'Plugins.Essentials/src/Sessions/SessionInfo.cs')
-rw-r--r-- | Plugins.Essentials/src/Sessions/SessionInfo.cs | 231 |
1 files changed, 231 insertions, 0 deletions
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 +{ + /// <summary> + /// When attached to a connection, provides persistant session storage and inforamtion based + /// on a connection. + /// </summary> + public readonly struct SessionInfo : IObjectStorage, IEquatable<SessionInfo> + { + /// <summary> + /// A value indicating if the current instance has been initiailzed + /// with a session. Otherwise properties are undefied + /// </summary> + public readonly bool IsSet; + + private readonly ISession UserSession; + /// <summary> + /// Key that identifies the current session. (Identical to cookie::sessionid) + /// </summary> + public readonly string SessionID; + /// <summary> + /// Session stored User-Agent + /// </summary> + public readonly string UserAgent; + /// <summary> + /// If the stored IP and current user's IP matches + /// </summary> + public readonly bool IPMatch; + /// <summary> + /// If the current connection and stored session have matching cross origin domains + /// </summary> + public readonly bool CrossOriginMatch; + /// <summary> + /// Flags the session as invalid. IMPORTANT: the user's session data is no longer valid and will throw an exception when accessed + /// </summary> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Invalidate(bool all = false) => UserSession.Invalidate(all); + /// <summary> + /// Marks the session ID to be regenerated during closing event + /// </summary> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void RegenID() => UserSession.RegenID(); + ///<inheritdoc/> + public T GetObject<T>(string key) + { + //Attempt to deserialze the object, or return default if it is empty + return this[key].AsJsonObject<T>(SR_OPTIONS); + } + ///<inheritdoc/> + public void SetObject<T>(string key, T obj) + { + //Serialize and store the object, or set null (remove) if the object is null + this[key] = obj?.ToJsonString(SR_OPTIONS); + } + + /// <summary> + /// Was the original session cross origin? + /// </summary> + public readonly bool CrossOrigin; + /// <summary> + /// The origin header specified during session creation + /// </summary> + public readonly Uri SpecifiedOrigin; + /// <summary> + /// Privilages associated with user specified during login + /// </summary> + public readonly DateTimeOffset Created; + /// <summary> + /// Was this session just created on this connection? + /// </summary> + public readonly bool IsNew; + /// <summary> + /// Gets or sets the session's login hash, if set to a non-empty/null value, will trigger an upgrade on close + /// </summary> + public readonly string LoginHash + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => UserSession.GetLoginToken(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set => UserSession.SetLoginToken(value); + } + /// <summary> + /// Gets or sets the session's login token, if set to a non-empty/null value, will trigger an upgrade on close + /// </summary> + public readonly string Token + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => UserSession.Token; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set => UserSession.Token = value; + } + /// <summary> + /// <para> + /// Gets or sets the user-id for the current session. + /// </para> + /// <para> + /// Login code usually sets this value and it should be read-only + /// </para> + /// </summary> + public readonly string UserID + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => UserSession.UserID; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set => UserSession.UserID = value; + } + /// <summary> + /// Privilages associated with user specified during login + /// </summary> + public readonly ulong Privilages + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => UserSession.Privilages; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set => UserSession.Privilages = value; + } + /// <summary> + /// The IP address belonging to the client + /// </summary> + public readonly IPAddress UserIP; + /// <summary> + /// Was the session Initialy established on a secure connection? + /// </summary> + public readonly SslProtocols SecurityProcol; + /// <summary> + /// A value specifying the type of the backing session + /// </summary> + public readonly SessionType SessionType => UserSession.SessionType; + + /// <summary> + /// Accesses the session's general storage + /// </summary> + /// <param name="index">Key for specifie data</param> + /// <returns>Value associated with the key from the session's general storage</returns> + 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; + } + + ///<inheritdoc/> + public bool Equals(SessionInfo other) => SessionID.Equals(other.SessionID, StringComparison.Ordinal); + ///<inheritdoc/> + public override bool Equals(object obj) => obj is SessionInfo si && Equals(si); + ///<inheritdoc/> + public override int GetHashCode() => SessionID.GetHashCode(StringComparison.Ordinal); + ///<inheritdoc/> + public static bool operator ==(SessionInfo left, SessionInfo right) => left.Equals(right); + ///<inheritdoc/> + public static bool operator !=(SessionInfo left, SessionInfo right) => !(left == right); + + } +}
\ No newline at end of file |