· 6 years ago · Oct 17, 2019, 10:56 PM
1require("mysqloo")
2local DATABASE_HOST = ""
3local DATABASE_PORT = 3306
4local DATABASE_NAME = ""
5local DATABASE_USERNAME = ""
6local DATABASE_PASSWORD = ""
7local DATABASE_TABLE = "ulibsql"
8
9ULib.ucl.saveiterationtime = 0.25
10
11function ULib_SQL_Connect()
12
13 if not ULib.ucl.db then
14 ULib_SQL_ConnectToDatabase()
15 return
16 end
17
18 if not ( ULib.ucl.db:status() == 0 ) then
19 ULib_SQL_ConnectToDatabase()
20 return
21 end
22
23end
24
25
26function ULib_SQL_Format( str )
27 if not str then return "NULL" end
28 return string.format( "%q", str )
29end
30
31function Escape( str )
32 if not ULib.ucl.db then
33 Msg( "Not connected to DB.\n" )
34 return
35 end
36
37 if not str then return end
38
39 local esc = ULib.ucl.db:escape( str )
40 if not esc then
41 return nil
42 end
43 return esc
44end
45
46
47function ULib_SQL_ConnectToDatabase()
48 ULib.ucl.db = mysqloo.connect( DATABASE_HOST, DATABASE_USERNAME, DATABASE_PASSWORD, DATABASE_NAME, DATABASE_PORT )
49 ULib.ucl.db.onConnectionFailed = function( db, Error )
50 ServerLog("Connection to database failed\n")
51 ServerLog("Error: " .. Error .. "\n")
52 timer.Simple( 3, ULib_SQL_ConnectToDatabase )
53 end
54 ULib.ucl.db.onConnected = function( db )
55 ULib_SQL_CreateTable()
56 end
57 ULib.ucl.db:connect()
58end
59
60
61function ULib_SQL_CreateTable()
62 if ULib.ucl.db:status() == 0 then
63 local query = ULib.ucl.db:query("CREATE TABLE IF NOT EXISTS " .. DATABASE_TABLE .. " ( uid varchar(255), accessblob text, PRIMARY KEY( uid ) )")
64 function query:onSuccess( data )
65 ServerLog("SUCCESSFULLY CREATED TABLE OR TABLE ALREADY EXISTED\n")
66 end
67 function query:onError( Q, E )
68 ServerLog("FAILED CREATING DATABASE TABLE\n")
69 ServerLog("Error: " .. E .. "\n")
70 timer.Simple(3, ULib_SQL_CreateTable )
71 end
72 query:start()
73 else
74 ServerLog("Database unexpectedly offline, attempting to reconnect\n")
75 ULib_SQL_ConnectToDatabase()
76 end
77end
78
79
80function ULib_SQL_SaveAll( tbl )
81 if ULib.ucl.db:status() == 0 then
82 for k, v in pairs( tbl ) do
83 local query = ULib.ucl.db:query("REPLACE INTO " .. DATABASE_TABLE .. " (uid, accessblob) VALUES ('" .. k .. "', '" .. Escape(util.TableToJSON(v)) .. "')")
84 function query:onSuccess( data )
85 ServerLog("SUCCESSFULLY ADDED/UPDATED (" .. k .. ") to the database\n")
86 end
87 function query:onError( Q, E )
88 ServerLog("FAILED ADDING/UPDATING (" .. k .. ") to the database\n")
89 ServerLog(E .. "\n")
90 end
91 query:start()
92 end
93 else
94 ServerLog("Database unexpectedly offline, attempting to reconnect\n")
95 ULib_SQL_ConnectToDatabase()
96 end
97end
98
99function ULib_SQL_SaveUser( id )
100 local tbl = ULib.ucl.users[ id ]
101 if not tbl then return end
102 if ULib.ucl.db:status() == 0 then
103 local query = ULib.ucl.db:query("REPLACE INTO " .. DATABASE_TABLE .. " (uid, accessblob) VALUES ('" .. id .. "', '" .. Escape(util.TableToJSON(tbl)) .. "')")
104 function query:onSuccess( data )
105 ServerLog("SUCCESSFULLY ADDED/UPDATED (" .. id .. ") to the database\n")
106 end
107 function query:onError( Q, E )
108 ServerLog("FAILED ADDING/UPDATING (" .. id .. ") to the database\n")
109 ServerLog(E .. "\n")
110 end
111 query:start()
112 else
113 ServerLog("Database unexpectedly offline, attempting to reconnect\n")
114 ULib_SQL_ConnectToDatabase()
115 end
116end
117
118function ULib_SQL_WriteUsers( tbl )
119 for k, v in pairs( tbl ) do
120 ULib_SQL_SaveUser( k )
121 tbl[k] = nil
122 break
123 end
124
125 if table.Count( tbl ) > 0 then
126 timer.Simple( ULib.ucl.saveiterationtime, function() ULib_SQL_WriteUsers( tbl ) end )
127 else
128 tbl = nil
129 ULib.ucl.tempusers = nil
130 ULib.ucl.saving = false
131 end
132end
133
134function ULib_SQL_Load()
135 if ULib.ucl.db:status() == 0 then
136 local query = ULib.ucl.db:query("SELECT * FROM " .. DATABASE_TABLE .. "")
137 function query:onSuccess( data )
138 ULib.ucl.users = {}
139 ServerLog("Loading UCL Users List from Database\n")
140 for k, v in pairs( data ) do
141 ULib.ucl.users[v.uid] = util.JSONToTable(v.accessblob)
142 end
143 end
144 function query:onError( Q, E )
145
146 end
147 query:start()
148 else
149 ServerLog("Database unexpectedly offline, attempting to reconnect\n")
150 ULib_SQL_ConnectToDatabase()
151 end
152end
153
154function ULib_SQL_TryLoad()
155 if ULib.ucl.db:status() == 0 then
156 ULib_SQL_Load()
157 else
158 timer.Simple( 1, ULib_SQL_TryLoad )
159 end
160end
161
162ULib_SQL_Connect()
163ULib_SQL_TryLoad()
164
165-------------------------------------------------------------------
166--[[
167 Title: UCL
168
169 ULib's Access Control List
170]]
171
172
173local ucl = ULib.ucl -- Make it easier for us to refer to
174
175local accessStrings = ULib.parseKeyValues( ULib.fileRead( ULib.UCL_REGISTERED ) or "" ) or {}
176local accessCategories = {}
177ULib.ucl.accessStrings = accessStrings
178ULib.ucl.accessCategories = accessCategories
179
180-- Helper function to save access string registration to misc_registered.txt
181local function saveAccessStringRegistration()
182 ULib.fileWrite( ULib.UCL_REGISTERED, ULib.makeKeyValues( accessStrings ) )
183end
184
185-- Save what we've got with ucl.groups so far!
186function ucl.saveGroups()
187 for _, groupInfo in pairs( ucl.groups ) do
188 table.sort( groupInfo.allow )
189 end
190
191 ULib.fileWrite( ULib.UCL_GROUPS, ULib.makeKeyValues( ucl.groups ) )
192end
193
194function ucl.saveUsers()
195 if ucl.saving then
196 return
197 end
198
199 ucl.saving = true
200
201 for _, userInfo in pairs( ucl.users ) do
202 table.sort( userInfo.allow )
203 table.sort( userInfo.deny )
204 end
205
206 ucl.tempusers = table.Copy( ucl.users )
207
208 if table.Count( ucl.tempusers ) > 0 then
209 ULib_SQL_WriteUsers( ucl.tempusers )
210 end
211
212 --ULib.fileWrite( ULib.UCL_USERS, ULib.makeKeyValues( ucl.users ) )
213 --ULib_SQL_SaveAll( ucl.users )
214end
215
216function ucl.saveUser( id )
217 ULib_SQL_SaveUser( id )
218end
219
220function reloadGroups()
221 local needsBackup = false
222 local err
223 ucl.groups, err = ULib.parseKeyValues( ULib.removeCommentHeader( ULib.fileRead( ULib.UCL_GROUPS ), "/" ) )
224
225 if not ucl.groups or not ucl.groups[ ULib.ACCESS_ALL ] then
226 needsBackup = true
227 -- Totally messed up! Clear it.
228 local f = "addons/ulib/" .. ULib.UCL_GROUPS
229 if not ULib.fileExists( f ) then
230 Msg( "ULIB PANIC: groups.txt is corrupted and I can't find the default groups.txt file!!\n" )
231 else
232 local err2
233 ucl.groups, err2 = ULib.parseKeyValues( ULib.removeCommentHeader( ULib.fileRead( f ), "/" ) )
234 if not ucl.groups or not ucl.groups[ ULib.ACCESS_ALL ] then
235 Msg( "ULIB PANIC: default groups.txt is corrupt!\n" )
236 err = err2
237 end
238 end
239 if ULib.fileExists( ULib.UCL_REGISTERED ) then
240 ULib.fileDelete( ULib.UCL_REGISTERED ) -- Since we're regnerating we'll need to remove this
241 end
242 accessStrings = {}
243
244 else
245 -- Check to make sure it passes a basic validity test
246 ucl.groups[ ULib.ACCESS_ALL ].inherit_from = nil -- Ensure this is the case
247 for groupName, groupInfo in pairs( ucl.groups ) do
248 if type( groupName ) ~= "string" then
249 needsBackup = true
250 ucl.groups[ groupName ] = nil
251 else
252
253 if type( groupInfo ) ~= "table" then
254 needsBackup = true
255 groupInfo = {}
256 ucl.groups[ groupName ] = groupInfo
257 end
258
259 if type( groupInfo.allow ) ~= "table" then
260 needsBackup = true
261 groupInfo.allow = {}
262 end
263
264 local inherit_from = groupInfo.inherit_from
265 if inherit_from and inherit_from ~= "" and not ucl.groups[ groupInfo.inherit_from ] then
266 needsBackup = true
267 groupInfo.inherit_from = nil
268 end
269
270 -- Check for cycles
271 local group = ucl.groupInheritsFrom( groupName )
272 while group do
273 if group == groupName then
274 needsBackup = true
275 groupInfo.inherit_from = nil
276 end
277 group = ucl.groupInheritsFrom( group )
278 end
279
280 if groupName ~= ULib.ACCESS_ALL and not groupInfo.inherit_from or groupInfo.inherit_from == "" then
281 groupInfo.inherit_from = ULib.ACCESS_ALL -- Clean :)
282 end
283
284 -- Lower case'ify
285 for k, v in pairs( groupInfo.allow ) do
286 if type( k ) == "string" and k:lower() ~= k then
287 groupInfo.allow[ k:lower() ] = v
288 groupInfo.allow[ k ] = nil
289 else
290 groupInfo.allow[ k ] = v
291 end
292 end
293 end
294 end
295 end
296
297 if needsBackup then
298 Msg( "Groups file was not formatted correctly. Attempting to fix and backing up original\n" )
299 if err then
300 Msg( "Error while reading groups file was: " .. err .. "\n" )
301 end
302 Msg( "Original file was backed up to " .. ULib.backupFile( ULib.UCL_GROUPS ) .. "\n" )
303 ucl.saveGroups()
304 end
305end
306reloadGroups()
307
308local function reloadUsers()
309 local needsBackup = false
310 local err
311 ucl.users, err = ULib.parseKeyValues( ULib.removeCommentHeader( ULib.fileRead( ULib.UCL_USERS ), "/" ) )
312
313 -- Check to make sure it passes a basic validity test
314 if not ucl.users then
315 needsBackup = true
316 -- Totally messed up! Clear it.
317 local f = "addons/ulib/" .. ULib.UCL_USERS
318 if not ULib.fileExists( f ) then
319 Msg( "ULIB PANIC: users.txt is corrupted and I can't find the default users.txt file!!\n" )
320 else
321 local err2
322 ucl.users, err2 = ULib.parseKeyValues( ULib.removeCommentHeader( ULib.fileRead( f ), "/" ) )
323 if not ucl.users then
324 Msg( "ULIB PANIC: default users.txt is corrupt!\n" )
325 err = err2
326 end
327 end
328 if ULib.fileExists( ULib.UCL_REGISTERED ) then
329 ULib.fileDelete( ULib.UCL_REGISTERED ) -- Since we're regnerating we'll need to remove this
330 end
331 accessStrings = {}
332
333 else
334 for id, userInfo in pairs( ucl.users ) do
335 if type( id ) ~= "string" then
336 needsBackup = true
337 ucl.users[ id ] = nil
338 else
339
340 if type( userInfo ) ~= "table" then
341 needsBackup = true
342 userInfo = {}
343 ucl.users[ id ] = userInfo
344 end
345
346 if type( userInfo.allow ) ~= "table" then
347 needsBackup = true
348 userInfo.allow = {}
349 end
350
351 if type( userInfo.deny ) ~= "table" then
352 needsBackup = true
353 userInfo.deny = {}
354 end
355
356 if userInfo.group and type( userInfo.group ) ~= "string" then
357 needsBackup = true
358 userInfo.group = nil
359 end
360
361 if userInfo.name and type( userInfo.name ) ~= "string" then
362 needsBackup = true
363 userInfo.name = nil
364 end
365
366 if userInfo.group == "" then userInfo.group = nil end -- Clean :)
367
368 -- Lower case'ify
369 for k, v in pairs( userInfo.allow ) do
370 if type( k ) == "string" and k:lower() ~= k then
371 userInfo.allow[ k:lower() ] = v
372 userInfo.allow[ k ] = nil
373 else
374 userInfo.allow[ k ] = v
375 end
376 end
377
378 for k, v in ipairs( userInfo.deny ) do
379 if type( k ) == "string" and type( v ) == "string" then -- This isn't allowed here
380 table.insert( userInfo.deny, k )
381 userInfo.deny[ k ] = nil
382 else
383 userInfo.deny[ k ] = v
384 end
385 end
386 end
387 end
388 end
389
390 if needsBackup then
391 Msg( "Users file was not formatted correctly. Attempting to fix and backing up original\n" )
392 if err then
393 Msg( "Error while reading groups file was: " .. err .. "\n" )
394 end
395 Msg( "Original file was backed up to " .. ULib.backupFile( ULib.UCL_USERS ) .. "\n" )
396 --ucl.saveUsers()
397 end
398end
399--reloadUsers()
400
401function ULib_SQL_LoadLegacy()
402end
403
404ULib_SQL_LoadLegacy = reloadUsers
405
406concommand.Add( "ulib_loadlegacy", function( ply, com, args )
407 if IsValid(ply) and ply:IsSuperAdmin() then
408 ULib_SQL_LoadLegacy()
409 ucl.saveUsers() --Saving all entries (Loading from Legacy file)
410 else
411 if not IsValid( ply ) then
412 ULib_SQL_LoadLegacy()
413 ucl.saveUsers() --Saving all entries (Loading from Legacy file)
414 end
415 end
416end )
417
418concommand.Add( "ulib_loadlegacy_nousers", function( ply, com, args )
419 if IsValid(ply) and ply:IsSuperAdmin() then
420 ULib_SQL_LoadLegacy()
421 ULib_SQL_DumpUsers()
422 ucl.saveUsers() --Saving all entries (Loading from Legacy file)
423 else
424 if not IsValid( ply ) then
425 ULib_SQL_LoadLegacy()
426 ULib_SQL_DumpUsers()
427 ucl.saveUsers() --Saving all entries (Loading from Legacy file)
428 end
429 end
430end )
431
432function ULib_SQL_DumpUsers()
433 for k, v in pairs( ULib.ucl.users ) do
434 if v.group == "user" then
435 ULib.ucl.users[k] = nil
436 end
437 end
438end
439
440
441--[[
442 Function: ucl.addGroup
443
444 Adds a new group to the UCL. Automatically saves.
445
446 Parameters:
447
448 name - A string of the group name. (IE: superadmin)
449 allows - *(Optional, defaults to empty table)* The allowed access for the group.
450 inherit_from - *(Optional)* A string of a valid group to inherit from
451
452 Revisions:
453
454 v2.10 - acl is now an options parameter, added inherit_from.
455 v2.40 - Rewrite, changed parameter list around.
456]]
457function ucl.addGroup( name, allows, inherit_from )
458 ULib.checkArg( 1, "ULib.ucl.addGroup", "string", name )
459 ULib.checkArg( 2, "ULib.ucl.addGroup", {"nil","table"}, allows )
460 ULib.checkArg( 3, "ULib.ucl.addGroup", {"nil","string"}, inherit_from )
461 allows = allows or {}
462 inherit_from = inherit_from or "user"
463
464 if ucl.groups[ name ] then return error( "Group already exists, cannot add again (" .. name .. ")", 2 ) end
465 if inherit_from then
466 if inherit_from == name then return error( "Group cannot inherit from itself", 2 ) end
467 if not ucl.groups[ inherit_from ] then return error( "Invalid group for inheritance (" .. tostring( inherit_from ) .. ")", 2 ) end
468 end
469
470 -- Lower case'ify
471 for k, v in ipairs( allows ) do allows[ k ] = v:lower() end
472
473 ucl.groups[ name ] = { allow=allows, inherit_from=inherit_from }
474 ucl.saveGroups()
475
476 hook.Call( ULib.HOOK_UCLCHANGED )
477end
478
479
480--[[
481 Function: ucl.groupAllow
482
483 Adds or removes an access tag in the allows for a group. Automatically reprobes, automatically saves.
484
485 Parameters:
486
487 name - A string of the group name. (IE: superadmin)
488 access - The string of the access or a table of accesses to add or remove. Access tags can be specified in values in the table for allows.
489 revoke - *(Optional, defaults to false)* A boolean of whether access should be granted or revoked.
490
491 Returns:
492
493 A boolean stating whether you changed anything or not.
494
495 Revisions:
496
497 v2.40 - Initial.
498]]
499function ucl.groupAllow( name, access, revoke )
500 ULib.checkArg( 1, "ULib.ucl.groupAllow", "string", name )
501 ULib.checkArg( 2, "ULib.ucl.groupAllow", {"string","table"}, access )
502 ULib.checkArg( 3, "ULib.ucl.groupAllow", {"nil","boolean"}, revoke )
503
504 if type( access ) == "string" then access = { access } end
505 if not ucl.groups[ name ] then return error( "Group does not exist for changing access (" .. name .. ")", 2 ) end
506
507 local allow = ucl.groups[ name ].allow
508
509 local changed = false
510 for k, v in pairs( access ) do
511 local access = v:lower()
512 local accesstag
513 if type( k ) == "string" then
514 accesstag = v
515 access = k:lower()
516 end
517
518 if not revoke and (allow[ access ] ~= accesstag or (not accesstag and not ULib.findInTable( allow, access ))) then
519 changed = true
520 if not accesstag then
521 table.insert( allow, access )
522 allow[ access ] = nil -- Ensure no access tag
523 else
524 allow[ access ] = accesstag
525 if ULib.findInTable( allow, access ) then -- Ensure removal of non-access tag version
526 table.remove( allow, ULib.findInTable( allow, access ) )
527 end
528 end
529 elseif revoke and (allow[ access ] or ULib.findInTable( allow, access )) then
530 changed = true
531
532 allow[ access ] = nil -- Remove any accessTags
533 if ULib.findInTable( allow, access ) then
534 table.remove( allow, ULib.findInTable( allow, access ) )
535 end
536 end
537 end
538
539 if changed then
540 for id, userInfo in pairs( ucl.authed ) do
541 local ply = ULib.getPlyByID( id )
542 if ply and ply:CheckGroup( name ) then
543 ULib.queueFunctionCall( hook.Call, ULib.HOOK_UCLAUTH, _, ply ) -- Inform the masses
544 end
545 end
546
547 ucl.saveGroups()
548
549 hook.Call( ULib.HOOK_UCLCHANGED )
550 end
551
552 return changed
553end
554
555
556--[[
557 Function: ucl.renameGroup
558
559 Renames a group in the UCL. Automatically moves current members, automatically renames inheritances, automatically saves.
560
561 Parameters:
562
563 orig - A string of the original group name. (IE: superadmin)
564 new - A string of the new group name. (IE: owner)
565
566 Revisions:
567
568 v2.40 - Initial.
569]]
570function ucl.renameGroup( orig, new )
571 ULib.checkArg( 1, "ULib.ucl.renameGroup", "string", orig )
572 ULib.checkArg( 2, "ULib.ucl.renameGroup", "string", new )
573
574 if orig == ULib.ACCESS_ALL then return error( "This group (" .. orig .. ") cannot be renamed!", 2 ) end
575 if not ucl.groups[ orig ] then return error( "Group does not exist for renaming (" .. orig .. ")", 2 ) end
576 if ucl.groups[ new ] then return error( "Group already exists, cannot rename (" .. new .. ")", 2 ) end
577
578 for id, userInfo in pairs( ucl.users ) do
579 if userInfo.group == orig then
580 userInfo.group = new
581 ucl.saveUser( id )
582 end
583 end
584
585 for id, userInfo in pairs( ucl.authed ) do
586 local ply = ULib.getPlyByID( id )
587 if ply and ply:CheckGroup( orig ) then
588 if ply:GetUserGroup() == orig then
589 ULib.queueFunctionCall( ply.SetUserGroup, ply, new ) -- Queued so group will be removed
590 else
591 ULib.queueFunctionCall( hook.Call, ULib.HOOK_UCLAUTH, _, ply ) -- Inform the masses
592 end
593 end
594 end
595
596 ucl.groups[ new ] = ucl.groups[ orig ] -- Copy!
597 ucl.groups[ orig ] = nil
598
599 for _, groupInfo in pairs( ucl.groups ) do
600 if groupInfo.inherit_from == orig then
601 groupInfo.inherit_from = new
602 end
603 end
604
605 --ucl.saveUsers() --Group was changed, saving all users to reflect
606 ucl.saveGroups()
607
608 hook.Call( ULib.HOOK_UCLCHANGED )
609end
610
611
612--[[
613 Function: ucl.setGroupInheritance
614
615 Sets a group's inheritance in the UCL. Automatically reprobes current members, automatically saves.
616
617 Parameters:
618
619 group - A string of the group name. (IE: superadmin)
620 inherit_from - Either a string of the new inheritance group name or nil to remove inheritance. (IE: admin)
621
622 Revisions:
623
624 v2.40 - Initial.
625]]
626function ucl.setGroupInheritance( group, inherit_from )
627 ULib.checkArg( 1, "ULib.ucl.renameGroup", "string", group )
628 ULib.checkArg( 2, "ULib.ucl.renameGroup", {"nil","string"}, inherit_from )
629 if inherit_from then
630 if inherit_from == ULib.ACCESS_ALL then inherit_from = nil end -- Implicitly inherited
631 end
632
633 if group == ULib.ACCESS_ALL then return error( "This group (" .. group .. ") cannot have it's inheritance changed!", 2 ) end
634 if not ucl.groups[ group ] then return error( "Group does not exist (" .. group .. ")", 2 ) end
635 if inherit_from and not ucl.groups[ inherit_from ] then return error( "Group for inheritance does not exist (" .. inherit_from .. ")", 2 ) end
636
637 -- Check for cycles
638 local old_inherit = ucl.groups[ group ].inherit_from
639 ucl.groups[ group ].inherit_from = inherit_from -- Temporary!
640 local groupCheck = ucl.groupInheritsFrom( group )
641 while groupCheck do
642 if groupCheck == group then -- Got back to ourselves. This is bad.
643 ucl.groups[ group ].inherit_from = old_inherit -- Set it back
644 error( "Changing group \"" .. group .. "\" inheritance to \"" .. inherit_from .. "\" would cause cyclical inheritance. Aborting.", 2 )
645 end
646 groupCheck = ucl.groupInheritsFrom( groupCheck )
647 end
648 ucl.groups[ group ].inherit_from = old_inherit -- Set it back
649
650 if old_inherit == inherit_from then return end -- Nothing to change
651
652 for id, userInfo in pairs( ucl.authed ) do
653 local ply = ULib.getPlyByID( id )
654 if ply and ply:CheckGroup( group ) then
655 ULib.queueFunctionCall( hook.Call, ULib.HOOK_UCLAUTH, _, ply ) -- Queued so group will be changed
656 end
657 end
658
659 ucl.groups[ group ].inherit_from = inherit_from
660
661 ucl.saveGroups()
662
663 hook.Call( ULib.HOOK_UCLCHANGED )
664end
665
666
667--[[
668 Function: ucl.setGroupCanTarget
669
670 Sets what a group is allowed to target in the UCL. Automatically saves.
671
672 Parameters:
673
674 group - A string of the group name. (IE: superadmin)
675 can_target - Either a string of who the group is allowed to target (IE: !%admin) or nil to clear the restriction.
676
677 Revisions:
678
679 v2.40 - Initial.
680]]
681function ucl.setGroupCanTarget( group, can_target )
682 ULib.checkArg( 1, "ULib.ucl.setGroupCanTarget", "string", group )
683 ULib.checkArg( 2, "ULib.ucl.setGroupCanTarget", {"nil","string"}, can_target )
684 if not ucl.groups[ group ] then return error( "Group does not exist (" .. group .. ")", 2 ) end
685
686 if ucl.groups[ group ].can_target == can_target then return end -- Nothing to change
687
688 ucl.groups[ group ].can_target = can_target
689
690 ucl.saveGroups()
691
692 hook.Call( ULib.HOOK_UCLCHANGED )
693end
694
695
696--[[
697 Function: ucl.removeGroup
698
699 Removes a group from the UCL. Automatically removes this group from members in it, automatically patches inheritances, automatically saves.
700
701 Parameters:
702
703 name - A string of the group name. (IE: superadmin)
704
705 Revisions:
706
707 v2.10 - Initial.
708 v2.40 - Rewrite, removed write parameter.
709]]
710function ucl.removeGroup( name )
711 ULib.checkArg( 1, "ULib.ucl.removeGroup", "string", name )
712
713 if name == ULib.ACCESS_ALL then return error( "This group (" .. name .. ") cannot be removed!", 2 ) end
714 if not ucl.groups[ name ] then return error( "Group does not exist for removing (" .. name .. ")", 2 ) end
715
716 local inherits_from = ucl.groupInheritsFrom( name )
717 if inherits_from == ULib.ACCESS_ALL then inherits_from = nil end -- Easier
718
719 for id, userInfo in pairs( ucl.users ) do
720 if userInfo.group == name then
721 userInfo.group = inherits_from
722 ucl.saveUsers( id )
723 end
724 end
725
726 for id, userInfo in pairs( ucl.authed ) do
727 local ply = ULib.getPlyByID( id )
728 if ply and ply:CheckGroup( name ) then
729 if ply:GetUserGroup() == name then
730 ULib.queueFunctionCall( ply.SetUserGroup, ply, inherits_from or ULib.ACCESS_ALL ) -- Queued so group will be removed
731 else
732 ULib.queueFunctionCall( hook.Call, ULib.HOOK_UCLAUTH, _, ply ) -- Inform the masses
733 end
734 end
735 end
736
737 ucl.groups[ name ] = nil
738 for _, groupInfo in pairs( ucl.groups ) do
739 if groupInfo.inherit_from == name then
740 groupInfo.inherit_from = inherits_from
741 end
742 end
743
744 --ucl.saveUsers() --Group was removed, saving all users to reflect
745 ucl.saveGroups()
746
747 hook.Call( ULib.HOOK_UCLCHANGED )
748end
749
750--[[
751 Function: ucl.getUserRegisteredID
752
753 Returns the SteamID, IP, or UniqueID of a player if they're registered under any of those IDs under ucl.users. Checks in order. Returns nil if not registered.
754
755 Parameters:
756
757 ply - The player object you wish to check.
758
759 Revisions:
760
761 2.41 - Initial.
762]]
763
764function ucl.getUserRegisteredID( ply )
765 local id = ply:SteamID()
766 local uid = ply:UniqueID()
767 local ip = ULib.splitPort( ply:IPAddress() )
768 local checkIndexes = { id, ip, uid }
769 for _, index in ipairs( checkIndexes ) do
770 if ULib.ucl.users[ index ] then
771 return id
772 end
773 end
774end
775
776--[[
777 Function: ucl.addUser
778
779 Adds a user to the UCL. Automatically probes for the user, automatically saves.
780
781 Parameters:
782
783 id - The SteamID, IP, or UniqueID of the user you wish to add.
784 allows - *(Optional, defaults to empty table)* The list of access you wish to give this user.
785 denies - *(Optional, defaults to empty table)* The list of access you wish to explicitly deny this user.
786 group - *(Optional)* The sting of the group this user should belong to. Must be a valid group.
787
788 Revisions:
789
790 2.10 - No longer makes a group if it doesn't exist.
791 2.40 - Rewrite, changed the arguments all around.
792]]
793function ucl.addUser( id, allows, denies, group )
794 ULib.checkArg( 1, "ULib.ucl.addUser", "string", id )
795 ULib.checkArg( 2, "ULib.ucl.addUser", {"nil","table"}, allows )
796 ULib.checkArg( 3, "ULib.ucl.addUser", {"nil","table"}, denies )
797 ULib.checkArg( 4, "ULib.ucl.addUser", {"nil","string"}, group )
798
799 id = id:upper() -- In case of steamid, needs to be upper case
800 allows = allows or {}
801 denies = denies or {}
802 if allows == ULib.DEFAULT_GRANT_ACCESS.allow then allows = table.Copy( allows ) end -- Otherwise we'd be changing all guest access
803 if denies == ULib.DEFAULT_GRANT_ACCESS.deny then denies = table.Copy( denies ) end -- Otherwise we'd be changing all guest access
804 if group and not ucl.groups[ group ] then return error( "Group does not exist for adding user to (" .. group .. ")", 2 ) end
805
806 -- Lower case'ify
807 for k, v in ipairs( allows ) do allows[ k ] = v end
808 for k, v in ipairs( denies ) do denies[ k ] = v end
809
810 local name
811 if ucl.users[ id ] and ucl.users[ id ].name then name = ucl.users[ id ].name end -- Preserve name
812 ucl.users[ id ] = { allow=allows, deny=denies, group=group, name=name }
813
814 --ucl.saveUsers() --User was added, only need to save that one user.
815 ucl.saveUser(id)
816
817 local ply = ULib.getPlyByID( id )
818 if ply then
819 ucl.probe( ply )
820 else -- Otherwise this gets called twice
821 hook.Call( ULib.HOOK_UCLCHANGED )
822 end
823end
824
825
826--[[
827 Function: ucl.userAllow
828
829 Adds or removes an access tag in the allows or denies for a user. Automatically reprobes, automatically saves.
830
831 Parameters:
832
833 id - The SteamID, IP, or UniqueID of the user to change. Must be a valid, existing ID, or an ID of a connected player.
834 access - The string of the access or a table of accesses to add or remove. Access tags can be specified in values in the table for allows.
835 revoke - *(Optional, defaults to false)* A boolean of whether the access tag should be added or removed
836 from the allow or deny list. If true, it's removed.
837 deny - *(Optional, defaults to false)* If true, the access is added or removed from the deny list,
838 if false it's added or removed from the allow list.
839
840 Returns:
841
842 A boolean stating whether you changed anything or not.
843
844 Revisions:
845
846 v2.40 - Initial.
847 v2.50 - Relaxed restrictions on id parameter.
848 v2.51 - Fixed this function not working on disconnected players.
849]]
850function ucl.userAllow( id, access, revoke, deny )
851 ULib.checkArg( 1, "ULib.ucl.userAllow", "string", id )
852 ULib.checkArg( 2, "ULib.ucl.userAllow", {"string","table"}, access )
853 ULib.checkArg( 3, "ULib.ucl.userAllow", {"nil","boolean"}, revoke )
854 ULib.checkArg( 4, "ULib.ucl.userAllow", {"nil","boolean"}, deny )
855
856 id = id:upper() -- In case of steamid, needs to be upper case
857 if type( access ) == "string" then access = { access } end
858
859 local uid = id
860 if not ucl.authed[ uid ] then -- Check to see if it's a steamid or IP
861 local ply = ULib.getPlyByID( id )
862 if ply and ply:IsValid() then
863 uid = ply:UniqueID()
864 end
865 end
866
867 local userInfo = ucl.users[ id ] or ucl.authed[ uid ] -- Check both tables
868 if not userInfo then return error( "User id does not exist for changing access (" .. id .. ")", 2 ) end
869
870 -- If they're connected but don't exist in the ULib user database, add them.
871 -- This can be the case if they're only using the default garrysmod file to pull in users.
872 if userInfo.guest then
873 local allows = {}
874 local denies = {}
875 if not revoke and not deny then allows = access
876 elseif not revoke and deny then denies = access end
877
878 ucl.addUser( id, allows, denies )
879 return true -- And we're done
880 end
881
882 local accessTable = userInfo.allow
883 local otherTable = userInfo.deny
884 if deny then
885 accessTable = userInfo.deny
886 otherTable = userInfo.allow
887 end
888
889 local changed = false
890 for k, v in pairs( access ) do
891 local access = v:lower()
892 local accesstag
893 if type( k ) == "string" then
894 access = k:lower()
895 if not revoke and not deny then -- Not valid to have accessTags unless this is the case
896 accesstag = v
897 end
898 end
899
900 if not revoke and (accessTable[ access ] ~= accesstag or (not accesstag and not ULib.findInTable( accessTable, access ))) then
901 changed = true
902 if not accesstag then
903 table.insert( accessTable, access )
904 accessTable[ access ] = nil -- Ensure no access tag
905 else
906 accessTable[ access ] = accesstag
907 if ULib.findInTable( accessTable, access ) then -- Ensure removal of non-access tag version
908 table.remove( accessTable, ULib.findInTable( accessTable, access ) )
909 end
910 end
911
912 -- If it's on the other table, remove
913 if deny then
914 otherTable[ access ] = nil -- Remove any accessTags
915 end
916 if ULib.findInTable( otherTable, access ) then
917 table.remove( otherTable, ULib.findInTable( otherTable, access ) )
918 end
919
920 elseif revoke and (accessTable[ access ] or ULib.findInTable( accessTable, access )) then
921 changed = true
922
923 if not deny then
924 accessTable[ access ] = nil -- Remove any accessTags
925 end
926 if ULib.findInTable( accessTable, access ) then
927 table.remove( accessTable, ULib.findInTable( accessTable, access ) )
928 end
929 end
930 end
931
932 if changed then
933 local ply = ULib.getPlyByID( id )
934 if ply then
935 ULib.queueFunctionCall( hook.Call, ULib.HOOK_UCLAUTH, _, ply ) -- Inform the masses
936 end
937
938 --ucl.saveUsers() --User's accesses are changed, need only save that one user.
939 ucl.saveUser( id )
940
941 hook.Call( ULib.HOOK_UCLCHANGED )
942 end
943
944 return changed
945end
946
947
948--[[
949 Function: ucl.removeUser
950
951 Removes a user from the UCL. Automatically probes for the user, automatically saves.
952
953 Parameters:
954
955 id - The SteamID, IP, or UniqueID of the user you wish to remove. Must be a valid, existing ID.
956 The unique id of a connected user is always valid.
957
958 Revisions:
959
960 v2.40 - Rewrite, removed the write argument.
961]]
962function ucl.removeUser( id )
963 ULib.checkArg( 1, "ULib.ucl.addUser", "string", id )
964 id = id:upper() -- In case of steamid, needs to be upper case
965
966 local userInfo = ucl.users[ id ] or ucl.authed[ id ] -- Check both tables
967 if not userInfo then return error( "User id does not exist for removing (" .. id .. ")", 2 ) end
968
969 local changed = false
970 local q_id = nil --Query Index
971
972 if ucl.authed[ id ] and not ucl.users[ id ] then -- Different ids between offline and authed
973 local ply = ULib.getPlyByID( id )
974 if not ply then return error( "SANITY CHECK FAILED!" ) end -- Should never be invalid
975
976 local ip = ULib.splitPort( ply:IPAddress() )
977 local checkIndexes = { ply:UniqueID(), ip, ply:SteamID() }
978
979 for _, index in ipairs( checkIndexes ) do
980 if ucl.users[ index ] then
981 changed = true
982 ucl.users[ index ] = nil
983 q_id = index
984 break -- Only match the first one
985 end
986 end
987 else
988 changed = true
989 ucl.users[ id ] = nil
990 q_id = id
991 end
992
993 if changed then -- If the user is only added to the default garry file, then nothing changed
994 --ucl.saveUsers() -- user was removed, need to remove them from the database
995 if q_id then
996 if ULib.ucl.db:status() == 0 then
997 local query = ULib.ucl.db:query("DELETE FROM " .. DATABASE_TABLE .. " WHERE uid='" .. Escape(q_id) .. "'")
998 function query:onSuccess( data )
999 ServerLog("SUCCESSFULLY REMOVED (" .. q_id .. ") from the database\n")
1000 end
1001 function query:onError( Q, E )
1002 ServerLog("FAILED REMOVING (" .. q_id .. ") from the database\n")
1003 ServerLog(E .. "\n")
1004 end
1005 query:start()
1006 else
1007 ServerLog("Database unexpectedly offline, attempting to reconnect\n")
1008 ULib_SQL_ConnectToDatabase()
1009 end
1010 end
1011 end
1012
1013 local ply = ULib.getPlyByID( id )
1014 if ply then
1015 ply:SetUserGroup( ULib.ACCESS_ALL, true )
1016 ucl.probe( ply ) -- Reprobe
1017 else -- Otherwise this is called twice
1018 hook.Call( ULib.HOOK_UCLCHANGED )
1019 end
1020end
1021
1022
1023--[[
1024 Function: ucl.registerAccess
1025
1026 Inform UCL about the existence of a particular access string, optionally make it have a certain default access,
1027 optionally give a help message along with it. The use of this function is optional, it is not required in order
1028 to query an access string, but it's use is highly recommended.
1029
1030 Parameters:
1031
1032 access - The access string (IE, "ulx slap" or "ups deletionAccess").
1033 groups - *(Optional, defaults to no access)* Either a string of a group or a table of groups to give the default access to.
1034 comment - *(Optional)* A brief description of what this access string is granting access to.
1035 category - *(Optional)* Category for the access string (IE, "Command", "CVAR", "Limits")
1036
1037 Revisions:
1038
1039 v2.40 - Rewrite.
1040]]
1041function ucl.registerAccess( access, groups, comment, category )
1042 ULib.checkArg( 1, "ULib.ucl.registerAccess", "string", access )
1043 ULib.checkArg( 2, "ULib.ucl.registerAccess", {"nil","string","table"}, groups )
1044 ULib.checkArg( 3, "ULib.ucl.registerAccess", {"nil","string"}, comment )
1045 ULib.checkArg( 4, "ULib.ucl.registerAccess", {"nil","string"}, category )
1046
1047 access = access:lower()
1048 comment = comment or ""
1049 if groups == nil then groups = {} end
1050 if type( groups ) == "string" then
1051 groups = { groups }
1052 end
1053
1054 accessCategories[ access ] = category
1055 if accessStrings[ access ] ~= comment then -- Only if not already registered or if the comment has changed
1056 accessStrings[ access ] = comment
1057
1058 -- Create a named timer so no matter how many times this function is called in a frame, it's only saved once.
1059 timer.Create( "ULibSaveAccessStrings", 1, 1, saveAccessStringRegistration ) -- 1 sec delay, 1 rep
1060
1061 -- Double check to make sure this isn't already registered with some group somewhere before re-adding it
1062 for _, groupInfo in pairs( ucl.groups ) do
1063 if table.HasValue( groupInfo.allow, access ) then return end -- Found, don't add again
1064 end
1065
1066 for _, group in ipairs( groups ) do
1067 -- Create group if it doesn't exist
1068 if not ucl.groups[ group ] then ucl.addGroup( group ) end
1069
1070 table.insert( ucl.groups[ group ].allow, access )
1071 end
1072
1073 timer.Create( "ULibSaveGroups", 1, 1, ucl.saveGroups ) -- 1 sec delay, 1 rep
1074 end
1075end
1076
1077
1078--[[
1079 Function: ucl.probe
1080
1081 Probes the user to assign access appropriately.
1082 *DO NOT CALL THIS DIRECTLY, UCL HANDLES IT.*
1083
1084 Parameters:
1085
1086 ply - The player object to probe.
1087
1088 Revisions:
1089
1090 v2.40 - Rewrite.
1091]]
1092function ucl.probe( ply )
1093 local ip = ULib.splitPort( ply:IPAddress() )
1094 local uid = ply:UniqueID()
1095 local checkIndexes = { uid, ip, ply:SteamID() }
1096
1097 local match = false
1098 for _, index in ipairs( checkIndexes ) do
1099 if ucl.users[ index ] then
1100 ucl.authed[ uid ] = ucl.users[ index ] -- Setup an ALIAS
1101
1102 -- If they have a group, set it
1103 local group = ucl.authed[ uid ].group
1104 if group and group ~= "" then
1105 ply:SetUserGroup( group, true )
1106 end
1107
1108 -- Update their name
1109 ucl.authed[ uid ].name = ply:Nick()
1110 --ucl.saveUsers() -- Save only the probed player to account for a name change
1111 ucl.saveUser( index )
1112
1113 match = true
1114 break
1115 end
1116 end
1117
1118 if not match then
1119 ucl.authed[ ply:UniqueID() ] = ULib.DEFAULT_GRANT_ACCESS
1120 if ply.tmp_group then
1121 ply:SetUserGroup( ply.tmp_group, true ) -- Make sure they keep the group
1122 ply.tmp_group = nil
1123 end
1124 end
1125
1126 hook.Call( ULib.HOOK_UCLCHANGED )
1127 hook.Call( ULib.HOOK_UCLAUTH, _, ply )
1128end
1129-- Note that this function is hooked into "PlayerAuthed", below.
1130
1131
1132local function botCheck( ply )
1133 if ply:IsBot() and not ucl.authed[ ply:UniqueID() ] then
1134 ply:SetUserGroup( ULib.ACCESS_ALL, true ) -- Give it a group!
1135 ucl.probe( ply )
1136 end
1137end
1138hook.Add( "PlayerInitialSpawn", "ULibSendAuthToClients", botCheck, HOOK_MONITOR_HIGH )
1139
1140local function sendAuthToClients( ply )
1141 ULib.clientRPC( _, "authPlayerIfReady", ply, ply:UserID() ) -- Call on client
1142end
1143hook.Add( ULib.HOOK_UCLAUTH, "ULibSendAuthToClients", sendAuthToClients, HOOK_MONITOR_LOW )
1144
1145local function sendUCLDataToClient( ply )
1146 ULib.clientRPC( ply, "ULib.ucl.initClientUCL", ucl.authed, ucl.groups ) -- Send all UCL data (minus offline users) to all loaded users
1147 ULib.clientRPC( ply, "hook.Call", ULib.HOOK_UCLCHANGED ) -- Call hook on client
1148 ULib.clientRPC( ply, "authPlayerIfReady", ply, ply:UserID() ) -- Call on client
1149end
1150hook.Add( ULib.HOOK_LOCALPLAYERREADY, "ULibSendUCLDataToClient", sendUCLDataToClient, HOOK_MONITOR_HIGH )
1151
1152local function playerDisconnected( ply )
1153 -- We want to perform these actions after everything else has processed through, but we need high priority hook to ensure we don't get sniped.
1154 local uid = ply:UniqueID()
1155 ULib.queueFunctionCall( function()
1156 ucl.authed[ uid ] = nil
1157 hook.Call( ULib.HOOK_UCLCHANGED )
1158 end )
1159end
1160hook.Add( "PlayerDisconnected", "ULibUCLDisconnect", playerDisconnected, HOOK_MONITOR_HIGH )
1161
1162local function UCLChanged()
1163 ULib.clientRPC( _, "ULib.ucl.initClientUCL", ucl.authed, ucl.groups ) -- Send all UCL data (minus offline users) to all loaded users
1164 ULib.clientRPC( _, "hook.Call", ULib.HOOK_UCLCHANGED ) -- Call hook on client
1165end
1166hook.Add( ULib.HOOK_UCLCHANGED, "ULibSendUCLToClients", UCLChanged )
1167
1168--[[
1169-- The following is useful for debugging since Garry changes client bootstrapping so frequently
1170hook.Add( ULib.HOOK_UCLCHANGED, "UTEST", function() print( "HERE HERE: UCL Changed" ) end )
1171hook.Add( "PlayerInitialSpawn", "UTEST", function() print( "HERE HERE: Initial Spawn" ) end )
1172hook.Add( "PlayerAuthed", "UTEST", function() print( "HERE HERE: Player Authed" ) end )
1173]]
1174
1175---------- Modify
1176
1177-- Move garry's auth function so it gets called sooner
1178local playerAuth = hook.GetTable().PlayerInitialSpawn.PlayerAuthSpawn
1179hook.Remove( "PlayerInitialSpawn", "PlayerAuthSpawn" ) -- Remove from original spot
1180
1181local function newPlayerAuth( ply, ... )
1182 ucl.authed[ ply:UniqueID() ] = nil -- If the player ent is removed before disconnecting, we can have this hanging out there.
1183 playerAuth( ply, ... ) -- Put here, slightly ahead of ucl.
1184 ucl.probe( ply, ... )
1185end
1186hook.Add( "PlayerAuthed", "ULibAuth", newPlayerAuth, HOOK_MONITOR_HIGH )
1187
1188local meta = FindMetaTable( "Player" )
1189if not meta then return end
1190
1191local oldSetUserGroup = meta.SetUserGroup
1192function meta:SetUserGroup( group, dontCall )
1193 if not ucl.groups[ group ] then ULib.ucl.addGroup( group ) end
1194
1195 local oldGroup = self:GetUserGroup()
1196 oldSetUserGroup( self, group )
1197
1198 if ucl.authed[ self:UniqueID() ] then
1199 if ucl.authed[ self:UniqueID() ] == ULib.DEFAULT_GRANT_ACCESS then
1200 ucl.authed[ self:UniqueID() ] = table.Copy( ULib.DEFAULT_GRANT_ACCESS )
1201 end
1202 ucl.authed[ self:UniqueID() ].group = group
1203 else
1204 self.tmp_group = group
1205 end
1206
1207 if not dontCall and self:GetUserGroup() ~= oldGroup then -- Changed! Inform the masses of the change
1208 hook.Call( ULib.HOOK_UCLCHANGED )
1209 hook.Call( ULib.HOOK_UCLAUTH, _, self )
1210 end
1211end