mirror of
https://github.com/esiur/esiur-dotnet.git
synced 2026-04-04 12:28:21 +00:00
Layout
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
using Esiur.Security.Authority;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Esiur.Security.Membership
|
||||
{
|
||||
public class AuthorizationIndication
|
||||
{
|
||||
public Session Session { get; set; }
|
||||
public AuthorizationResults Results { get; set; }
|
||||
}
|
||||
}
|
||||
52
Libraries/Esiur/Security/Membership/AuthorizationRequest.cs
Normal file
52
Libraries/Esiur/Security/Membership/AuthorizationRequest.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using Esiur.Data;
|
||||
using Esiur.Net.Packets;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Esiur.Security.Membership
|
||||
{
|
||||
public class AuthorizationRequest
|
||||
{
|
||||
public uint Reference { get; set; }
|
||||
public EpAuthPacketIAuthDestination Destination { get; set; }
|
||||
public string Clue { get; set; }
|
||||
public EpAuthPacketIAuthFormat? RequiredFormat { get; set; }
|
||||
public EpAuthPacketIAuthFormat? ContentFormat { get; set; }
|
||||
public object? Content { get; set; }
|
||||
|
||||
public byte? Trials { get; set; }
|
||||
|
||||
public DateTime? Issue { get; set; }
|
||||
public DateTime? Expire { get; set; }
|
||||
|
||||
public int Timeout => Expire.HasValue && Issue.HasValue ? (int)(Expire.Value - Issue.Value).TotalSeconds : 0;
|
||||
|
||||
public AuthorizationRequest(Map<EpAuthPacketIAuthHeader, object> headers)
|
||||
{
|
||||
Reference = (uint)headers[EpAuthPacketIAuthHeader.Reference];
|
||||
Destination =(EpAuthPacketIAuthDestination)headers[EpAuthPacketIAuthHeader.Destination];
|
||||
Clue = (string)headers[EpAuthPacketIAuthHeader.Clue];
|
||||
|
||||
if (headers.ContainsKey(EpAuthPacketIAuthHeader.RequiredFormat))
|
||||
RequiredFormat = (EpAuthPacketIAuthFormat)headers[EpAuthPacketIAuthHeader.RequiredFormat];
|
||||
|
||||
if (headers.ContainsKey(EpAuthPacketIAuthHeader.ContentFormat))
|
||||
ContentFormat = (EpAuthPacketIAuthFormat)headers[EpAuthPacketIAuthHeader.ContentFormat];
|
||||
|
||||
if (headers.ContainsKey(EpAuthPacketIAuthHeader.Content))
|
||||
Content = headers[EpAuthPacketIAuthHeader.Content];
|
||||
|
||||
if (headers.ContainsKey(EpAuthPacketIAuthHeader.Trials))
|
||||
Trials = (byte)headers[EpAuthPacketIAuthHeader.Trials];
|
||||
|
||||
if (headers.ContainsKey(EpAuthPacketIAuthHeader.Issue))
|
||||
Issue = (DateTime)headers[EpAuthPacketIAuthHeader.Issue];
|
||||
|
||||
if (headers.ContainsKey(EpAuthPacketIAuthHeader.Expire))
|
||||
Expire = (DateTime)headers[EpAuthPacketIAuthHeader.Expire];
|
||||
}
|
||||
}
|
||||
}
|
||||
31
Libraries/Esiur/Security/Membership/AuthorizationResults.cs
Normal file
31
Libraries/Esiur/Security/Membership/AuthorizationResults.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using Esiur.Net.Packets;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Esiur.Security.Membership
|
||||
{
|
||||
public class AuthorizationResults
|
||||
{
|
||||
public AuthorizationResultsResponse Response { get; set; }
|
||||
|
||||
|
||||
public uint Reference { get; set; }
|
||||
public EpAuthPacketIAuthDestination Destination { get; set; }
|
||||
public string? Clue { get; set; }
|
||||
public EpAuthPacketIAuthFormat? RequiredFormat { get; set; }
|
||||
public EpAuthPacketIAuthFormat? ContentFormat { get; set; }
|
||||
public object? Content { get; set; }
|
||||
|
||||
public byte? Trials { get; set; }
|
||||
|
||||
public DateTime? Issue { get; set; } = DateTime.UtcNow;
|
||||
public DateTime? Expire { get; set; }
|
||||
|
||||
public int Timeout => Expire.HasValue && Issue.HasValue ? (int)(Expire.Value - Issue.Value).TotalSeconds : 0;
|
||||
|
||||
public bool Expired => DateTime.Now > Expire;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Esiur.Security.Membership
|
||||
{
|
||||
public enum AuthorizationResultsResponse
|
||||
{
|
||||
Success,
|
||||
Failed,
|
||||
Expired,
|
||||
ServiceUnavailable,
|
||||
IAuthPlain,
|
||||
IAuthHashed,
|
||||
IAuthEncrypted
|
||||
}
|
||||
}
|
||||
59
Libraries/Esiur/Security/Membership/IMembership.cs
Normal file
59
Libraries/Esiur/Security/Membership/IMembership.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2017-2024 Ahmed Kh. Zamil
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Esiur.Data;
|
||||
using Esiur.Core;
|
||||
using Esiur.Security.Authority;
|
||||
using Esiur.Resource;
|
||||
using Esiur.Net.Packets;
|
||||
|
||||
namespace Esiur.Security.Membership;
|
||||
|
||||
public interface IMembership
|
||||
{
|
||||
public event ResourceEventHandler<AuthorizationIndication> Authorization;
|
||||
|
||||
AsyncReply<string> UserExists(string username, string domain);
|
||||
AsyncReply<string> TokenExists(ulong tokenIndex, 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, EpAuthPacketHashAlgorithm algorithm, byte[] value);
|
||||
//AsyncReply<AuthorizationResults> AuthorizeEncrypted(Session session, uint reference, EpAuthPacketPublicKeyAlgorithm algorithm, byte[] value);
|
||||
|
||||
AsyncReply<bool> Login(Session session);
|
||||
AsyncReply<bool> Logout(Session session);
|
||||
bool GuestsAllowed { get; }
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
40
Libraries/Esiur/Security/Membership/IUser.cs
Normal file
40
Libraries/Esiur/Security/Membership/IUser.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2017 Ahmed Kh. Zamil
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
using Esiur.Core;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Security.Membership;
|
||||
|
||||
public interface IUser
|
||||
{
|
||||
string Username
|
||||
{
|
||||
get;
|
||||
}
|
||||
}
|
||||
184
Libraries/Esiur/Security/Membership/SimpleMembership.cs
Normal file
184
Libraries/Esiur/Security/Membership/SimpleMembership.cs
Normal 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 { add { } remove { } }
|
||||
|
||||
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 = EpAuthPacketIAuthDestination.Self,
|
||||
Reference = (uint)r.Next(),
|
||||
RequiredFormat = format,
|
||||
Expire = DateTime.Now.AddSeconds(60),
|
||||
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, EpAuthPacketPublicKeyAlgorithm algorithm, byte[] value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public AsyncReply<AuthorizationResults> AuthorizeHashed(Session session, uint reference, EpAuthPacketHashAlgorithm algorithm, byte[] value)
|
||||
{
|
||||
if (algorithm != EpAuthPacketHashAlgorithm.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[EpAuthPacketHeader.Nonce];
|
||||
var localNonce = (byte[])session.LocalHeaders[EpAuthPacketHeader.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, 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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Esiur.Security.Membership
|
||||
{
|
||||
public enum TwoFactorAuthorizationMethod
|
||||
{
|
||||
Email,
|
||||
SMS,
|
||||
App,
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user