//using FabAccessAPI.Schema;
//using S22.Sasl;
//using System.Collections.Generic;
//using System.Linq;
//using System.Threading.Tasks;
//using Exception = System.Exception;

//namespace FabAccessAPI
//{
//    / Authentication Identity
//    /
//    / Under the hood a string because the form depends heavily on the method
//    public struct AuthCId {
//        public string Id { get; private set; }

//        public AuthCId(string id) : this() { Id = id; }
//    }
    
//    / Authorization Identity
//    /
//    / This identity is internal to FabAccess and completely independent from the authentication
//    / method or source
//    public struct AuthZId {
//        / Main User ID. Generally an user name or similar
//        public string Uid;

//        / Sub user ID. 
//        /
//        / Can change scopes for permissions, e.g. having a +admin account with more permissions than
//        / the default account and +dashboard et.al. accounts that have restricted permissions for
//        / their applications
//        public string Subuid;

//        / Realm this account originates.
//        /
//        / The Realm is usually described by a domain name but local policy may dictate an unrelated
//        / mapping
//        public string Realm;
//    }

//    / Authentication/Authorization user object.
//    /
//    / This struct contains the user as is passed to the actual authentication/authorization
//    / subsystems
//    /
//    public struct AuthUser {
//        / Contains the Authentication ID used
//        /
//        / The authentication ID is an identifier for the authentication exchange. This is different
//        / than the ID of the user to be authenticated; for example when using x509 the authcid is
//        / the dn of the certificate, when using GSSAPI the authcid is of form `<userid>@<REALM>`
//        public AuthCId Authcid;

//        / Contains the Authorization ID
//        /
//        / This is the identifier of the user to *authenticate as*. This in several cases is different
//        / to the `authcid`: 
//        / If somebody wants to authenticate as somebody else, su-style.
//        / If a person wants to authenticate as a higher-permissions account, e.g. foo may set authzid foo+admin
//        / to split normal user and "admin" accounts.
//        / If a method requires a specific authcid that is different from the identifier of the user
//        / to authenticate as, e.g. GSSAPI, x509 client certificates, API TOKEN authentication.
//        public AuthZId Authzid;

//        / Contains the authentication method used
//        /
//        / For the most part this is the SASL method
//        public string AuthMethod;

//        / Method-specific key-value pairs
//        /
//        / Each method can use their own key-value pairs.
//        / E.g. EXTERNAL encodes the actual method used (x509 client certs, UID/GID for unix sockets,
//        / ...)
//        public Dictionary<string, string> Kvs;

//    }

//     Authentication has two parts: Granting the authentication itself and then performing the
//     authentication.
//     Granting the authentication checks if 
//     a) the given authcid fits with the given (authMethod, kvs). In general a failure here indicates
//        a programming failure — the authcid come from the same source as that tuple
//     b) the given authcid may authenticate as the given authzid. E.g. if a given client certificate
//        has been configured for that user, if a GSSAPI user maps to a given user, 
//    public enum AuthError {
//        / Authentication ID is bad/unknown/..
//        BadAuthcid,
//        / Authorization ID is unknown/..
//        BadAuthzid,
//        / Authorization ID is not of form user+uid@realm
//        MalformedAuthzid,
//        / User may not use that authorization id
//        NotAllowedAuthzid,

//    }

//    public class UnauthorizedException : Exception{}
//    public class UnsupportedMechanismException : Exception{}

//    / <summary>
//    / THIS IS VERY INCOMPLETE!
//    / </summary>
//    public class Auth {
//        #region Log
//        private static readonly log4net.ILog _Log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
//        #endregion
        
//        private IAuthentication _authCap;
//        public Auth(IAuthentication authCap) {
//            _authCap = authCap;
//        }

//        public Task<IReadOnlyList<string>> GetMechanisms() {
//            return _authCap.Mechanisms();
//        }

//        public async Task<bool> Authenticate(string mech, Dictionary<string, object> properties) {

//            var m = SaslFactory.Create(mech);
//            foreach (KeyValuePair<string, object> entry in properties) {
//                m.Properties.Add(entry.Key, entry.Value);
//            }

//            var initialResponse = new Request.initialResponse();
//            if (m.HasInitial) {
//                initialResponse.Initial = m.GetResponse(new byte[0]);
//            }

//            var req = new Request {
//                Mechanism = m.Name,
//                InitialResponse = initialResponse
//            };

//            var resp = await _authCap.Start(req);
//            while (!m.IsCompleted) {
//                if (resp.which == Response.WHICH.Challence) {
//                    var additional = m.GetResponse(resp.Challence.ToArray());
//                    resp = await _authCap.Step(additional);
//                }
//                else {
//                    break;
//                }
//            }

//            if (resp.which == Response.WHICH.Outcome) {
//                if (resp.Outcome.Result == Response.Result.successful) {
//                    return true;
//                }
//                else {
//                    TODO: Provide meaningful info about auth failure
//                    return false;
//                }
//            }

//            return false;
//        }

        

//    }

//}