Authentication Alpha

master
Harald Wolff 2019-11-15 13:46:08 +01:00
parent 294c9736b4
commit 0be67ce4a1
13 changed files with 255 additions and 211 deletions

View File

@ -11,16 +11,17 @@ using ln.http.resources;
using ln.types.btree;
using ln.types.rpc;
using ln.application.slots;
using ln.identities;
using ln.http.session;
namespace ln.application
{
public class Application : IApplicationInterface
public class Application : ResourceApplication
{
public ArgumentContainer Arguments { get; protected set; }
public HTTPServer HttpServer { get; private set; }
public ServiceContainer ServiceContainer { get; }
public ResourceApplication HTTPApplication { get; private set; }
public MemoryLogger MemoryLogger { get; }
@ -29,22 +30,30 @@ namespace ln.application
public RPCContainer RPCContainer { get; } = new RPCContainer();
public BinarySlotContainer BinarySlots { get; } = new BinarySlotContainer();
public IIdentityProvider IdentityProvider { get; protected set; }
MappingBTree<Guid, ApplicationContext> applicationContexts = new MappingBTree<Guid, ApplicationContext>((x)=>x.ContextID);
MappingBTree<Guid, ApplicationSession> applicationSessions = new MappingBTree<Guid, ApplicationSession>((x)=>x.SessionID);
public Application()
public Application(IIdentityProvider identityProvider,RPCContainer rpcContainer)
{
IdentityProvider = identityProvider;
RPCContainer = rpcContainer;
MemoryLogger = new MemoryLogger(8192);
Logger.Default.Backends.Add(MemoryLogger);
Arguments = new ArgumentContainer()
.Add((char)0, "log-level", "INFO")
.Add('l', "http-listen","0.0.0.0")
.Add('l', "http-listen", "0.0.0.0")
.Add('p', "http-port", "8080");
ServiceContainer = new ServiceContainer(this);
}
public Application() :this(null,new RPCContainer()) { }
public Application(IIdentityProvider identityProvider) : this(identityProvider,new RPCContainer()) { }
public virtual void PrepareStart()
{
}
@ -68,8 +77,9 @@ namespace ln.application
));
HttpServer.Start();
HTTPApplication = new ResourceApplication();
HttpServer.DefaultApplication = HTTPApplication;
HttpServer.DefaultApplication = this;
RootResource = new BaseResource("");
/* Application Services */
@ -100,45 +110,64 @@ namespace ln.application
HttpServer.Stop();
}
public virtual ApplicationContext CreateApplicationContext() => new ApplicationContext(this);
public virtual ApplicationSession CreateApplicationSession() => new ApplicationSession(this);
public ApplicationSession GetApplicationSession(Session session)
{
ApplicationSession applicationSession = session.Get<ApplicationSession>("LNApplicationSession");
if (applicationSession == null)
{
applicationSession = CreateApplicationSession();
applicationSessions.Add(applicationSession);
session["LNApplicationSession"] = applicationSession;
}
public ApplicationContext GetApplicationContext() => GetApplicationContext(Guid.Empty);
public ApplicationContext GetApplicationContext(Guid contextID)
return applicationSession;
}
public ApplicationSession GetApplicationSession() => GetApplicationSession(Guid.Empty);
public ApplicationSession GetApplicationSession(Guid contextID)
{
ApplicationContext applicationContext;
ApplicationSession applicationSession;
if (Guid.Empty.Equals(contextID))
{
while (true)
{
applicationContext = CreateApplicationContext();
if (!applicationContexts.ContainsKey(applicationContext.ContextID))
applicationSession = CreateApplicationSession();
if (!applicationSessions.ContainsKey(applicationSession.SessionID))
{
applicationContexts.Add(applicationContext);
applicationSessions.Add(applicationSession);
break;
}
}
}
else
{
if (!applicationContexts.ContainsKey(contextID))
if (!applicationSessions.ContainsKey(contextID))
throw new KeyNotFoundException();
applicationContext = applicationContexts[contextID];
applicationSession = applicationSessions[contextID];
}
return applicationContext;
return applicationSession;
}
public virtual AuthenticatedUser AuthenticateUser(string username,object prove)
public virtual object ProcessMessage(object message)
{
//Application.BinarySlots.RequestSlot(message as SlotRequest);
return null;
}
public virtual Identity Authenticate(string username,object prove)
{
return null;
}
public void CleanupApplicationContexts()
{
foreach (ApplicationContext applicationContext in applicationContexts.ToArray())
foreach (ApplicationSession applicationContext in applicationSessions.ToArray())
{
if (applicationContext.Untouched > TimeSpan.FromSeconds(ApplicationContextTimeout))
applicationContexts.RemoveKey(applicationContext.ContextID);
applicationSessions.RemoveKey(applicationContext.SessionID);
}
}

View File

@ -1,56 +0,0 @@
using System;
using System.Threading;
namespace ln.application
{
public class ApplicationContext
{
static ThreadLocal<ApplicationContext> currentApplicationContext = new ThreadLocal<ApplicationContext>();
public static void SetCurrentContext(ApplicationContext applicationContext) => currentApplicationContext.Value = applicationContext;
public static void ClearCurrentContext() => currentApplicationContext.Value = null;
public Guid ContextID { get; }
public DateTime Created { get; }
public DateTime LastAccess { get; protected set; }
public TimeSpan Age => LastAccess - Created;
public TimeSpan Untouched => DateTime.Now - LastAccess;
public Application Application { get; }
public AuthenticatedUser CurrentUser { get; protected set; }
public ApplicationContext(Application application)
{
ContextID = Guid.NewGuid();
Created = DateTime.Now;
LastAccess = Created;
Application = application;
CurrentUser = AuthenticatedUser.Anonymous;
}
protected void UpdateLastAccess()
{
LastAccess = DateTime.Now;
}
public virtual bool AuthenticateUser(string username,object prove)
{
AuthenticatedUser authenticatedUser = Application.AuthenticateUser(username, prove);
if (authenticatedUser != null)
{
CurrentUser = authenticatedUser;
return true;
}
else
{
CurrentUser = AuthenticatedUser.Anonymous;
return false;
}
}
public virtual void DeauthenticateUser()
{
CurrentUser = AuthenticatedUser.Anonymous;
}
}
}

View File

@ -0,0 +1,90 @@
using System;
using System.Threading;
using ln.identities;
using ln.types.rpc;
using System.Collections.Generic;
using ln.logging;
using ln.json;
using ln.json.mapping;
namespace ln.application
{
public class ApplicationSession : IDisposable
{
static ThreadLocal<ApplicationSession> currentApplicationSession = new ThreadLocal<ApplicationSession>();
public static void SetCurrentSession(ApplicationSession applicationSession) => currentApplicationSession.Value = applicationSession;
public static void ClearCurrentSession() => currentApplicationSession.Value = null;
public static ApplicationSession CurrentSession => currentApplicationSession.Value;
public Guid SessionID { get; }
public DateTime Created { get; }
public DateTime LastAccess { get; protected set; }
public TimeSpan Age => LastAccess - Created;
public TimeSpan Untouched => DateTime.Now - LastAccess;
public Application Application { get; }
public Identity SessionIdentity { get; protected set; }
public ApplicationSession(Application application)
{
SessionID = Guid.NewGuid();
Created = DateTime.Now;
LastAccess = Created;
Application = application;
SessionIdentity = null;
Logging.Log(LogLevel.DEBUG, "ApplicationSession created: {0}",SessionID);
}
protected void UpdateLastAccess()
{
LastAccess = DateTime.Now;
}
public virtual object ProcessMessage(object message)
{
if (message is RPCCall rpcCall)
{
return Application.RPCContainer.Invoke(rpcCall);
} else if (message is AuthenticationRequest authenticationRequest)
{
Identity identity = Application.IdentityProvider.GetIdentity(authenticationRequest.IdentityName);
SecureAttribute[] secureAttributes = identity.GetSecureAttributes(authenticationRequest.SecureAttributeTypeName);
AuthenticationChallenges authenticationChallenges = new AuthenticationChallenges(secureAttributes);
return authenticationChallenges;
} else if (message is AuthenticationProve authenticationProve)
{
Identity identity = Application.IdentityProvider.GetIdentity(authenticationProve.IdentityName);
SecureAttribute secureAttribute = identity.GetSecureAttribute(authenticationProve.SecureAttributeUniqueID);
if (secureAttribute.Authenticate(authenticationProve.Challenge, authenticationProve.Prove))
{
SessionIdentity = identity;
JSONObject r = new JSONObject();
r.Add("UniqueID", JSONMapper.DefaultMapper.ToJson(identity.UniqueID));
r.Add("IdentityName", JSONMapper.DefaultMapper.ToJson(identity.IdentityName));
return r;
}
else
{
throw new ArgumentException();
}
throw new KeyNotFoundException();
}
return null;
}
public virtual void DeauthenticateUser()
{
SessionIdentity = null;
}
public virtual void Dispose()
{
Logging.Log(LogLevel.DEBUG, "ApplicationSession disposing: {0}", SessionID);
DeauthenticateUser();
}
}
}

View File

@ -6,28 +6,33 @@ using ln.types.rpc;
using ln.http;
using System.Collections.Generic;
using ln.application.slots;
using ln.json.mapping;
using ln.identities;
namespace ln.application
{
public class ApplicationWebSocket : WebSocket
{
public IApplicationInterface ApplicationInterface { get; }
static Dictionary<string, Func<JSONValue, object>> messageConverters = new Dictionary<string, Func<JSONValue, object>>();
static public void RegisterMessageConverter(string messageType, Func<JSONValue, object> converter) => messageConverters[messageType] = converter;
static public Func<JSONValue, object> FindConverter(string messageType) => messageConverters[messageType];
Dictionary<string, Func<JSONValue, object>> messageConverters = new Dictionary<string, Func<JSONValue, object>>();
public Application Application { get; }
public ApplicationSession ApplicationSession => Application.GetApplicationSession();
public ApplicationWebSocket(IApplicationInterface applicationInterface,HttpRequest httpRequest)
:base(httpRequest)
{
ApplicationInterface = applicationInterface;
}
public void AddMessageConverter(string messageType, Func<JSONValue, object> converter)
static ApplicationWebSocket()
{
messageConverters[messageType] = converter;
RegisterMessageConverter("RPCCall", (JSONValue json) => JSONMapper.DefaultMapper.FromJson(json, typeof(RPCCall)));
RegisterMessageConverter("SlotRequest", (JSONValue json) => JSONMapper.DefaultMapper.FromJson(json, typeof(SlotRequest)));
RegisterMessageConverter("AuthenticationProve", (JSONValue json) => JSONMapper.DefaultMapper.FromJson(json, typeof(AuthenticationProve)));
RegisterMessageConverter("AuthenticationRequest", (JSONValue json) => JSONMapper.DefaultMapper.FromJson(json, typeof(AuthenticationRequest)));
}
public Func<JSONValue, object> FindConverter(string messageType)
public ApplicationWebSocket(Application application, HttpRequest httpRequest)
: base(httpRequest)
{
return messageConverters[messageType];
Application = application;
}
public override bool Received(string textMessage)
@ -59,27 +64,41 @@ namespace ln.application
try
{
object convertedMessage = FindConverter(messageType)(message);
return ReceivedMessage(messageId, messageType, convertedMessage);
ReceivedMessage(messageId, messageType, convertedMessage);
return true;
} catch (Exception e)
{
Logging.Log(e);
SendError(messageId, e.ToString(), e.InnerException.ToString());
SendError(messageId, e.ToString(), e.InnerException?.ToString());
return false;
}
}
public virtual bool ReceivedMessage(long messageId, string messageType, object message)
public virtual void ReceivedMessage(long messageId, string messageType, object message)
{
switch (messageType)
ApplicationSession.SetCurrentSession(ApplicationSession);
try
{
case "RPCCall":
SendMessage(messageId, ApplicationInterface.RPCContainer.Invoke(message as RPCCall));
return true;
case "SlotRequest":
SlotAllocation slotAllocation = ApplicationInterface.BinarySlots.RequestSlot(message as SlotRequest);
return true;
default:
return false;
object result = ApplicationSession?.ProcessMessage(message);
if (result is null)
{
result = Application.ProcessMessage(message);
}
if (result is null)
{
throw new NotImplementedException();
}
SendMessage(messageId, result);
} catch (Exception e)
{
SendError(messageId, e.ToString(), e.InnerException?.ToString());
}
finally
{
ApplicationSession.ClearCurrentSession();
}
}
@ -91,7 +110,7 @@ namespace ln.application
public void SendMessage(long messageID, object message)
{
string messageType = message.GetType().Name;
JSONObject jsonMessage = JSONObject.From(message);
JSONObject jsonMessage = message is JSONObject ? message as JSONObject : JSONObject.From(message);
SendMessage(messageID, messageType, jsonMessage);
}
@ -117,35 +136,6 @@ namespace ln.application
SendMessage(messageId, "error", failed);
}
public void a()
{
try
{
Logging.Log(LogLevel.DEBUGDETAIL, "ApplicationWebSocket: Received: {0}",textMessage);
JSONObject json = (JSONObject)JSONParser.Parse(textMessage);
RPCCall rpcCall = json.ToObject<RPCCall>();
requestContext.HttpRequest.HTTPServer.ThreadPool.Enqueue(() =>
{
RPCResult rpcResult = Invoke(rpcCall);
string resultText = JSONObject.From(rpcResult).ToString();
Logging.Log(LogLevel.DEBUGDETAIL, "ApplicationWebSocket: Sending: {0}", resultText);
requestContext.WebSocket.Send(resultText);
});
}
catch (Exception e)
{
Logging.Log(e);
}
}
public virtual RPCResult Invoke(RPCCall call)
{
RPCResult result = null;
result = ApplicationInterface.RPCContainer.Invoke(call);
return result;
}
}
}

View File

@ -1,64 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
namespace ln.application
{
public class AuthenticatedUser
{
public static AuthenticatedUser Anonymous { get; } = new AuthenticatedUser();
public Guid ID { get; }
public string Name { get; }
public byte[] SecretHash { get; set; }
public int Token { get; set; }
public string[] Roles => Roles.ToArray();
HashSet<string> roles = new HashSet<string>();
public AuthenticatedUser()
{
ID = Guid.Empty;
Name = "Anonymous";
}
public AuthenticatedUser(Guid userID, string name) : this(userID, name, new string[0]) { }
public AuthenticatedUser(Guid userID, string name, IEnumerable<string> userRoles)
{
ID = userID;
Name = name;
foreach (string role in userRoles)
roles.Add(role);
}
public bool HasRole(string role) => roles.Contains(role);
public void SetSecret(string secret)
{
byte[] secretBytes = Encoding.UTF8.GetBytes(secret);
SHA256 sha256 = SHA256.Create();
sha256.ComputeHash(secretBytes);
SecretHash = sha256.Hash;
}
public override int GetHashCode()
{
return ID.GetHashCode();
}
public override bool Equals(object obj)
{
if (obj is AuthenticatedUser)
{
AuthenticatedUser other = obj as AuthenticatedUser;
return ID.Equals(other.ID);
}
return false;
}
}
}

View File

@ -37,8 +37,7 @@
<Compile Include="service\ServiceContainer.cs" />
<Compile Include="service\ApplicationServiceBase.cs" />
<Compile Include="ApplicationWebSocket.cs" />
<Compile Include="ApplicationContext.cs" />
<Compile Include="AuthenticatedUser.cs" />
<Compile Include="ApplicationSession.cs" />
<Compile Include="slots\BinarySlot.cs" />
<Compile Include="slots\SlotRequest.cs" />
<Compile Include="slots\BinarySlotContainer.cs" />
@ -65,6 +64,10 @@
<Project>{D9342117-3249-4D8B-87C9-51A50676B158}</Project>
<Name>ln.json</Name>
</ProjectReference>
<ProjectReference Include="..\ln.identities\ln.identities.csproj">
<Project>{AA1F75AF-0AEC-4CC0-AC3B-209AE90781AC}</Project>
<Name>ln.identities</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Folder Include="service\" />

View File

@ -12,10 +12,17 @@ using ln.http.resources;
using ln.application.service;
using ln.http;
using ln.http.listener;
using ln.http.resources.websocket;
namespace ln.application.demo
{
public class ApplicationDemo : Application
{
public ApplicationDemo()
:base(new DemoIdentityProvider())
{
}
public override void PrepareStart()
{
base.PrepareStart();
@ -29,17 +36,20 @@ namespace ln.application.demo
public ApplicationService() : base("core") { }
public override void ServiceMain(IApplicationInterface applicationInterface)
public override void ServiceMain(Application application)
{
DirectoryResource directoryResource = new DirectoryResource(new string[] { "../../www", "www" });
DirectoryResource directoryResource = new DirectoryResource(new string[] { "../../../www", "www" });
directoryResource.DefaultResource = directoryResource.GetResource("demo.html");
directoryResource.FallBackResource = directoryResource.DefaultResource;
CurrentApplicationInterface.HTTPApplication.RootResource = directoryResource;
CurrentApplication.RootResource = directoryResource;
WebsocketResource websocketResource = new WebsocketResource(null, "socket", (HttpRequest arg) => new ApplicationWebSocket((Application)this.CurrentApplication, arg));
directoryResource.InjectResource(websocketResource);
//applicationInterface.HttpServer.AddListener(new HttpsListener(8443));
base.ServiceMain(applicationInterface);
base.ServiceMain(application);
}

View File

@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using ln.identities;
using System.Linq;
namespace ln.application.demo
{
public class DemoIdentityProvider : BaseIdentityProvider
{
Dictionary<Guid, Identity> identities = new Dictionary<Guid, Identity>();
public DemoIdentityProvider()
{
Identity demoIdentity = new Identity("demo");
demoIdentity.AddSecureAttribute(new SeededPassword("demopass"));
identities.Add(demoIdentity.UniqueID,demoIdentity);
}
public override Identity CreateIdentity(string identityName)
{
Identity identity = new Identity(identityName);
identities.Add(identity.UniqueID, identity);
return identity;
}
public override IEnumerable<KeyValuePair<Guid, string>> GetIdentities() => identities.Select((arg) => new KeyValuePair<Guid, string>(arg.Key,arg.Value.IdentityName));
public override Identity GetIdentity(Guid uniqueID) => identities[uniqueID];
public override bool Save(Identity identity) => true;
}
}

View File

@ -33,6 +33,7 @@
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ApplicationDemo.cs" />
<Compile Include="DemoIdentityProvider.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ln.application.csproj">
@ -63,6 +64,10 @@
<Project>{8D9AB9A5-E513-4BA7-A450-534F6456BF28}</Project>
<Name>ln.types</Name>
</ProjectReference>
<ProjectReference Include="..\..\ln.identities\ln.identities.csproj">
<Project>{AA1F75AF-0AEC-4CC0-AC3B-209AE90781AC}</Project>
<Name>ln.identities</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Folder Include="www\" />

View File

@ -17,6 +17,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ln.types", "..\ln.types\ln.
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ln.application.demo", "ln.application.demo\ln.application.demo.csproj", "{2CBEE2D5-C4D4-4DBD-8AA4-B54E99812808}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ln.identities", "..\ln.identities\ln.identities.csproj", "{AA1F75AF-0AEC-4CC0-AC3B-209AE90781AC}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -55,5 +57,9 @@ Global
{2CBEE2D5-C4D4-4DBD-8AA4-B54E99812808}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2CBEE2D5-C4D4-4DBD-8AA4-B54E99812808}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2CBEE2D5-C4D4-4DBD-8AA4-B54E99812808}.Release|Any CPU.Build.0 = Release|Any CPU
{AA1F75AF-0AEC-4CC0-AC3B-209AE90781AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AA1F75AF-0AEC-4CC0-AC3B-209AE90781AC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AA1F75AF-0AEC-4CC0-AC3B-209AE90781AC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AA1F75AF-0AEC-4CC0-AC3B-209AE90781AC}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

View File

@ -22,7 +22,7 @@ namespace ln.application.service
public string ServiceStateText { get; protected set; }
public IApplicationInterface CurrentApplicationInterface { get; protected set; }
public Application CurrentApplication { get; protected set; }
public Type[] DependingServiceTypes => dependingServiceTypes.ToArray();
HashSet<Type> dependingServiceTypes = new HashSet<Type>();
@ -42,14 +42,14 @@ namespace ln.application.service
public T Dependency<T>() where T : ApplicationServiceBase
{
T serviceBase = (T)CurrentApplicationInterface.ServiceContainer[typeof(T)].ServiceBase;
T serviceBase = (T)CurrentApplication.ServiceContainer[typeof(T)].ServiceBase;
if (serviceBase == null)
throw new EntryPointNotFoundException(String.Format("depending service not found: {0}",typeof(T).ToString()));
return serviceBase;
}
public virtual void ServiceMain(IApplicationInterface applicationInterface)
public virtual void ServiceMain(Application application)
{
Ready();
while (!StopRequested)
@ -61,19 +61,19 @@ namespace ln.application.service
}
}
public virtual bool Start(IApplicationInterface applicationInterface, String[] arguments)
public virtual bool Start(Application application, String[] arguments)
{
if (IsAlive)
throw new NotSupportedException("Service is already alive");
CurrentApplicationInterface = applicationInterface;
CurrentApplication = application;
foreach (Type dep in dependingServiceTypes)
{
ServiceDefinition depService = CurrentApplicationInterface.ServiceContainer[dep];
ServiceDefinition depService = CurrentApplication.ServiceContainer[dep];
if (!depService.IsAlive)
{
CurrentApplicationInterface.ServiceContainer.Start(depService);
CurrentApplication.ServiceContainer.Start(depService);
if (!depService.IsAlive)
{
Logging.Log(LogLevel.ERROR, "service {0} could not start depending service {1}", ServiceName, depService.ServiceClassName);
@ -97,7 +97,7 @@ namespace ln.application.service
ready = false;
StopRequested = false;
ServiceThread = new Thread(() => ServiceMain(applicationInterface));
ServiceThread = new Thread(() => ServiceMain(application));
ServiceThread.Start();
return true;
@ -131,7 +131,7 @@ namespace ln.application.service
}
if (!ServiceThread.IsAlive)
CurrentApplicationInterface = null;
CurrentApplication = null;
ready = false;
return !ServiceThread.IsAlive;

View File

@ -75,14 +75,14 @@ namespace ln.application.service
serviceType = null;
serviceAssembly = null;
}
public void Start(IApplicationInterface applicationInterface)
public void Start(Application application)
{
if (!IsLoaded)
Load();
Logging.Log(LogLevel.INFO, "service: starting {0} [{1}]", ServiceClassName, FinalAssembly.GetName().Name);
if (ServiceBase.Start(applicationInterface,new string[0]))
if (ServiceBase.Start(application,new string[0]))
Logging.Log(LogLevel.INFO, "service: started {0} [{1}]", ServiceClassName, FinalAssembly.GetName().Name);
else
Logging.Log(LogLevel.ERROR, "service: failed {0} [{1}]", ServiceClassName, FinalAssembly.GetName().Name);

2
www

@ -1 +1 @@
Subproject commit d1663cbbbe0d91631c4bd6b7b0f7228621329705
Subproject commit 6815c42b253152f120fd74a1f0cdc0bebf071547