2
0
mirror of https://github.com/esiur/esiur-dotnet.git synced 2025-06-27 05:23:13 +00:00
This commit is contained in:
2024-06-21 16:37:48 +03:00
parent c04fc29810
commit 3a89a108db
49 changed files with 2095 additions and 1373 deletions

View File

@ -7,7 +7,7 @@ namespace Esiur.Security.Authority;
public enum AuthenticationMethod : byte
{
None,
Certificate,
Credentials,
Token
Token,
Certificate
}

View File

@ -31,34 +31,34 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Esiur.Security.Cryptography;
using static System.Collections.Specialized.BitVector32;
using Esiur.Net.Packets;
namespace Esiur.Security.Authority;
public class Session
{
public Authentication LocalAuthentication => localAuth;
public Authentication RemoteAuthentication => remoteAuth;
// public Source Source { get; }
public byte[] Id { get; set; }
public DateTime Creation { get; }
public DateTime Modification { get; }
public KeyList<string, object> Variables { get; } = new KeyList<string, object>();
//KeyList<string, object> Variables { get; }
//IStore Store { get; }
//string id;
Authentication localAuth, remoteAuth;
//string domain;
public IKeyExchanger KeyExchanger { get; set; } = null;
public ISymetricCipher SymetricCipher { get; set; } = null;
public Session(Authentication localAuthentication, Authentication remoteAuthentication)
{
this.localAuth = localAuthentication;
this.remoteAuth = remoteAuthentication;
}
public Map<IIPAuthPacketHeader, object> LocalHeaders { get; set; } = new Map<IIPAuthPacketHeader, object>();
public Map<IIPAuthPacketHeader, object> RemoteHeaders { get; set; } = new Map<IIPAuthPacketHeader, object>();
public AuthenticationMethod LocalMethod { get; set; }
public AuthenticationMethod RemoteMethod { get; set; }
public AuthenticationType AuthenticationType { get; set; }
public string AuthorizedAccount { get; set; }
}

View File

@ -5,9 +5,9 @@ using System.Text;
namespace Esiur.Security.Membership
{
public class AuthorizationResponse
public class AuthorizationIndication
{
public Session Session { get; set; }
public bool Succeeded { get; set; }
public AuthorizationResults Results { get; set; }
}
}

View File

@ -1,4 +1,5 @@
using System;
using Esiur.Net.Packets;
using System;
using System.Collections.Generic;
using System.Text;
@ -6,11 +7,16 @@ namespace Esiur.Security.Membership
{
public class AuthorizationResults
{
AuthorizationResultsResponse Response { get; set; }
TwoFactorAuthorizationMethod TwoFactorMethod { get; set; }
public AuthorizationResultsResponse Response { get; set; }
public IIPAuthPacketIAuthDestination Destination { get; set; }
public IIPAuthPacketIAuthFormat RequiredFormat { get; set; }
public string Clue { get; set; }
public string AppName { get; set; }
public string Code { get; set; }
public int Timeout { get; set; }
public ushort Timeout { get; set; } // 0 means no timeout
public uint Reference { get; set; }
public DateTime Issue { get; set; } = DateTime.UtcNow;
public bool Expired => Timeout == 0 ? false : (DateTime.UtcNow - Issue).TotalSeconds > Timeout;
}
}

View File

@ -7,7 +7,11 @@ namespace Esiur.Security.Membership
public enum AuthorizationResultsResponse
{
Success,
Failed,
Expired,
ServiceUnavailable,
TwoFactoryAuthorization,
IAuthPlain,
IAuthHashed,
IAuthEncrypted
}
}

View File

@ -32,21 +32,28 @@ using Esiur.Net.IIP;
using Esiur.Core;
using Esiur.Security.Authority;
using Esiur.Resource;
using Esiur.Net.Packets;
namespace Esiur.Security.Membership;
public interface IMembership
{
public event ResourceEventHandler<AuthorizationResponse> Authorization;
public event ResourceEventHandler<AuthorizationIndication> Authorization;
AsyncReply<string> UserExists(string username, string domain);
AsyncReply<string> TokenExists(ulong tokenIndex, string domain);
AsyncReply<bool> UserExists(string username, string domain);
AsyncReply<byte[]> GetPassword(string username, string domain);
AsyncReply<byte[]> GetToken(ulong tokenIndex, string domain);
AsyncReply<AuthorizationResults> Authorize(Session session);
AsyncReply<AuthorizationResults> AuthorizePlain(Session session, uint reference, object value);
AsyncReply<AuthorizationResults> AuthorizeHashed(Session session, uint reference, IIPAuthPacketHashAlgorithm algorithm, byte[] value);
AsyncReply<AuthorizationResults> AuthorizeEncrypted(Session session, uint reference, IIPAuthPacketPublicKeyAlgorithm algorithm, byte[] value);
AsyncReply<bool> Login(Session session);
AsyncReply<bool> Logout(Session session);
bool GuestsAllowed { get; }
AsyncReply<string> TokenExists(ulong tokenIndex, string domain);
}

View File

@ -0,0 +1,184 @@
using Esiur.Core;
using Esiur.Data;
using Esiur.Net.Packets;
using Esiur.Resource;
using Esiur.Security.Authority;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
namespace Esiur.Security.Membership
{
public class SimpleMembership : IMembership
{
public bool GuestsAllowed { get; set; } = false;
public event ResourceEventHandler<AuthorizationIndication> Authorization;
KeyList<string, UserInfo> users = new KeyList<string, UserInfo>();
KeyList<ulong, TokenInfo> tokens = new KeyList<ulong, TokenInfo>();
public class QuestionAnswer
{
public string Question { get; set; }
public object Answer { get; set; }
public bool Hashed { get; set; }
}
public class UserInfo
{
public string Username { get; set; }
public string Password { get; set; }
public QuestionAnswer[] Questions { get; set; }
public List<AuthorizationResults> Results { get; set; } = new List<AuthorizationResults>();
}
public class TokenInfo
{
public ulong Index { get; set; }
public string Token { get; set; }
public string Username { get; set; }
}
public void AddToken(ulong index, string value, string username)
{
if (users.ContainsKey(username))
throw new Exception("User not found.");
tokens.Add(index, new TokenInfo() { Index = index, Token = value, Username = username });
}
public void AddUser(string username, string password, QuestionAnswer[] questions)
{
users.Add(username, new UserInfo() { Password = password, Username = username, Questions = questions });
}
public void RemoveToken(ulong index) => tokens.Remove(index);
public void RemoveUser(string username) => users.Remove(username);
public AsyncReply<AuthorizationResults> Authorize(Session session)
{
if (session.AuthorizedAccount.StartsWith("g-"))
return new AsyncReply<AuthorizationResults>(new AuthorizationResults() { Response = AuthorizationResultsResponse.Success });
if (users[session.AuthorizedAccount].Questions.Length > 0)
{
var q = users[session.AuthorizedAccount].Questions.First();
var r = new Random();
var format = q.Answer.GetIAuthFormat();
var ar = new AuthorizationResults()
{
Clue = q.Question,
Destination = IIPAuthPacketIAuthDestination.Self,
Reference = (uint)r.Next(),
RequiredFormat = format,
Timeout = 30,
Response = q.Hashed ? AuthorizationResultsResponse.IAuthHashed : AuthorizationResultsResponse.IAuthPlain
};
users[session.AuthorizedAccount].Results.Add(ar);
return new AsyncReply<AuthorizationResults>(ar);
}
else
{
return new AsyncReply<AuthorizationResults>(new AuthorizationResults() { Response = AuthorizationResultsResponse.Success });
}
}
public AsyncReply<AuthorizationResults> AuthorizeEncrypted(Session session, uint reference, IIPAuthPacketPublicKeyAlgorithm algorithm, byte[] value)
{
throw new NotImplementedException();
}
public AsyncReply<AuthorizationResults> AuthorizeHashed(Session session, uint reference, IIPAuthPacketHashAlgorithm algorithm, byte[] value)
{
if (algorithm != IIPAuthPacketHashAlgorithm.SHA256)
throw new NotImplementedException();
var ar = users[session.AuthorizedAccount].Results.First(x => x.Reference == reference);
var qa = users[session.AuthorizedAccount].Questions.First(x => x.Question == ar.Clue);
// compute hash
var remoteNonce = (byte[])session.RemoteHeaders[IIPAuthPacketHeader.Nonce];
var localNonce = (byte[])session.LocalHeaders[IIPAuthPacketHeader.Nonce];
var hashFunc = SHA256.Create();
// local nonce + password or token + remote nonce
var challenge = hashFunc.ComputeHash(new BinaryList()
.AddUInt8Array(remoteNonce)
.AddUInt8Array(Codec.Compose(qa.Answer, null))
.AddUInt8Array(localNonce)
.ToArray());
if (challenge.SequenceEqual(value))
return new AsyncReply<AuthorizationResults>(new AuthorizationResults() { Response = AuthorizationResultsResponse.Success });
else
return new AsyncReply<AuthorizationResults>(new AuthorizationResults() { Response = AuthorizationResultsResponse.Failed });
}
public AsyncReply<AuthorizationResults> AuthorizePlain(Session session, uint reference, object value)
{
var ar = users[session.AuthorizedAccount].Results.First(x => x.Reference == reference);
var qa = users[session.AuthorizedAccount].Questions.First(x => x.Question == ar.Clue);
if (qa.Answer.ToString() == value.ToString())
return new AsyncReply<AuthorizationResults>(new AuthorizationResults() { Response = AuthorizationResultsResponse.Success });
else
return new AsyncReply<AuthorizationResults>(new AuthorizationResults() { Response = AuthorizationResultsResponse.Failed });
}
public AsyncReply<byte[]> GetPassword(string username, string domain)
{
return new AsyncReply<byte[]>(DC.ToBytes(users[username].Password));
}
public AsyncReply<byte[]> GetToken(ulong tokenIndex, string domain)
{
return new AsyncReply<byte[]>(DC.ToBytes(tokens[tokenIndex].Token));
}
public AsyncReply<bool> Login(Session session)
{
return new AsyncReply<bool>(true);
}
public AsyncReply<bool> Logout(Session session)
{
return new AsyncReply<bool>(true);
}
public AsyncReply<string> TokenExists(ulong tokenIndex, string domain)
{
if (!tokens.ContainsKey(tokenIndex))
return new AsyncReply<string>(null);
else
return new AsyncReply<string>(tokens[tokenIndex].Username);
}
public AsyncReply<string> UserExists(string username, string domain)
{
if (!users.ContainsKey(username))
return new AsyncReply<string>(null);
else
return new AsyncReply<string>(username);
}
}
}

View File

@ -44,8 +44,8 @@ public class UserPermissionsManager : IPermissionsManager
{
Map<string,object> userPermissions = null;
if (settings.ContainsKey(session.RemoteAuthentication.FullName))
userPermissions = settings[session.RemoteAuthentication.FullName] as Map<string, object>;
if (settings.ContainsKey(session.AuthorizedAccount))
userPermissions = settings[session.AuthorizedAccount] as Map<string, object>;
else if (settings.ContainsKey("public"))
userPermissions = settings["public"] as Map<string,object>;
else