1 /* 2 Copyright (c) 2019, DUNEX Contributors 3 Use, modification and distribution are subject to the 4 Boost Software License, Version 1.0. (See accompanying file 5 COPYING or copy at http://www.boost.org/LICENSE_1_0.txt) 6 7 dunex-auth: D toolkit to manage UNIX authentication 8 9 Author: Clipsey 10 */ 11 module dunex.auth.fdb; 12 import std.conv; 13 import std.file : fremove = remove, exists, write, readText; 14 import core.thread : Thread; 15 import std.datetime; 16 import std.format; 17 import std.array : split; 18 19 /** 20 The UNIX file database 21 */ 22 class UnixFDB(EntryT, SubType, string dbFile, string lockFile) { 23 static assert(__traits(hasMember, EntryT, "parseEntry"), "Invalid database format"); 24 alias ThisT = typeof(this); 25 26 private: 27 28 bool readonly = true; 29 30 static void waitOnLock() { 31 while(locked) { 32 Thread.sleep(100.msecs); 33 } 34 } 35 36 protected: 37 EntryT[] entries; 38 39 package(dunex.auth): 40 41 this(string data, bool readonly) { 42 this.readonly = readonly; 43 foreach(line; data.split("\n")) { 44 if (line.length == 0) continue; 45 entries ~= EntryT.parseEntry(line); 46 } 47 } 48 49 public: 50 51 ~this() { 52 if (readonly) return; 53 unlock(); 54 } 55 56 /** 57 Unlock the password db 58 */ 59 void unlock() { 60 if (readonly) return; 61 if (lockFile.exists) fremove(lockFile); 62 } 63 64 /** 65 Save changes to password db 66 */ 67 void save() { 68 if (readonly) throw new Exception("Saving only allowed in read/write mode"); 69 string data; 70 foreach(entry; entries) { 71 data ~= entry.toString() ~ "\n"; 72 } 73 write(dbFile, data); 74 } 75 76 /** 77 Gets wether the password db is locked 78 */ 79 static bool locked() { 80 return lockFile.exists; 81 } 82 83 /** 84 Gets entry at index 85 */ 86 ref EntryT opIndex(size_t index) { 87 return entries[index]; 88 } 89 90 /** 91 Assigns entry at index 92 */ 93 void opIndexAssign(EntryT entry, size_t index) { 94 entries[index] = entry; 95 } 96 97 /** 98 Assigns entry 99 */ 100 void opOpAssign(string op = "~=")(EntryT entry) { 101 entries ~= entry; 102 } 103 104 /** 105 Gets the index of an entry 106 107 Throws an exception if not found 108 */ 109 size_t indexOf(EntryT xentry) { 110 foreach(i, entry; entries) { 111 if (entry == xentry) return i; 112 } 113 throw new Exception("%s not found!".format(xentry)); 114 } 115 116 /** 117 Remove index in array 118 */ 119 void remove(size_t index) { 120 import std.algorithm.mutation : arrrem = remove; 121 entries = arrrem(entries, index); 122 } 123 124 /** 125 Wether the password db has the specified entry 126 */ 127 bool has(EntryT xentry) { 128 foreach(i, entry; entries) { 129 if (entry == xentry) return true; 130 } 131 return false; 132 } 133 134 /** 135 Locks the password db 136 */ 137 static void lock(bool throwOnLock = false)() { 138 synchronized { 139 if (locked) { 140 static if (throwOnLock) { 141 throw new Exception(dbFile~" is locked"); 142 } else { 143 waitOnLock(); 144 } 145 } 146 write(lockFile, ""); 147 } 148 } 149 150 /** 151 Opens up the password database 152 */ 153 static SubType open(bool throwOnLock = false)() { 154 lock!(throwOnLock)(); 155 return new SubType(readText(dbFile), false); 156 } 157 158 /** 159 Opens up the password database as read-only non locked 160 */ 161 static SubType openro() { 162 return new SubType(readText(dbFile), true); 163 } 164 }