Closed danielmarschall closed 2 weeks ago
@wehowski Please add all your thoughts in re the Multi-Tenancy-Topic into this GitHub issue. Thank you!
At "hosting.oidplus.com" I have a few standalone systems, so these would be a perfect possibility to test and develop the multi-tenancy.
So, I think a tenant should have:
tenant
field), because some tenants might have a lot of data and therefore destroy the performance of the other tenant.I have the following idea how to do this change without a lot of effort:
tenants.json
. This file might look like this:{
"https://hosted.viathinksoft.de/abc/" => ["userdata_abc", "userdata_pub_abc"],
"https://hosted.viathinksoft.de/def/" => ["userdata_def", "userdata_pub_def"],
"https://hosted.viathinksoft.de/xyz/" => ["userdata_xyz", "userdata_pub_xyz"],
"default" => ["userdata", "userdata_pub"]
}
$_SERVER['SCRIPT_URI']
will be looked up in the tenants.json
in order to find the correct userdata
and userdata_pub
dir.userdata/
and userdata_pub/
and replace it with a method OIDplus::getUserDataDir()
and OIDplus::getUserDataPubDir()
.plugins/
, but also in userdata/plugins/
. We do not need to do the "plugin certification" stuff yet, because as long as the tentant has no FTP access to userdata/, they can't install plugins (or malicous code) anyways./usr/bin/php cron.php
need to be replaced by "web-cronjobs" (curl http://..../cron.php
)What do you think?
... Should the web visitor see that there are multiple users in one system, i.e. the system has a "IANA PEN" root, which you can extend, and then you see "RA 123", "RA 456", etc. ?
I would suggest to decide how an OID can be REGISTERED vs. how it can be ADMINISTRATED. To have a single root OID would prevent to add other arcs, e.g. I would suggest to offer a registration formualr, where we can do the validation for PEN automatically (e.g. by confirmation email to the registered RA). This way we could provide (automatically) registered OIDs are ~valid.
So, I think a tenant should have:...
Yes, that sounds good! Maybe it would make sense to first seperate config/db/dirs/paths as the question about to customize plugins is one with different possible solutions?
On a tenancy enabled system, there will a file tenants.json. This file might look like this:...
Yes. Maybe it yould make sense to use a php file or to have the tenants.json in a none-readable dir? The question is if it would be reasonable to save (addtional) tenancy config in (central) DB? To get the tenant from request in common there are
For cron/console maybe we could use an argument for tenant-ID/Domain/path like the PHP build in web-server?
Software Updates should be done by admin only, or it should be on a seperated hosting multi-tenant system as you have now on hosted.oidplus? Because we have a single runtime code an cannot personalize it for tenancy in a comfortable way?
Removing the hardcoded userdata/
and userdata_pub/
will be a lot of work. We could do it step-by-step as preparation for the multi-tenancy. As soon as the hardcoded strings are replaced by OIDplus::getUserDataDir()
, we have freedom to implement this method as we want.
$ grep -r "userdata/" | cut -d ':' -f 1 | sort | uniq | grep -v pristine
changelog.json.php
dev/find_copyright_missing
dev/test_database_plugins
doc/config_values.md
doc/developer_notes/database/firebird_notes.txt
doc/developer_notes/database/oracle_test_vm.txt
doc/install_alpine_linux.md
doc/install_facebook_oauth2.md
doc/install_fail2ban.md
doc/install_google_oauth2.md
doc/install_ldap.md
doc/oidplus_custom_guid.md
favicon.png.php
.gitignore
.idea/workspace.xml
includes/classes/OIDplusAuthUtils.class.php
includes/classes/OIDplusBaseConfig.class.php
includes/classes/OIDplus.class.php
includes/classes/OIDplusConfig.class.php
includes/classes/OIDplusConfigInitializationException.class.php
includes/oidplus_limits.inc.php
nginx.conf
oidplus.min.css.php
phpstan.neon.dist
plugins/frdl/publicPages/1276945_rdap/OIDplusRDAP.class.php
plugins/viathinksoft/adminPages/010_notifications/OIDplusPageAdminNotifications.class.php
plugins/viathinksoft/adminPages/050_oobe/oobe.php
plugins/viathinksoft/adminPages/110_system_config/OIDplusPageAdminSystemConfig.class.php
plugins/viathinksoft/adminPages/120_registration/OIDplusPageAdminRegistration.class.php
plugins/viathinksoft/adminPages/900_software_update/OIDplusPageAdminSoftwareUpdate.class.php
plugins/viathinksoft/adminPages/900_software_update/private/gen_serverside_git
plugins/viathinksoft/adminPages/900_software_update/private/gen_serverside_svn
plugins/viathinksoft/adminPages/901_vnag_version_check/vnag.php
plugins/viathinksoft/adminPages/902_systemfile_check/OIDplusPageAdminSystemFileCheck.class.php
plugins/viathinksoft/adminPages/920_nostalgia/export_dos.php
plugins/viathinksoft/adminPages/920_nostalgia/export_win.php
plugins/viathinksoft/captcha/vts_challenge/OIDplusCaptchaPluginVtsClientChallenge.class.php
plugins/viathinksoft/database/sqlite3/OIDplusDatabaseConnectionSQLite3.class.php
plugins/viathinksoft/database/sqlite3/OIDplusDatabasePluginSQLite3.class.php
plugins/viathinksoft/design/default/oidplus_base.css
plugins/viathinksoft/design/ironbase/oidplus_base.css
plugins/viathinksoft/language/dede/messages.xml
plugins/viathinksoft/logger/300_userdata_logfile/OIDplusLoggerPluginUserdataLogfile.class.php
plugins/viathinksoft/objectTypes/gs1/barcode.php
plugins/viathinksoft/publicPages/000_objects/OIDplusPagePublicObjects.class.php
plugins/viathinksoft/publicPages/000_objects/welcome$dede.html
plugins/viathinksoft/publicPages/000_objects/welcome.html
plugins/viathinksoft/publicPages/092_forgot_password_admin/OIDplusPagePublicForgotPasswordAdmin.class.php
plugins/viathinksoft/publicPages/095_attachments/OIDplusPagePublicAttachments.class.php
plugins/viathinksoft/publicPages/500_resources/OIDplusPagePublicResources.class.php
plugins/viathinksoft/publicPages/500_resources/show_icon.php
polyfill.min.js.php
res/ATTENTION.TXT
setup/includes/setup_base.js
setup/index.php
setup/setup.min.css.php
userdata/attachments/info.txt
userdata/baseconfig/info.txt
userdata/cache/info.txt
userdata/cache/master_changelog.json
userdata/cache/translation_d96733c0588ec4e1450d9b821cda9859.ser
userdata/database/info.txt
userdata/favicon/info.txt
userdata/info.txt
userdata/logs/info.txt
userdata/private/info.txt
userdata_pub/info.txt
userdata/resources/info.txt
userdata/styles/info.txt
userdata/welcome/info.txt
$ grep -r "userdata_pub/" | cut -d ':' -f 1 | sort | uniq | grep -v pristine
plugins/viathinksoft/adminPages/902_systemfile_check/OIDplusPageAdminSystemFileCheck.class.php
From internal email communication (edited code):
Als Vorbereitung würde ich den CORE so ändern, dass alle Zugriffe auf userdata und userdata_pub gekapselt werden. Um es sogar noch mehr flexibler zu machen würde ich es so machen:
Vorher:
$irgendeinedatei = file_get_contents(OIDplus::localpath()."userdata/baseconfig/test123.php");
Neu:
$irgendeinedatei = file_get_contents(OIDplus::getUserDataDir("baseconfig")."test123.php");
Dadurch kann die Funktion OIDplus::getUserDataDir()
entscheiden, dass beispielsweise "baseconfig" Mandant-Abhängig wäre, aber "cache" nicht.
Die Funktion OIDplus::getUserDataDir können wir im Anschluss in Absprache miteinander implementieren, ggf. sogar Plugin-Spezifisch.
Eine mögliche Implementierung könnte sein:
function tenantSubDirName(): string {
return $_SERVER['HTTP_HOST'];
}
function getUserDataDir(string $subdir, bool $public=false): string {
$localdir = OIDplus::localpath(); // contains trailing dir separator
$priv_or_pub = $public ? "userdata_pub/" : "userdata/";
$tenant_dir = "tenant/".tenantSubDirName()."/";
// Tenancy dependant dir `userdata/tenant/<tenantSubDirName>/$subdir` existing? Then use this.
$candidate1 = $localdir.$priv_or_pub.$tenant_dir.$subdir.'/';
if (is_dir($candidate1)) return $candidate1;
// General dir `userdata/$subdir` existing? Then use this.
$candidate2 = $localdir.$priv_or_pub.$subdir.'/';
if (is_dir($candidate2)) return $candidate2;
if (is_dir($localdir.$priv_or_pub.$tenant_dir)) {
// This is a tenancy-enabled system. Therefore, create tenancy-dependant dir by default
mkdir($candidate1);
return $candidate1;
} else {
// This is a non-tenancy-enabled system. Therefore, create a general dir by default
mkdir($candidate2);
return $candidate2;
}
}
VORTEILE: Dadurch, dass userdata abstrahiert wird, haben wir den Vorteil, dass wirklich ALLES (bis auf die Plugins) individuell gestaltbar ist. Cache, Public Key, Welcome Pages, Baseconfig, einfach alles!
Bzgl. Datenbank-Tabellen: In meiner Variante würde ich dem Administrator die Wahl lassen, das userdata Verzeichnis so bestücken, wie er möchte. Wenn man möchte, dass der DB-Tabellen-Präfix automatisch generiert wird, dann kann man ja gerne Dinge machen wie setValue('TABLENAMEPREFIX', 'oidplus'.md5($mydomain).'_') Die Tabellen werden bei OIDplus ja automatisch erzeugt, wenn die Datenbankverbindung OK ist. Deshalb müssten wir da nix großartig beachten?
@wehowski Is that OK for you?
The change was easier than I thought... Just took 3 hours.
I have now replaced all occurences of userdata/ with OIDplus::getUserDataDir()
.
Theoretically, with this approach, multi-tenancy should already be possible.
In userdata/info.txt , I have added the following explanation:
Tenants
-------
Multiple domains can use the same OIDplus installation but have different
`userdata/` and `userdata_pub/` directories. Therefore, simply create
a directory named `userdata/tenant/www.example.com` where `www.example.com` is
your host name.
Then, if the user visits this domain name, then the data inside
`userdata/tenant/www.example.com` will be chosen rather than `userdata/`.
Each domain can therefore have different database connections (or simply different
table prefixes), different resources (welcome page), etc.
@wehowski Can you please give me feedback on commit 773f4c1 ? Do you think it is useful and can you integrate this userdata abstraction in your tenant plugin, or doesn't it fit in your plugin development? As I said earlier, I try to avoid that we develop two different solutions. So I hope you can integrate parts of my solution into your solution. Feel free to request any changes if you need some!
More things to do:
Hello Daniel, although I did not fully test yet, so far, it looks PERFECT!
@wehowski Hello, you can either download the latest ZIP file from Github (or clone it), or download the SVN snapshot here https://www.viathinksoft.com/download/235/oidplus2_svn.tar.gz
When testing, please also look at this method: https://github.com/danielmarschall/oidplus/blob/master/includes/classes/OIDplus.class.php#L2106 This method is very questionable at the moment, so I am not fully sure about it. You know that I have several systems at https://hosted.oidplus.com/.../ where each OIDplus system has an own directory. I kinda want multi-tenancy here (without defining sub domains), but don't know how to do it. Probably something symlink-related is the solution?
Also a very funny line of code: https://github.com/danielmarschall/oidplus/blob/master/cron.sh#L37
Please wait with the testing. There are a few things broken which I am currently fixing...
Great news! I have now solved a lot of problems and you are free to test the beta version (download the ZIP at GitHub or the SVN snapshot at oidplus.com).
The following systems are now all tenancy systems, meaning I have now only one OIDplus installation which I need to care about!
How does it work in my case?
All directories except one are symlinks:
[22:28]oidplus@viathinksoft.de:~/hosted$ ls
insgesamt 12K
lrwxrwxrwx 1 oidplus oidplus 16 12. Jun 14:21 cjydev -> viathinksoft/
-rw-r--r-- 1 oidplus oidplus 52 25. Nov 2023 index.php
lrwxrwxrwx 1 oidplus oidplus 16 12. Jun 14:21 infernostars -> viathinksoft/
lrwxrwxrwx 1 oidplus oidplus 16 12. Jun 12:38 r74n -> viathinksoft/
-rw-r--r-- 1 oidplus oidplus 178 2. Dez 2023 robots.txt
drwxr-xr-x 15 oidplus oidplus 4,0K 12. Jun 22:08 viathinksoft/
lrwxrwxrwx 1 oidplus oidplus 16 12. Jun 13:41 viathinksoft_demo -> viathinksoft/
The userdata is as follows:
[22:28]oidplus@viathinksoft.de:~/hosted$ ls viathinksoft/userdata/tenant/
insgesamt 16K
drwxr-xr-x 13 oidplus oidplus 4,0K 12. Jun 14:53 hosted.oidplus.com__cjydev/
drwxr-xr-x 13 oidplus oidplus 4,0K 12. Jun 15:00 hosted.oidplus.com__infernostars/
drwxr-xr-x 13 oidplus oidplus 4,0K 12. Jun 12:34 hosted.oidplus.com__r74n/
drwxr-xr-x 13 oidplus oidplus 4,0K 12. Jun 14:54 hosted.oidplus.com__viathinksoft_demo/
lrwxrwxrwx 1 oidplus oidplus 24 12. Jun 12:39 oid.r74n.com -> hosted.oidplus.com__r74n/
ALSO FIXED:
EDIT: Released as 2.0.1.22 to the public (still "beta" though)
Based on an idea by @wehowski : https://startforum.de/content/perma?id=2039
A few basic ideas:
... Multiple RA using one OIDplus codebase and one database
... It simplifies "Hosting" of OIDplus systems
A lot of things need to be considered / decided:
... Plugins: Should Tenancy-Owners be able to install plugins (just for their system)? Then we need to take care that the plugin-loader only loads plugins that are enabled for the Tenancy.
... Should the web visitor see that there are multiple users in one system, i.e. the system has a "IANA PEN" root, which you can extend, and then you see "RA 123", "RA 456", etc. ? One one hand, this might be nice because it might look like a "Master OIDplus system" eventually, but on the other hand, it has the disadvantage that the Tenancy owner cannot choose their own design that represents their RA. For example, this RA has a pretty nice and unique design: https://oid.r74n.com/ So I rather would say Multi-Tenancy means that the RAs are using one codebase, one database, one server folder, but they have different URLs and Designs. (If we allow multiple per-tenancy-plugins, then it is required to have different URLs, because the plugins might change the menu structure)