#pragma once #include #include #include #include #ifdef NT_EXPORTS #define NT_API __declspec(dllexport) #else #define NT_API __declspec(dllimport) #endif enum OBJECT_TYPE { MULTIGROUP, RELATION, TUPLE, ATTRIBUTE, TRANSACTION }; enum METHOD { OPEN, CLOSE, PARSE, // To plug a secondary namespace SECURITY }; enum AUTH_CLAIM { READ, WRITE }; enum AUTH_METHOD { CERTIFICATE, PLAIN_TEXT }; namespace nt { class NT_API ObjectManager { public: class IObject { virtual ~IObject() {} }; class Multigroup : IObject {}; class Relation : IObject {}; class Tuple : IObject {}; class Transaction : IObject {}; struct object_type { OBJECT_TYPE label; // If the object is not durable. E.g.: TRANSACTION bool disposable; std::set methods; }; struct registry_head { uint32_t reference_count; uint32_t handle_count; struct object_type* type; std::vector path; //security_descriptor to be added later, maybe also a set of labels }; struct registry { struct registry_head* head; IObject object; // TODO: Treat as a list for now, but ideally it's a tree struct registry* next; }; struct registry** registry; // Looks into this registry and attempts to retrieve the entry struct registry* Find(const std::vector object_path){} }; class NT_API PermissionsManager { public: // For validating connections at login std::set Firewall(AUTH_METHOD method) {} // For validating permission to an object const bool Access(const std::vector object_path, const void* connection_context) { // Checks the security descriptor on the head of the object we find in the ObjectManager::registry // Then additionally check other metadata yet to be defined in the connection_context, // like user group, policy, etc. // Yet to decide if this should be summoned at every attempt to get ahold of an object, // that might produce significant overhead. How can we solve this? } }; class NT_API NamespaceReferenceManager { }; class NT_API CursorManager { }; class NT_API IdentityManager { public: // Looks into the methods of the object type and asserts if it can be opened const bool CanOpen(struct ObjectManager::registry* object) {} }; class NT_API LifecycleManager { public: // If object is not being monitored already, monitors the lifecycle by updating the handle count. // Maybe also look at the reference count and kill some categories of disposable objects no longer referenced by other objects, like transactions. // Think of other use cases later. void Monitor(struct ObjectManager::registry* object) {} // Serializes contention for the change of a mutable reference. // This can be used in, for example, the HEAD of a branch to serialize the access. // If the object does not causes contention, like a relation (since they are immutable, just like multigroups, all snapshots) // then just approve, as they do not cause any contention. Transactions also do not cause any, // the only things that do are mutable reference states in shared sessions, // for example: the HEAD of a branch, namespace collisions (creating two branches with the same name, or two multigroups with the same name from the same state, etc), // multi-branch atomic commits (which I wish to not have as it damages isolation), // garbage collection of old state (as in, we want to delete an old-enough history to save up space), etc. const bool Contention(struct ObjectManager::registry* object){} // The inverse of `Monitor`, stops monitoring. // Maybe garbage collect here also if the counters goes to zero. void Unmonitor(struct ObjectManager::registry* object) {} }; class NT_API HandlerManager { public: // This is supposed to represent an authorized access to a certain object in the registry // This increases the handle count in the object manager // We should consider adding on the object manager a vector of all the handles that refer to an object struct handle {}; void DeallocateHandle(struct handle* handle) {} struct handle Open(std::vector object_path, void* connection_context) { struct ObjectManager::registry* retrieved_object = ObjectManager::Find((const std::vector)object_path); if (PermissionsManager::Access(retrieved_object, (const void*)connection_context) && IdentityManager::CanOpen(retrieved_object) && LifecycleManager::Contention(retrieved_object)) { // Starts monitoring this guy LifecycleManager::Monitor(retrieved_object); return {}; // Return a successful handle } else { // Return something more algebraic instead of null or an int return {}; }; } bool Close(struct handle *handle) { // Somehow grab the object pointer and connection (maybe drop it...) from the handle struct ObjectManager::registry* object = handle->object; void* connection_context = handle->connection_context; // Checking for permissions here may be relevant. // What if we have a handle but no permission? Spooky if (PermissionsManager::Access(object, connection_context) && IdentityManager::CanClose(object) // TODO: I think that in this case we do not need to worry about contention. // But be careful to evaluate this scenario later. //&& LifecycleManager::Contention(retrieved_object) ) { // Stops monitoring this guy LifecycleManager::Unmonitor(object); DeallocateHandle(handle); return true; // Return a successful handle } else { // Return something more algebraic instead of null or an int. // It's kinda bad to not even report what went wrong. return false; }; } }; class NT_API WAM { }; class NT_API NT { public: bool IsRunning() const; void SimulateEntryCall() {}; }; }