aboutsummaryrefslogtreecommitdiff
path: root/Plugins.Essentials/src/Sessions/SessionInfo.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Plugins.Essentials/src/Sessions/SessionInfo.cs')
-rw-r--r--Plugins.Essentials/src/Sessions/SessionInfo.cs231
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