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