Compare commits

...

1014 commits

Author SHA1 Message Date
Gravita
b16281e04a Merge branch 'release/5.6.11' 2025-04-05 10:03:36 +07:00
Gravita
06f0bc873a [ANY] 5.6.11 stable 2025-04-05 10:00:34 +07:00
Gravita
834fbab12b [FIX] Java 24 dont support SecurityManager 2025-04-03 15:29:53 +07:00
Gravita
822872992f [FIX] Component proguard commands 2025-03-28 11:42:24 +07:00
Gravita
ac43034d45 [ANY] Update dependencies 2025-03-28 11:41:16 +07:00
Gravita
00446b40f0 [FIX] DiscordGameSDK library link 2025-03-19 18:16:45 +07:00
Gravita
9e29053afa [FEATURE] Setup support env configuration in ServerWrapper, fix compile 2025-02-24 19:47:41 +07:00
Gravita
2e93102106 [FEATURE] Add serverName to env configuration in ServerWrapper 2025-02-24 19:21:10 +07:00
Gravita
d4b69195b3 [FEATURE] Support env configuration in ServerWrapper 2025-02-24 19:18:49 +07:00
Gravita
a67fbac8bc [ANY] 5.6.11-dev 2025-02-24 15:19:34 +07:00
Gravita
a7abb9cbfc [ANY] IDEA code inspect 2025-02-24 15:19:09 +07:00
Gravita
689478ee0f Merge tag 'v5.6.10' into dev
5.6.10 stable
2025-02-24 14:35:09 +07:00
Gravita
df77a1ebd6 Merge branch 'release/5.6.10' 2025-02-24 14:34:51 +07:00
Gravita
641796b44e [ANY] 5.6.10 stable 2025-02-24 14:34:44 +07:00
Gravita
b7ed56b27e [ANY] Update Hikari 2025-02-24 14:30:41 +07:00
Gravita
f119bd4b30 [ANY] Delete not working modules 2025-02-19 11:51:51 +07:00
Gravita
bbff0eac64 [FEATURE] Support IO_URING mode in netty 2025-02-18 15:14:55 +07:00
Gravita
41f93b9f8d [ANY] Upgrade netty to 4.2.0.RC3 2025-02-18 14:56:16 +07:00
Gravita
263cf26258
[ANY] Update gradle 2025-02-03 01:03:26 +07:00
Gravita
b9ad7c0f26
[ANY] Update dependencies 2025-02-03 01:03:01 +07:00
Gravita
5ba32e3405
[ANY] Update modules 2025-01-27 20:20:47 +07:00
Gravita
907332ff06
[FEATURE] Fix truststore path 2025-01-27 20:20:44 +07:00
microwin7
1d63fbbd93 [FEATURE] Disabling log4j.xml update while launchserver is running 2025-01-07 11:44:55 +03:00
Gravita
2f4667f5a6
Merge tag 'v5.6.9' into dev
5.6.9 stable release
2025-01-03 12:55:53 +07:00
Gravita
92ada65079
Merge branch 'release/5.6.9' 2025-01-03 12:55:42 +07:00
Gravita
29d98defff
[ANY] 5.6.9 stable 2025-01-03 12:55:33 +07:00
Gravita
d7a379383b
[FIX] Remove socket command debug 2024-12-31 14:35:42 +07:00
Gravita
0111b2ca2b
[FEATURE] Add ControlFile and proxy command handlers 2024-12-31 14:19:31 +07:00
microwin7
4671dfe49d [ANY] Added proxy repository jitpack. Sync moduls 2024-12-31 04:18:40 +03:00
Gravita
bb63aaa0ab
[ANY] Update modules 2024-12-13 22:40:23 +07:00
Gravita
912caa6b8a
[FIX] Add more logging to timestamp failed error 2024-11-05 08:52:59 +07:00
Gravita
926094076c
[FIX] Fix NPE with nativePath PR #719 2024-11-05 07:08:31 +07:00
Gravita
a1af61a599
[ANY] Update modules 2024-10-22 17:14:36 +07:00
Gravita
81b16cb54e
[ANY] Update gradle 2024-10-15 18:53:03 +07:00
Gravita
a486f21fa2
[ANY] Update dependencies 2024-10-15 18:48:20 +07:00
Gravita
86ea247f07
Merge tag 'v5.6.8' into dev
v5.6.8 stable
2024-10-13 19:19:00 +07:00
Gravita
d26b179006
Merge branch 'release/5.6.8' 2024-10-13 19:18:50 +07:00
Gravita
c4d1251429
[ANY] 5.6.8 stable 2024-10-13 19:05:35 +07:00
Gravita
f16f5fbc6d
[FIX] NPE: User not found and permissions enabled 2024-10-06 22:59:49 +07:00
Gravita
abe904d73c
[FEATURE] ExtendedCheckServer support in SQLAuthCoreProvider 2024-10-02 20:50:36 +07:00
Gravita
eaf685897f
Merge tag 'v5.6.7' into dev
5.6.7 stable release
2024-09-28 20:31:33 +07:00
Gravita
c8934d887a
Merge branch 'release/5.6.7' 2024-09-28 20:31:23 +07:00
Gravita
070a5d9b69
[ANY] 5.6.7 release 2024-09-28 20:31:10 +07:00
Gravita
cc2bce4300
[FIX] initializeAtStart thread safety 2024-09-28 02:29:19 +07:00
Gravita
c7f4d8ac49
[FEATURE] Support initializeAtStart 2024-09-28 02:27:42 +07:00
Gravita
7dcb08fdaf
[FEATURE] Support HWID in SQLCoreProvider 2024-09-28 01:47:55 +07:00
Gravita
9d870849a1
[ANY] Update dependencies 2024-09-18 16:36:15 +07:00
microwin7
dda3ebc7b4 [FIX] RejectedExecutionException after cancelled Download task 2024-08-23 22:02:13 +03:00
Gravita
537623afaf
Merge tag 'v5.6.6' into dev
5.6.6 stable
2024-08-18 23:57:14 +07:00
Gravita
0cff6e247a
Merge branch 'release/5.6.6' 2024-08-18 23:57:08 +07:00
Gravita
b85075c559
[ANY] 5.6.6 Stable 2024-08-18 23:57:00 +07:00
Gravita
3e654f4d79
[FEATURE] Add support opens, exports, reads to SYSTEM_CLASS_LOADER. Remove AGENT 2024-08-18 23:10:58 +07:00
Gravita
46f1f7b69e
[FIX] Symlink in launcher-pack 2024-08-18 19:18:53 +07:00
microwin7
981f2ac3dd [ANY] Update modules and update version oshi 2024-08-18 01:00:08 +03:00
microwin7
8509cbb6b5 [ANY] Update modules 2024-08-14 00:13:45 +03:00
microwin7
1119625d12 [ANY] Update modules 2024-08-13 05:44:32 +03:00
microwin7
9a69426547 [ANY] Update modules 2024-08-12 07:03:17 +03:00
microwin7
31cbfe2919 [ANY][FEATURE] Update modules. Removed mainClass for Cleanroom
In MakeProfileHelper
2024-08-12 06:01:08 +03:00
microwin7
27ebadcd19 [ANY] Update modules 2024-08-12 02:00:49 +03:00
Antoni
b3349044b5
[ANY] Update patch Version and Type release 2024-08-07 01:27:18 +03:00
Antoni
c43edeb982
[FIX] UpdatesProvider register 2024-08-06 03:25:02 +03:00
Gravita
1ff58099bd
[FIX] UpdatesProvider register 2024-08-06 06:19:12 +07:00
Gravita
9fba637f83
[FIX] Launcher update token 2024-08-05 05:36:53 +07:00
Gravita
ca70ee78d1
[FIX] Launcher update 2024-08-05 05:34:45 +07:00
Gravita
5e116a81e5
[ANY] Update modules 2024-08-05 03:25:05 +07:00
Gravita
b0f799d194
[FIX] UpdatesProvider init 2024-08-05 03:24:42 +07:00
Gravita
63f9f8e21d
[FEATURE] UpdatesProvider Part 2 2024-07-31 00:36:04 +07:00
Gravita
b8ccbc5e48
[FEATURE] UpdatesProvider Part 1 2024-07-31 00:15:08 +07:00
Gravita
a687c5afd8
[ANY] Update gradle 2024-07-30 23:20:35 +07:00
Gravita
3969d81db7
[ANY] Update modules 2024-07-30 23:06:54 +07:00
Gravita
a30d0624a1
[ANY] Update modules 2024-07-30 21:05:37 +07:00
Gravita
d5abe0d411
[ANY] Update modules 2024-07-30 17:34:20 +07:00
Gravita
1e7a856a99
[FIX] Profile duplicate 2024-07-30 15:43:22 +07:00
Gravita
e6f5b585a7
[ANY] Update modules 2024-07-30 15:33:46 +07:00
Gravita
2ed4abf9b0
[FIX] Usage profilesProvider 2024-07-26 01:27:01 +07:00
Gravita
af2dcec8cd
[FIX] ProfileProvider support overwrite profile 2024-07-25 22:28:26 +07:00
Gravita
9bffe07d36
[FEATURE] ProfileProvider 2024-07-25 22:20:48 +07:00
Gravita
4be299f6ca
[FIX] Add ClassLoader.registerAsParallelCapable() 2024-07-25 16:43:00 +07:00
Gravita
ef4f14f9b4
[FIX] Add netty epoll lib for aarch64 (ARM) 2024-07-25 16:33:49 +07:00
Gravita
d720328bc4
[FEATURE] moduleConf in SYSTEM_ARGS 2024-07-25 15:52:39 +07:00
Gravita
88f1eaf750
[ANY] Update modules 2024-07-21 20:39:30 +07:00
Gravita
a5ef86b105
Merge tag 'v5.6.5' into dev
5.6.5-stable
2024-07-21 20:18:08 +07:00
Gravita
b1a5ecdc13
Merge branch 'release/5.6.5' 2024-07-21 20:18:00 +07:00
Gravita
68e9affbe0
[ANY] 5.6.5-stable 2024-07-21 20:17:51 +07:00
Gravita
7d7485afdc
[FIX] Downloader downloadList truncate file 2024-07-21 19:48:55 +07:00
Gravita
c2926b5b40
[ANY] Update modules 2024-07-20 19:20:12 +07:00
Gravita
9c82d76781
[FEATURE][REFACTOR] Refactoring GenerateCertificateModule 2024-07-20 19:19:41 +07:00
Gravita
450774de7e
Merge tag 'v5.6.4' into dev
5.6.4-stable hotfix
2024-07-18 20:35:57 +07:00
Gravita
f88c0308f8
Merge branch 'release/5.6.4' 2024-07-18 20:35:46 +07:00
Gravita
20f713be05
5.6.4-stable 2024-07-18 20:35:26 +07:00
Gravita
5bf92d9a00
[FIX] ProGuard librariesDir 2024-07-18 20:34:31 +07:00
Gravita
903c4d40c5
Merge tag 'v5.6.3' into dev
5.6.3-stable
2024-07-18 15:55:07 +07:00
Gravita
ede9ab2c85
Merge branch 'release/5.6.3' 2024-07-18 15:54:58 +07:00
Gravita
c8c83c0dba
[ANY] 5.6.3-stable 2024-07-18 15:54:45 +07:00
Gravita
1bd5d8854c
[FIX] ProGuard and launchserver.dir.libraries 2024-07-17 01:32:51 +07:00
Gravita
992d31c883
[FEATURE] Set netty bind port in first run 2024-07-16 22:05:13 +07:00
Gravita
accbbe6b13
[FEATURE] Support port in launchserver address in first run 2024-07-16 21:54:59 +07:00
Gravita
0894e0b9c3
[FIX] Sync profiles before updates 2024-07-16 17:11:12 +07:00
Gravita
925007015f
[ANY] Update modules 2024-07-09 20:57:52 +07:00
Gravita
ec526a343d
[FIX] OptionalView.fixDependencies throw out of range 2024-07-08 21:29:40 +07:00
Gravita
1bebd8de2c
[FIX] classLoaderConfig is null 2024-07-08 21:22:44 +07:00
Gravita
c7781b30be
[FIX] launcher-libraries-compile not found 2024-07-06 22:34:20 +07:00
Gravita
df9d05a49c
[FEATURE] Support -Dlaunchserver.dir.modules and -Dlaunchserver.dir.launcher-modules 2024-07-06 03:24:02 +07:00
Gravita
03d53d4a09
[FEATURE] Support -Dlaunchserver.dir.libraries 2024-07-06 02:59:36 +07:00
Gravita
c261496af8
[ANY] Update modules 2024-07-06 01:55:10 +07:00
Gravita
8c11ab0cbe
[FIX] Disable Sodium lwjgl check 2024-07-05 16:27:50 +07:00
Gravita
78b4f1e3aa
[FIX] Gson version 2024-07-05 15:55:54 +07:00
Gravita
bbc5f1722b
[ANY] Update modules 2024-07-05 15:30:03 +07:00
Gravita
51411c5838
[ANY] Update modules 2024-07-05 14:55:21 +07:00
Gravita
3b22b76278
[FIX] Cleanroom install 2024-07-05 14:55:10 +07:00
Gravita
ef9cbfe0da
[ANY] Update modules 2024-07-05 14:38:57 +07:00
Gravita
353b663e12
[FEATURE] Support command execute by args 2024-07-05 14:38:45 +07:00
Gravita
78be606029
[FIX] FabricLoader main class 2024-07-05 14:27:45 +07:00
Gravita
584acdb8c3
[ANY] Update dependencies 2024-07-05 14:15:22 +07:00
Gravita
7f6a645dd7 [FIX] Compile fix 2024-06-01 23:16:34 +07:00
luckybl0ck
c6930ded74
[FIX] Set Java distrubution in Actions workflow (#714) 2024-06-01 23:11:50 +07:00
Gravita
c2a6a408c4 [FIX] Hikari getConnection() deadlock when with using user_permissions table 2024-06-01 23:10:56 +07:00
luckybl0ck
01cd50840a
[ANY] Update actions versions (#713) 2024-06-01 22:21:35 +07:00
Gravita
bb4d5b99c6 [ANY] 5.6.3-dev 2024-06-01 00:13:00 +07:00
Gravita
ff2f647b50 Merge tag 'v5.6.2' into dev
5.6.2 stable
2024-05-31 23:53:32 +07:00
Gravita
79fc42e86a Merge branch 'release/5.6.2' 2024-05-31 23:53:22 +07:00
Gravita
5b8aa8cd5e [ANY] 5.6.2-stable 2024-05-31 23:45:01 +07:00
Gravita
a4e5ef9d01 [FIX] Bouncy Castle 2024-05-31 23:30:19 +07:00
Gravita
1d5044c24a [FIX] Bouncy Castle cert gen 2024-05-31 22:58:18 +07:00
Gravita
b84911d445 [ANY] Update modules 2024-05-30 22:46:28 +07:00
Gravita
6a173b9b1b [ANY] Update modules 2024-05-30 22:35:27 +07:00
Gravita
6a53891c6a [ANY] Update dependencies 2024-05-30 22:28:34 +07:00
XakepSDK
7c637e078d
[FIX] Fix OpenID access token validation (#712)
Co-authored-by: d3coder <admin@xakeps.dk>
2024-05-24 00:42:32 +07:00
Gravita
c9a81da60c [FIX] Profile generation 2024-04-26 02:01:50 +07:00
Gravita
6c0ead015b [FIX] NPE in new profiles 2024-04-26 01:49:09 +07:00
XakepSDK
b5457ee866
[FIX OpenID validate issuer and aud (#710)
Co-authored-by: d3coder <admin@xakeps.dk>
2024-04-25 22:05:53 +07:00
Gravita
52c9196dcc [FIX] SQLCoreProvider 2024-04-25 03:05:13 +07:00
Gravita
095a5aef8b [FEATURE] Add mariadb connector 2024-04-25 02:09:17 +07:00
Gravita
765f1a9d8f [FEATURE] SQLCoreProvider 2024-04-25 02:06:44 +07:00
Gravita
9bd65c797b [REFACTOR] ClientProfileBuilder 2024-04-25 01:31:46 +07:00
XakepSDK
8908710ad6
[ANY] Add OpenID auth module (#709)
Co-authored-by: d3coder <admin@xakeps.dk>
2024-04-24 20:21:16 +07:00
Gravita
748612783c Merge tag 'vv5.6.1' into dev
5.6.1 stable
2024-04-21 13:42:04 +07:00
Gravita
42cf9bc79e Merge branch 'release/v5.6.1' 2024-04-21 13:41:55 +07:00
Gravita
8a81989d65 [ANY] 5.6.1 stable 2024-04-21 13:41:41 +07:00
Gravita
58fd3a7e8b [FIX] Add 'Eclipse Adoptium' to java search directory 2024-04-14 21:06:51 +07:00
Gravita
cc6ed82afb [FIX] Forge 1.7.10 lwjgl3ify classpath order 2024-04-14 20:53:09 +07:00
Gravita
00ab20473c [FIX] Strict profile access, update gradle 2024-04-10 16:49:05 +07:00
Gravita
cdb54b34de [ANY] Update modules 2024-04-03 22:13:03 +07:00
XakepSDK
1ebe68f5b8
[ANY] Add query helper (#708)
* [ANY] Add query helper

* [ANY] Rename AuthCodePassword field to the uri

---------

Co-authored-by: d3coder <admin@xakeps.dk>
2024-04-02 00:09:38 +07:00
Gravita
b719255bd5 [ANY] 5.6.1 dev 2024-03-27 23:37:55 +07:00
Gravita
6ecf716fca [FIX] ProGuard 2024-03-27 23:37:08 +07:00
Gravita
873100cf0a Merge tag 'v5.6.0' into dev
5.6.0 stable
2024-03-24 22:13:25 +07:00
Gravita
a8b165f081 Merge branch 'release/5.6.0' 2024-03-24 22:13:15 +07:00
Gravita
06ada30459 [ANY] 5.6.0 stable 2024-03-24 22:10:25 +07:00
Gravita
6017a89e20 [ANY] 5.6.0 beta 2024-03-24 16:11:32 +07:00
Gravita
bd677c26ba [FIX] Oshi requires slf4j api 2024-03-23 14:09:47 +07:00
Gravita
e5840243b3 [ANY] Update gradle 2024-03-22 13:40:46 +07:00
Gravita
09d36e066a [ANY] Update modules 2024-03-21 19:41:00 +07:00
Gravita
038af764a1 [REFACTOR] IDEA Code Inspect 2024-03-21 19:40:45 +07:00
Gravita
f7decac23d [FIX] Jwt library remove deprecated 2024-03-20 12:40:24 +07:00
zznty
1710eb7bec
[FIX] Fix resolution of files with special characters for embedded file server (#705) 2024-03-16 21:50:47 +07:00
XakepSDK
3926f3e5bf
[ANY] Update JWT library (#706)
Co-authored-by: d3coder <admin@xakeps.dk>
2024-03-14 18:07:44 +07:00
microwin7
7759ea9182 Revert "Merge pull request #703 from zznty/dev"
This reverts commit 3e6af5afd3, reversing
changes made to 730efae7c7.
2024-03-12 19:33:17 +03:00
zznty
ac6c312ed4
[FIX] Fix log4j config unpacking (#704) 2024-03-12 21:46:17 +07:00
microwin7
3e6af5afd3
Merge pull request #703 from zznty/dev
[FIX] Fix resolution of files with special characters for embedded file server
2024-03-12 17:06:25 +03:00
zznty
044813cca2 [FIX] Fix resolution of files with special characters for embedded file server 2024-03-12 20:56:23 +07:00
Gravita
730efae7c7 [ANY] Update modules 2024-03-12 16:08:35 +07:00
Gravita
bc6da641d6 [FEATURE] Support autobuild lwjgl3ify 2024-03-12 16:08:30 +07:00
Gravit'a
5c7f7eedec [FIX] Possible problem with lwjgl 2024-03-10 15:49:01 +07:00
Gravit'a
e72e4ebb92 [ANY] Update modules 2024-03-10 14:54:38 +07:00
Gravit'a
d811a04cba [FIX] DeleteProfileCommand 2024-03-10 14:53:52 +07:00
Gravit'a
572052163b [ANY] Update modules 2024-03-10 14:47:53 +07:00
Gravit'a
666c8a4b3e [ANY] Update modules 2024-03-10 14:03:25 +07:00
microwin7
ef5f932afb [FIX] Sort files in installAuthlib 2024-03-09 14:28:24 +03:00
microwin7
3002371fad [FEATURE] Sort jar files, before patching in installAuthlib 2024-03-09 00:40:28 +03:00
Gravita
998db80837 [ANY] Update modules 2024-03-08 14:46:55 +07:00
Gravita
494b3227b6 [FIX] Remove deprecated 'new URL' 2024-03-08 14:46:43 +07:00
Gravita
d686d9a388 [FEATURE] Add newHttpClientBuilder() 2024-03-08 14:37:15 +07:00
Gravita
e28c9773fc [FIX] file.encoding utf-8 by default 2024-03-05 19:07:02 +07:00
Gravita
7ff062f9e4 [ANY] Update modules 2024-03-05 16:48:35 +07:00
Gravita
29619bb7a4 [FIX] WebSocket dev log 2024-03-03 15:29:58 +07:00
Gravita
3179ee00eb [ANY] Update modules 2024-03-03 13:39:49 +07:00
Gravita
f484f045ca [FEATURE] FxRuntimeOptimizerModule 2024-03-03 13:39:28 +07:00
Gravita
ead4689bcf [FIX] checkServer token 2024-03-01 14:18:09 +07:00
Gravit
7a96e67517
Update push.yml 2024-03-01 14:11:24 +07:00
Gravita
b21082e201 [FIX] Netty in ServerWrapper 2024-02-27 16:50:58 +07:00
Gravita
8cd43b0324 [FEATURE] clearLauncherPackages, LaunchServerControlHolder 2024-02-27 16:33:43 +07:00
Gravita
3e8c1adebe [FIX] GetConnectUUIDCommand 2024-02-27 16:20:55 +07:00
Gravita
666644c9e0 [FEATURE] GetConnectUUIDCommand, shardId 2024-02-27 16:12:50 +07:00
Gravita
3152758d31 [FEATURE] GetConnectUUIDRequest 2024-02-27 16:04:17 +07:00
Gravita
80176ff1e1 [FEATURE] sync updatescache command 2024-02-27 15:53:27 +07:00
Gravita
11382d3465 [FEATURE] Profile delete and profile list commands 2024-02-27 15:34:20 +07:00
microwin7
31285a8066 [ANY] Update modules 2024-02-24 22:15:04 +03:00
Gravita
3ec79e3e93 [ANY] Update modules 2024-02-23 14:42:17 +07:00
Gravita
a4bf033aa8 [FEATURE] Support sudo in mysql/psotgresql/filesystem AuthCoreProvider 2024-02-23 14:42:00 +07:00
Gravita
4e50cea93a [FEATURE] Use encrypted ClientParams 2024-02-22 17:33:44 +07:00
Gravita
d40dc09aca [FEATURE] Handle non 2XX codes in Downloader 2024-02-15 01:31:27 +07:00
Gravita
0b59d6c0ed [FEATURE] Profile clone command 2024-02-10 14:47:13 +07:00
Gravita
80fc2900c8 [FEATURE] Public-Only server token 2024-02-07 14:27:04 +07:00
Gravita
4f47398211 [ANY] Update modules 2024-02-07 13:47:31 +07:00
Gravita
9676e55bcb [FEATURE] Support sudo 2024-02-07 13:47:18 +07:00
Gravita
aa7b007616 [FIX] Launcher log 2024-02-03 17:35:27 +07:00
Gravita
35bdf1607f [FEATURE] Add thread-safe info to responses 2024-02-03 17:11:36 +07:00
Gravita
7060697bad [FEATURE] Support virtual threads and client locks 2024-02-03 17:08:07 +07:00
Gravita
34ac6a0f28 Merge branch 'experimental/nojava8support' into dev 2024-01-31 15:42:19 +07:00
Gravita
5370130c2d Merge tag 'v5.5.4' into dev
5.5.4 stable
2024-01-31 14:36:57 +07:00
Gravita
db6ab061de Merge branch 'release/5.5.4' 2024-01-31 14:36:49 +07:00
Gravita
aeb55470ce [ANY] 5.5.4 stable 2024-01-31 14:36:41 +07:00
Gravita
a0788e4623 [FIX] Remove legacy launch4j in build.gradle 2024-01-23 19:44:19 +07:00
Gravita
0c754ae5e6 [FIX] Remove legacy manifest attributes 2024-01-23 19:35:54 +07:00
Gravita
c9ccf36252 [ANY] Remove reg limiter 2024-01-23 19:34:53 +07:00
Gravita
b7b7afbdbb [ANY] Update modules 2024-01-23 19:11:48 +07:00
Gravita
2fdd7d0199 [FIX] Slf4j fix 2024-01-23 19:11:33 +07:00
Gravit
1e3676778e
Merge pull request #696 from microwin7/dev
[FIX][FEATURE] Merge commits from nojava8support ref. Proguard, with jvmArgs
2024-01-18 02:57:44 +07:00
Gravita
ab884c8d23 [FEATURE] Add ProGuard jvmArgs 2024-01-17 20:24:43 +03:00
dima_dencep
48946d6e74 [FIX] proguard (#694) 2024-01-17 20:24:32 +03:00
microwin7
f075f39954
Merge branch 'GravitLauncher:dev' into dev 2024-01-17 20:22:02 +03:00
Gravita
f1922c52e2 [FEATURE] Add ProGuard jvmArgs 2024-01-18 00:13:36 +07:00
Gravita
f42e6de0b0 [ANY] Update dependencies 2024-01-17 23:51:56 +07:00
Gravita
efe967587c [FIX] Finally remove launch4j 2024-01-14 17:25:55 +07:00
Gravita
f2b92c2bbd [FIX] Update mirror link 2024-01-14 17:21:58 +07:00
Gravit'a
4251725467 [ANY] Update modules 2024-01-13 18:42:42 +07:00
dima_dencep
82bf2fdf56
[FIX] proguard (#694) 2024-01-12 16:02:02 +07:00
Gravita
f1bc0ea28a [ANY] Update modules 2024-01-09 16:59:15 +07:00
microwin7
8762aa470c
[FIX] Backport support arch i586 in release file (#693)
Co-authored-by: Gravita <12893402+gravit0@users.noreply.github.com>
2024-01-08 15:19:17 +07:00
Gravita
cfcd0010a7 [FIX] Support arch i586 in release file 2024-01-07 16:36:53 +03:00
Gravita
b3eb0ebb98 [FIX] Support arch i586 in release file 2024-01-07 20:18:15 +07:00
Gravita
8e82f5cd84 [FEATURE][EXPERIMENTAL] Parse java args method 2024-01-07 20:15:29 +07:00
Gravita
90f6d002d1 [FIX] Wayland support 2024-01-03 13:17:59 +07:00
Gravita
449798d52b [FIX] ServerWrapper 2023-12-29 17:07:03 +07:00
Gravita
f321b8bd27 [FIX] ExitResponse, update SimpleModuleManager 2023-12-29 16:37:14 +07:00
Gravita
64635cbb9b [REFACTOR] Renaming 2023-12-23 17:58:09 +07:00
Gravita
4607ab88bf [ANY] Update modules 2023-12-23 12:25:55 +07:00
Gravita
e6516a8991 [REFACTOR] IDEA mini refactor 2023-12-23 12:25:11 +07:00
Gravita
dfbb6e507a [REFACTOR] Using Java 17 2023-12-23 12:18:10 +07:00
Gravita
0855fc589d [REFACTOR] Remove log4j support, new LaunchServer start 2023-12-23 12:06:54 +07:00
Gravita
c9b6b0279a [REFACTOR] Renaming 2023-12-23 12:05:23 +07:00
Gravita
f8b060422e [FEATURE] Improve LaunchServer start 2023-12-20 04:45:41 +07:00
Gravita
31489a2b24 [FIX] Improve Wayland support 2023-12-20 02:13:39 +07:00
Gravita
6c0500f528 [ANY] 5.5.4-dev 2023-12-14 01:39:21 +07:00
Gravita
5896a12449 [FEATURE] Support option loadNatives 2023-12-14 01:36:07 +07:00
Gravita
240e36aab6 [FIX] Start Forge 1.18+ server 2023-12-14 01:19:36 +07:00
Gravita
f6f6ea13ad [ANY] Experimental branch 2023-12-08 20:24:31 +07:00
Gravita
967b81cc85 Merge tag 'v5.5.3' into dev
5.5.3 stable
2023-12-08 18:25:18 +07:00
Gravita
6dadea1b67 Merge branch 'release/5.5.3' 2023-12-08 18:25:10 +07:00
Gravita
a601a4ceef [ANY] Update modules 2023-12-08 17:53:51 +07:00
Gravita
1c90681b3b [FEATURE] Support Minecraft 1.20.3/1.20.4 2023-12-08 17:53:38 +07:00
Gravita
7b1f449667 [ANY] Update modules 2023-12-08 16:51:26 +07:00
Gravita
ae24fd6ccb [ANY] 5.5.3-stable 2023-12-08 16:51:10 +07:00
Gravita
2ff1d81076 [FEATURE] Support run Minecraft with Wayland 2023-12-01 20:26:06 +07:00
Gravita
ee0a7bc25a [FIX] Support mysql/postgresql 1.20.2 2023-12-01 17:59:05 +07:00
Gravita
ebbd1c87e8 [FIX] ModuleLaunch NPE 2023-11-30 00:05:59 +07:00
Gravita
3754a327b0 [FEATURE] ConfigService 2023-11-28 18:21:32 +07:00
Gravita
d2f34ced28 [FEATURE][EXPERIMENTAL] Add ClientServer and AuthService to ServerWrapper, extended check server, remove deprecated interfaces 2023-11-27 22:41:49 +07:00
Gravita
43626bf1f4 [FEATURE] Support createHackLookupNative 2023-11-26 20:51:58 +07:00
Gravita
380179faa3 [FIX] SystemdNotifyModule 2023-11-26 15:08:05 +07:00
Gravita
2e60d45c63 [FEATURE][EXPERIMENTAL] Support add javafx in LauncherEngineWrapper 2023-11-26 15:07:50 +07:00
Gravita
d678daac7b [FIX] Systemd notify logger 2023-11-25 00:28:00 +07:00
Gravita
1bc9351b0c [FIX] Compile fix 2023-11-21 13:58:10 +07:00
Gravita
54bfc6de9c [FIX] No refresh in client 2023-11-21 13:47:45 +07:00
Gravita
fe374c1f9e [ANY] Update modules 2023-11-21 13:17:48 +07:00
Gravita
3f4bdceb5a Merge tag 'v5.5.2' into dev
5.5.2 stable
2023-11-20 17:15:06 +07:00
Gravita
3d61635c6b Merge branch 'release/5.5.2' 2023-11-20 17:14:57 +07:00
Gravita
0c2779f1c0 [ANY] 5.5.2 stable 2023-11-20 16:47:23 +07:00
Gravita
8f598a40c5 Revert "[FIX] Force exit JVM"
This reverts commit 4720e4d106.
2023-11-19 14:19:56 +07:00
Gravita
4720e4d106 [FIX] Force exit JVM 2023-11-19 14:04:29 +07:00
Gravita
0482cfa9ab [ANY] Update modules 2023-11-10 00:32:40 +07:00
Gravita
a2167d483a [ANY] Update modules 2023-11-09 14:49:25 +07:00
Gravita
84a3845f1a [ANY] 5.5.2-dev 2023-11-04 21:51:18 +07:00
Gravita
0d5d772141 [ANY] Update modules 2023-11-04 21:50:31 +07:00
Gravita
9df116f951 [FIX] MirrorHelper 2023-11-04 21:50:11 +07:00
Gravita
ff3d500cc0 Merge tag 'v5.5.1' into dev
5.5.1 stable
2023-11-01 18:10:09 +07:00
Gravita
2e10c78ad2 Merge branch 'release/5.5.1' 2023-11-01 18:10:02 +07:00
Gravita
b6be2e243d [ANY] 5.5.1 stable 2023-11-01 17:40:52 +07:00
Gravita
44bc8b0bbc [FEATURE] Support modular start 2023-11-01 04:05:14 +07:00
Gravita
6cd5a69149 [FEATURE] ModuleLaunch bug fix 2023-11-01 00:10:19 +07:00
Gravita
b638efc0d1 [FEATURE] Support compat classes in ServerWrapper 2023-10-31 22:57:37 +07:00
Gravita
f2cbf0ed38 [ANY] Update modules 2023-10-31 22:52:21 +07:00
Gravita
0f36dfec16 [FEATURE] Support compat classes in client debug 2023-10-31 22:27:55 +07:00
Gravita
c1df548258 [FEATURE] Unified launch 2023-10-31 22:18:10 +07:00
Gravita
429c7a45c4 [ANY] Update modules 2023-10-31 16:59:23 +07:00
Gravita
90e116720c [FIX] onHardwareReport NPE 2023-10-31 16:22:44 +07:00
Gravita
4b222b9526 Merge tag 'v5.5.0' into dev
5.5.0 stable
2023-10-31 01:53:01 +07:00
Gravita
1df283d659 Merge branch 'release/5.5.0' 2023-10-31 01:52:54 +07:00
Gravita
b45618c0da [ANY] 5.5.0 stable 2023-10-31 01:52:41 +07:00
Gravita
0241a4d887 [ANY] Update modules 2023-10-31 01:29:45 +07:00
Gravita
de5ebe4a74 [ANY] Update modules 2023-10-31 00:59:25 +07:00
Gravita
80862e6116 [FEATURE] NoProtectHandler - disable joinServer protection 2023-10-31 00:20:57 +07:00
Gravita
f22aa36926 [FIX] Support Debug client with memory type 2023-10-31 00:14:56 +07:00
Gravita
198ce95176 [FEATURE] Support Debug client with memory type 2023-10-31 00:14:38 +07:00
Gravita
fca2ed2447 [FEATURE] Support 1.20+ autoenter 2023-10-30 23:11:05 +07:00
Gravita
2792b5a008 [ANY] Update modules 2023-10-30 22:17:24 +07:00
Gravita
a0335bd340 [ANY] Update modules 2023-10-30 22:06:48 +07:00
Gravita
3c6b8322a5 [ANY] Modules update 2023-10-30 19:45:36 +07:00
Gravita
a83f225933 [FIX] File not exist 2023-10-30 19:29:12 +07:00
Gravita
99af83fb06 [ANY] Update modules 2023-10-30 15:57:41 +07:00
Gravita
c4672387ac [ANY] Update modules 2023-10-29 21:50:15 +07:00
Gravita
fe7ae41f65 [FEATURE] Remove deprecated http methods, fix download 2023-10-29 21:18:47 +07:00
Gravita
474d557e3f [FIX] JsonTextureProvider 2023-10-29 20:16:23 +07:00
Gravita
50e6bb3b49 [FIX] Default config 2023-10-29 19:02:04 +07:00
Gravita
e85a12afab [FIX] Change default refresh rate to 5 seconds 2023-10-29 03:34:00 +07:00
Gravita
9e83e8bec8 [FIX] Use custom thread factory in autorefresh 2023-10-29 03:30:13 +07:00
Gravita
fce8453bd1 [FIX] Enable autoRefresh in default 2023-10-29 03:27:43 +07:00
Gravita
224649aa13 [FIX] Enable autoRefresh in default 2023-10-29 03:24:27 +07:00
Gravita
a3bcfed793 [ANY] Update gradle 2023-10-28 19:52:39 +07:00
Gravita
e1429356df [FIX] Build without proguard 2023-10-28 18:49:07 +07:00
Gravita
75f51c7727 [FIX] Use log4j-core annotation processor 2023-10-26 17:39:15 +07:00
Gravita
90162a1a25 [ANY] Update modules 2023-10-25 22:41:17 +07:00
Gravita
62e9276481 [FEATURE] Update build pipeline, fix multi release jar with proguard 2023-10-25 22:41:06 +07:00
Gravita
79f933646c [ANY] Update modules 2023-10-23 23:51:09 +07:00
Gravita
01c963d852 [ANY] Update modules 2023-10-23 23:08:18 +07:00
Gravita
941bf115b1 [FIX] New mirror path 2023-10-22 18:38:15 +07:00
Gravita
2e6d7bd94a [FIX] Improve JsonTextureProvider 2023-10-22 18:07:50 +07:00
Gravita
d262b99be6 [ANY] Update modules 2023-10-22 15:29:25 +07:00
Gravita
b5e10e8f9d [FEATURE] JsonTexture convertMap 2023-10-22 15:27:54 +07:00
Gravita
cebeb55c00 [FIX] Change JsonTextureProvider behavior, delete http method 2023-10-21 20:31:46 +07:00
Gravita
7e16f36ea0 [FIX] DebugMain exit 2023-10-21 19:16:01 +07:00
Gravita
8973a3462b [FEATURE] Support category in optional mods 2023-10-21 16:16:28 +07:00
Gravita
82dc299752 [FIX] Remove javafx.swing module 2023-10-18 18:10:16 +07:00
Gravita
19f966062b [ANY] Update modules 2023-10-10 16:14:28 +07:00
Gravita
93f916192e [FEATURE] MirrorHelper support build script 2023-10-10 16:14:10 +07:00
Gravita
dbeca56b05 [FEATURE] Support custom buttons 2023-10-06 12:55:07 +07:00
Gravita
d7474255da [ANY] Update modules 2023-10-03 19:16:23 +07:00
Gravita
b72fb643d4 [FEATURE] Support 1.20.2 2023-10-03 19:16:02 +07:00
Gravita
52e8f693b5 [FIX] AssetUploadInfo 2023-09-28 12:36:47 +07:00
Gravita
1f3b7e0552 [FEATURE] UploadAssetMixProvider 2023-09-26 14:08:44 +07:00
Gravita
183cfe949f [FEATURE] AssetUploadInfo 2023-09-26 13:55:01 +07:00
Gravita
aff254a875 [FEATURE] Support auth features in API 2023-09-24 00:13:32 +07:00
Gravita
e12f2ef897 [FEATURE] Support properties in ServerWrapper 2023-09-21 22:08:03 +07:00
Gravita
58d8ba2358 [FIX] Reset null config 2023-09-20 19:52:46 +07:00
Gravita
1cc3edd15b [ANY] Update modules 2023-09-20 19:33:15 +07:00
Gravita
606df6cb1a [ANY] IDEA code inspect 2023-09-20 19:32:49 +07:00
Gravita
98a314f697 [ANY] Update modules 2023-09-20 19:22:24 +07:00
Gravita
d03c6120ff [FIX] Sentry module Java 8 2023-09-20 19:22:04 +07:00
Gravita
a34267e902 [FEATURE] Use Java 21 2023-09-20 19:11:33 +07:00
Gravita
0ccef10a93 [FEATURE] Print error for unsupported Java 2023-09-19 16:44:09 +07:00
Gravita
29aee9dd30 [ANY] 5.5.0 dev 2023-09-18 16:26:47 +07:00
Gravita
1d563249d1 [FEATURE] Support build-in javafx in Java 9+ 2023-09-18 16:12:58 +07:00
Gravita
d0d2860317 [ANY] Gradle update 2023-09-18 16:12:58 +07:00
Gravita
faa5189795 [FEATURE][EXPERIMENTAL] Support debug args 2023-09-18 16:12:58 +07:00
Gravita
d9082f21a3 [ANY] Delete old interfaces 2023-09-18 16:12:58 +07:00
Gravita
3475f2f912 [FIX] Compile fix 2023-09-18 16:12:58 +07:00
Gravita
b635447eaf [FEATURE] Support Asset Upload 2023-09-18 16:12:58 +07:00
Gravita
0fb6102c29 [FEATURE] MixProvider init 2023-09-18 16:12:58 +07:00
Gravita
d4cc28f96a [FEATURE] MixProvider 2023-09-18 16:12:58 +07:00
Gravita
49a5215783 [ANY] Update modules 2023-09-18 16:12:58 +07:00
Gravita
f12d13ef58 [FIX] NPE 2023-09-18 16:12:58 +07:00
Gravita
55c77dd343 [FIX] Auto refresh 2023-09-18 16:12:58 +07:00
Gravita
caebd6b5de [FEATURE] Extended Token Expire, auto refresh 2023-09-18 16:12:58 +07:00
Gravita
647c8dba5c [FIX] Client debug 2023-09-18 16:12:58 +07:00
Gravita
6ffbc5515f [FEATURE] Client debug 2023-09-18 16:12:58 +07:00
Gravita
65c6520001 [ANY] Update modules 2023-09-18 16:12:58 +07:00
Gravita
ac9a78cea4 [ANY] Change no runtime module error message 2023-09-18 16:12:58 +07:00
Gravita
00a4a13536 [ANY] Update modules 2023-09-18 16:12:58 +07:00
Gravita
6e45a84c1c [FEATURE] Support lmodule with Java 17 2023-09-18 16:12:58 +07:00
Gravita
b31dd78b2b [ANY[ Update modules 2023-09-18 16:12:58 +07:00
Gravita
9de81095b1 [FEATURE][EXPERIMENTAL] separate branch 2023-09-18 16:12:58 +07:00
microwin7
4d1fd23e84
[FIX] Added Multi Release for LauncherCore (#672) 2023-07-30 14:32:13 +07:00
Gravita
41f00c2310 [FIX] Check certificate expired 2023-07-25 17:19:21 +07:00
Gravita
85986c2916 [FEATURE] Check certificate expired 2023-07-25 17:10:18 +07:00
Gravita
1bc0443dd5 Merge tag 'v5.4.4' into dev
5.4.4 stable
2023-07-25 16:35:06 +07:00
Gravita
50de0b1e44 Merge branch 'release/5.4.4' 2023-07-25 16:34:56 +07:00
Gravita
39d5eee51c [ANY] 5.4.4 stable 2023-07-25 16:34:38 +07:00
Gravita
8153c3a438 [FIX] componentName in config 2023-07-21 21:18:12 +07:00
Gravita
9d81db25d8 [ANY] Update modules 2023-07-21 21:17:37 +07:00
Sevastjan
d4ca612bff
[FEATURE] Фикс зависимостей и возможность взаимоисключения для опциональных модов (#663)
* [FEATURE] Mutually exclusive optional mods (XOR logic)

* [FIX] Bug with missing forgotten dependency/conflict

* [FIX] ArrayIndexOutOfBoundsException if xorConfict is empty

* [STYLE] Rename "xorConflict" to "group"
2023-07-21 21:11:48 +07:00
microwin7
680244e5d1
[FIX] Should no longer go into debugging ServerPinger (#669) 2023-07-21 21:09:05 +07:00
microwin7
2f7b94365a
[FIX] Proguard mapping letter L (#670) 2023-07-21 21:08:45 +07:00
Gravita
60f742b3ef Merge tag 'v5.4.3' into dev
5.4.3 stable
2023-07-16 14:33:42 +07:00
Gravita
7fee478552 Merge branch 'release/5.4.3' 2023-07-16 14:33:33 +07:00
Gravita
7efe7c8611 [ANY] Update modules 2023-07-16 14:32:34 +07:00
Gravita
d4abf27989 [ANY] 5.4.3 stable 2023-07-16 14:12:09 +07:00
Gravita
e887035920 [FIX] Remove experimental warning 2023-07-16 13:59:45 +07:00
Gravita
890591d2d2 [ANY] Update modules 2023-07-16 13:52:22 +07:00
Gravita
82938fe8d4 [FIX] Remove old style assets folder 2023-07-16 13:36:45 +07:00
Gravita
cebe47939a [ANY] Update asm 2023-07-16 13:19:23 +07:00
Gravita
fd24ca0ca7 [ANY] Update depencencides 2023-07-16 13:18:00 +07:00
Gravita
663685934b [ANY] Update gradle wrapper 2023-07-16 13:15:06 +07:00
Gravita
43d944bee1 [ANY] Update modules 2023-07-16 13:13:54 +07:00
microwin7
3a82065889
[FEATURE] JVMHelper class extension (#667) 2023-07-16 13:12:41 +07:00
microwin7
55c0cdfa0d
[FEATURE] Replaced with use TimeUnit (#666) 2023-07-16 13:11:58 +07:00
microwin7
b12c43676b
[FEATURE] Replaced String.format to formatted (#665) 2023-07-16 13:11:45 +07:00
Gravita
9d49eebffe [ANY] Update modules 2023-07-15 14:38:31 +07:00
microwin7
2aa8dffcaa
[REFACTOR] Replaced to pattern matching instanceof (#664) 2023-07-15 14:37:26 +07:00
Gravita
216928f258 [ANY] Update modules 2023-07-12 04:03:17 +07:00
Gravita
ec3775286c [ANY] Update modules 2023-07-03 22:32:18 +07:00
microwin7
7950eea975
Updating .gitattributes to common standards (#648) 2023-06-30 20:23:23 +07:00
microwin7
04dd7d655c
[ANY] Case ignoring for commands in ServerWrapper (#657) 2023-06-30 20:22:22 +07:00
Gravita
bd83e8a4c5 [FEATURE] Support hikariMaxLifetime 2023-06-30 20:21:20 +07:00
Gravita
d4f63a4e19 [ANY] Update modules 2023-06-28 19:21:09 +07:00
Gravita
c458283efb [ANY] Update modules 2023-06-19 01:50:09 +07:00
Gravita
bea7898939 [FEATURE] Change hash type to sha1 2023-06-09 18:25:08 +07:00
Gravit'a
bddf31c94e [FEATURE] LaunchServerLauncherExeInit 2023-05-25 15:33:01 +07:00
Gravita
24d625fd16 Merge tag 'v5.4.2' into dev
5.4.2 stable
2023-05-20 15:54:51 +07:00
Gravita
0c23b59749 Merge branch 'release/5.4.2' 2023-05-20 15:54:32 +07:00
Gravita
9b2c98e10b [ANY] 5.4.2 stable 2023-05-20 13:48:29 +07:00
Gravita
4a538cde61 [ANY] Update modules 2023-05-15 13:22:28 +07:00
Gravita
d9be4bb577 [FEATURE] Support LauncherAuthlib4 2023-05-15 13:22:24 +07:00
microwin7
ae426b2fd0
[FIX] Incorrect send AuthException for User not found (#651) 2023-05-13 15:49:30 +07:00
Gravita
498325f3e8 [ANY] Update modules 2023-05-08 00:42:00 +07:00
Gravita
907e7cc47e [ANY] Update modules 2023-05-07 00:10:23 +07:00
Gravita
d4eabbc4c0 [FIX] Remove digest check 2023-05-06 00:25:48 +07:00
Gravita
540ad0b0da Merge tag 'v5.4.1' into dev
5.4.1 stable
2023-04-28 21:23:09 +07:00
Gravita
3b8c01835d Merge branch 'release/5.4.1' 2023-04-28 21:22:54 +07:00
Gravita
00baf4adf0 [ANY] 5.4.1 stable 2023-04-28 21:22:31 +07:00
Gravita
e338bb9a02 [FIX] Autoupdate mirror link 2023-04-28 21:19:44 +07:00
Gravita
373fc8a255 [ANY] 5.4.1 dev 2023-04-28 20:44:47 +07:00
Gravita
73d8a037d5 Merge tag 'v5.4.0' into dev
5.4.0 stable
2023-04-28 20:42:21 +07:00
Gravita
aa47fd6f1b Merge branch 'release/5.4.0' 2023-04-28 20:42:07 +07:00
Gravita
dec86c9a91 [ANY] 5.4.0 stable 2023-04-28 20:41:48 +07:00
Gravita
74af58bc7a [ANY] Remove deprecated flag 2023-04-28 01:02:40 +07:00
Gravita
d97b856ad6 [FIX] Update mysql connector 2023-04-28 00:08:31 +07:00
Gravita
5436b2a2d6 [FIX] Double read textures 2023-04-27 21:12:21 +07:00
Gravita
5aa4fe8d47 [ANY] Update modules 2023-04-27 21:03:21 +07:00
Gravita
b10535042f [FIX] FileAuthSystem slim support 2023-04-27 21:02:30 +07:00
Gravit
9351f3ca1e
Merge pull request #647 from microwin7/patch-15
[FIX] DownloadAssetCommand.java command
2023-04-25 20:31:50 +07:00
microwin7
e05aa4b204
[FIX] DownloadAssetCommand.java command
ArrayIndexOutOfBoundsException
2023-04-25 16:28:14 +03:00
Gravita
bfa6966ec6 [ANY] Update mirror link 2023-04-22 20:17:02 +07:00
Gravita
50b463b439 [FEATURE] Support custom protocol 2023-04-21 16:14:33 +07:00
Gravita
6caa34e255 [FEATURE] MirrorHelper Quilt support 2023-04-21 15:34:42 +07:00
Gravita
26c017a277 [ANY] Update modules 2023-04-20 21:48:52 +07:00
Gravita
48799cf3c2 [FEATURE] New ClientProfile$Version 2023-04-20 19:06:12 +07:00
Gravita
3bc8040352 [FEATURE] MirrorHelper Modrinth support 2023-04-20 15:54:02 +07:00
Gravita
dcaec54814 [ANY] Update modules 2023-04-18 14:53:35 +07:00
Gravit'a
76f8b4602c [FIX] JAVA_HOME override 2023-04-15 17:40:34 +07:00
Gravit'a
fc7f96d536 [FIX] launcher-pack in Windows 2023-04-15 13:59:32 +07:00
Gravit'a
4ed687087f [FIX] LauncherGuard default config 2023-04-15 13:51:33 +07:00
Gravita
95da394a5d [FEATURE] LauncherGuard module 2023-04-11 13:28:31 +07:00
Gravita
06e9bc8578 [ANY] IDEA code inspect 2023-04-03 13:04:12 +07:00
Gravita
e0b3f3d6a5 [FEATURE] Launcher build number 2023-03-29 23:46:26 +07:00
Gravita
6a057514b2 [FIX] RequestTextureProvider localPath 2023-03-26 23:57:28 +07:00
Gravita
70d102222b [ANY] Upgrade gradle 2023-03-25 23:54:00 +07:00
Gravita
55d2fbd57f [ANY] Update modules 2023-03-23 20:28:03 +07:00
Gravita
111d7616d0 [FIX] NPE 2023-03-23 18:56:23 +07:00
Gravita
fb2883d215 [FIX] Possible multithreading issue 2023-03-22 21:46:40 +07:00
Gravita
dc664c7ee2 [FIX] Forbidden modification info 2023-03-22 21:18:08 +07:00
Gravita
eff739ce12 [ANY] Update modules 2023-03-20 00:25:55 +07:00
Gravita
17f9c28f3d [FEATURE] WebSocket hook rework 2023-03-20 00:25:27 +07:00
Gravita
71739f5670 [ANY] Update modules 2023-03-19 21:27:14 +07:00
Gravita
e4bf8f1e9c [ANY] Update modules 2023-03-19 21:22:37 +07:00
Gravita
a796b82a16 [FIX] JavaHelper NPE 2023-03-16 21:57:33 +07:00
Gravita
cf802fb0b3 [FEATURE] Support 1.19.4 2023-03-15 18:50:15 +07:00
Gravita
57868a7136 [ANY] Update modules 2023-03-07 15:23:49 +07:00
Gravita
8ac9866258 [FEATURE] SentryProguardUpload 2023-03-07 15:23:20 +07:00
Gravita
47392ceec7 Merge tag 'v5.3.6' into dev
5.3.6 stable
2023-03-07 14:22:28 +07:00
Gravita
c6f8793031 Merge branch 'release/5.3.6' 2023-03-07 14:21:15 +07:00
Gravita
0d74d8a671 Merge branch 'experimental/profiles-rework' into dev 2023-03-03 18:06:35 +07:00
Gravita
70012a2a8f [ANY] 5.3.6 release 2023-03-03 18:00:57 +07:00
Gravita
3821fa7e51 [ANY] Update modules 2023-03-03 16:59:50 +07:00
Gravita
930a5caf74 [ANY] Remove old guard/antiinject support 2023-02-26 19:01:43 +07:00
Gravita
ffad29f53b [FEATURE] Isolate Minecraft: Support add packages to ignore 2023-02-26 18:40:11 +07:00
Gravita
bd4e454be9 Merge branch 'experimental/5.3.0' into experimental/profiles-rework 2023-02-26 18:06:50 +07:00
Gravita
0818b3037c [FEATURE] ClientProfile compat flags, remove deprecated 2023-02-26 18:00:44 +07:00
Gravita
43ffacdf5e [FEATURE] Update Sentry module 2023-02-26 13:12:11 +07:00
Gravita
5c374462ef [FIX] Ping error handle 2023-02-25 19:54:05 +07:00
Gravita
ef5695f679 [ANY] Update modules 2023-02-25 18:43:34 +07:00
Gravita
b4331819cb [FEATURE] Update OptionalView 2023-02-25 18:42:26 +07:00
Gravit
ebf25a65f7
Merge pull request #637 from microwin7/patch-16
[FEATURE] Default value for client profile and assets dir
2023-02-22 16:44:57 +07:00
microwin7
5588b4aac1 [FEATURE] Default value for client profile and assets dir 2023-02-18 07:00:35 +03:00
Gravita
fbaf9ab87f [ANY] Update modules 2023-01-30 20:33:16 +07:00
Gravita
6d1440207b [FIX] Launch4j build exe 2023-01-25 18:02:24 +07:00
Gravita
9d719c48e3 [ANY] Update dependencies 2023-01-24 17:17:03 +07:00
Gravita
b0fba84fbb [FEATURE] MergeAuthCoreProvider 2023-01-20 19:57:14 +07:00
Gravita
1ffd36fc82 Merge tag 'v5.3.5' into dev
5.3.5 stable release
2023-01-15 14:50:37 +07:00
Gravita
9c359747ea Merge branch 'release/5.3.5' 2023-01-15 14:50:21 +07:00
Gravita
2c1972c12c [ANY] 5.3.5 stable 2023-01-15 14:49:20 +07:00
Gravita
d30f0b900b [ANY] Change domain 2023-01-11 18:16:51 +07:00
Tenebrius
f71444b8d6
[FIX] Check openjdk 8 on linux (#632)
* Correct check 8 openjdk on linux

* JRE rt lib in jre/lib/rt.jar

* [fix] remove dev log
2023-01-10 18:14:47 +07:00
Gravita
a0ac58f0b5 [FEATURE] printExperimentalBranch info 2022-12-22 19:11:11 +07:00
Gravita
a27d7f1597 [FEATURE] printExperimentalBranch 2022-12-22 19:07:46 +07:00
Gravita
7aa08c1846 Merge tag 'v5.3.4' into dev
5.3.4 hotfix
2022-12-12 15:15:51 +07:00
Gravita
2045f1ac99 Merge branch 'hotfix/5.3.4' 2022-12-12 15:15:39 +07:00
Gravita
292e7d2af7 [ANY] 5.3.4 hotfix 2022-12-12 15:15:22 +07:00
Gravita
5f7808afff [FIX] MySQLAuthCoreProvider 2022-12-12 15:14:12 +07:00
Gravita
3c10a668de Merge tag 'v5.3.3' into dev
5.3.3 stable
2022-12-09 21:47:43 +07:00
Gravita
ec222aed6f Merge branch 'release/5.3.3' 2022-12-09 21:47:32 +07:00
Gravita
7dcc5aef3f [FIX] More details in error when reading launcher module config file 2022-12-09 21:46:05 +07:00
Gravita
ae994ebb4f [FIX] More safe save config and 5.3.3 2022-12-09 21:34:23 +07:00
Gravita
553cdf5250 [FEATURE] Safe config write 2022-12-09 21:17:07 +07:00
Gravita
0d1b32fc1c [FIX] Save config with postgresql 2022-12-09 20:55:58 +07:00
Gravita
7f4fe566de [FIX] ClassCastException 2022-12-09 20:19:22 +07:00
Gravita
cc825df41e Merge tag 'v5.3.2' into dev
5.3.2 stable
2022-12-09 14:02:28 +07:00
Gravita
dd5ce00c3f Merge branch 'release/5.3.2' 2022-12-09 14:02:19 +07:00
Gravita
7c7952545e [ANY] 5.3.2 release 2022-12-09 14:02:13 +07:00
Gravita
cc8250d6cf [FEATURE] Support 1.19.3 2022-12-09 13:17:45 +07:00
Gravita
3c5e25b67f [FIX] Launcher permissions 2022-12-05 23:17:12 +07:00
Gravita
a0722fb5f4 [FIX] HttpAuthCoreProvider findmulti 2022-12-05 17:22:32 +07:00
Gravita
31b6ae35e7 [FIX] ServerWrapper NPE 2022-11-24 19:52:28 +07:00
Gravita
8fb1349487 [FIX] Roles support 2022-11-21 13:44:58 +07:00
Gravita
5631bc6af1 [FIX] permissionsPermissionColumn 2022-11-21 13:32:10 +07:00
AlexCatze
9cc1810831
[FEATURE] Roles support for MySQL and PostgreSQL (#622)
* [FEATURE] Roles support for MySQL and PostgreSQL

Roles are stored in separate (name,uuid) table. Role can be given to user by adding "role.<name/guid>" permission to user. Permissions can be added to role by adding record with role`s uuid to permissions table.

* [REFACTOR] Refactor roles quering

Use 2 queries, first to query names of all user`s roles, second to recursively query all permissions. This also allows roles inheritance. All code from previous realisation, that is not used in this one, was commented, so if this realisation will be accepted, that code can be finaly removed.

* [REFACTOR] Removed commented code

* [REFACTOR] Remover useless catches
2022-11-19 18:01:32 +07:00
Gravita
841d01b417 [ANY] Use launcher-pack directory for modules 2022-11-18 16:10:28 +07:00
Gravita
a7d3cba949 [FEATURE] Directory launcher-pack 2022-11-18 16:06:42 +07:00
Gravita
b2d2059ebd [ANY] Update modules 2022-11-18 14:47:18 +07:00
Gravita
10888ed2e7 [ANY] Full IDEA reformat 2022-11-18 14:47:03 +07:00
Gravita
82accb211d [ANY] IDEA reformat 2022-11-18 14:39:35 +07:00
Gravita
10df931c2a [FIX] MySQL/PostgreSQL permissions 2022-11-18 14:38:39 +07:00
Gravit
1996525b65
Merge pull request #621 from AlexCatze/feature-sqlabstraction
[FEATURE] Move shared SQL code to abstract class
2022-11-15 13:13:57 +07:00
AlexCatze
40d4681bce [FEATURE] Move shared SQL code to abstract class
Also ports permissions support to MySQL, because it doen`t have PostgreSQL-specific code.
2022-11-14 21:14:53 +02:00
AlexCatze
d11c9e92a5
[FEATURE] Add permissions support to PostgreSQL
This commit adds permissions support to PostgreSQL auth method.
2022-11-13 19:52:20 +01:00
Gravita
60030bd769 [ANY] Update modules 2022-10-30 20:56:32 +07:00
Gravita
bfa9a9c187 [ANY] Update modules 2022-10-30 20:51:43 +07:00
Gravita
a560ab4812 Merge tag 'v5.3.1' into dev
5.3.1 stable
2022-10-23 17:06:00 +07:00
Gravita
a5b4282037 Merge branch 'release/5.3.1' 2022-10-23 17:05:52 +07:00
Gravita
420fd53553 [ANY] 5.3.1 stable 2022-10-23 17:05:38 +07:00
Gravita
1bb7e99e12 [FIX] Paper installAuthlib 2022-10-23 17:00:39 +07:00
Gravita
c74f430129 [FIX] Paper installAuthlib 2022-10-17 00:47:24 +07:00
Gravita
df04b459be [FEATURE] Public getDigest() 2022-10-15 15:21:42 +07:00
Andrew Molchanov
5a0ff3610b
[DOCS] Updating the code of conduct (#616) 2022-10-14 20:11:16 +07:00
Gravita
ca12e7cbd0 [ANY] Update modules 2022-10-07 18:56:09 +07:00
Gravita
6fb9174681 [FEATURE] AuthSupportRemoteClientAccess 2022-10-07 18:55:42 +07:00
Gravita
36d97e7f8b [FEATURE] Profiles refresh 2022-10-01 16:40:19 +07:00
Gravita
28a9b5efc4 [ANY] Update modules 2022-09-28 16:54:16 +07:00
Gravita
86f9f20b52 [FEATURE] BCryptPasswordVerifier 2022-09-28 16:53:56 +07:00
Gravita
8bec134611 Merge tag 'v5.3.0' into dev
5.3.0 stable
2022-09-08 14:09:59 +07:00
Gravita
07be86f695 Merge branch 'release/5.3.0' 2022-09-08 14:09:51 +07:00
Gravita
91f3773c54 [ANY] 5.3.0 stable 2022-09-08 13:45:46 +07:00
Gravita
b2486efb30 [ANY] Update modules 2022-09-08 13:44:11 +07:00
Gravita
970761374a [FEATURE] ArchTrigger 2022-09-04 16:18:33 +07:00
Gravita
b43ff9e7a6 [ANY] 5.3.0 beta 2022-09-04 14:48:00 +07:00
Gravita
c7058499b7 [FIX] EnFS Build 2022-09-04 14:40:13 +07:00
microwin7
6d5ae93889
[FIX] JRE Full Download URL Update (#607)
Which is based on OpenJFX and Minimal VM
2022-09-01 14:04:27 +07:00
Clercq
78e5c8866f
[FEATURE] Injecting certificates to default truststore (#609) 2022-09-01 14:03:40 +07:00
Gravita
692aa8d3cf [ANY] Update modules 2022-08-30 14:26:24 +07:00
Gravita
2324af1c46 [FIX] Bug fixes 2022-08-30 14:26:08 +07:00
Gravita
7414132bad [ANY] Update modules 2022-08-28 13:37:09 +07:00
Gravita
65ae13a042 [FEATURE] MirrorHelper module 2022-08-28 13:36:49 +07:00
Gravita
230194f2a0 [FEATURE] Update rework 2022-08-24 13:41:01 +07:00
Clercq
9bac9e3bef
[FIX] HttpAuthCoreProvider (#603) 2022-08-12 16:22:50 +07:00
Gravita
2d9037fedc [ANY] Support 1.19.2 2022-08-06 00:01:14 +07:00
Clercq
385f2d9ec5
Add to HttpAuthCoreProvider the AuthSupportHardware implementation (#601)
* initial

* test fixes

* review fixes

* review fixes

* test fixes

* review fixes

* review fixes
2022-08-01 15:54:07 +07:00
Gravita
81b80a7938 [FEATURE] Download assets from mojang 2022-07-30 16:15:35 +07:00
Gravita
bbb962c624 [ANY] Add client version 1.19.1 2022-07-28 21:50:55 +07:00
Gravita
eb5bbb9acf [FIX] Compile error 2022-07-26 17:24:14 +07:00
Gravita
3561522d14 [FIX] Remove updateFastCheck 2022-07-26 17:22:34 +07:00
Gravita
c8768326ea [FEATURE] 5.3.0 features 2022-07-24 16:27:24 +07:00
Gravit
dbdc1b4d6a
Merge pull request #600 from microwin7/patch-10
[FIX] Disable Html Escaping
2022-07-21 22:06:41 +07:00
microwin7
d46c380f45
[FIX] Disable Html Escaping 2022-07-21 11:35:13 +03:00
Gravita
6bed8b383d Merge tag 'v5.2.13' into dev
5.2.13 stable release
2022-07-12 15:48:34 +07:00
Gravita
b7a6cfda6d Merge branch 'release/5.2.13' 2022-07-12 15:48:22 +07:00
Gravita
349cec3e40 [ANY] 5.2.13 stable 2022-07-12 15:47:31 +07:00
Gravita
1d349746d3 [ANY] Update dependencies 2022-07-12 15:39:44 +07:00
Gravita
ab7e5ce2f7 [ANY] Update modules 2022-06-29 17:37:00 +07:00
Gravita
afd147f92e [FIX] Small fixes 2022-06-29 17:36:45 +07:00
Gravita
08fe9b2eb4 [FEATURE] InstallAuthlib support 'download-context' 2022-06-19 17:04:44 +07:00
Gravita
bc3c7ba171 [FEATURE] InstallAuthlib command 2022-06-13 19:37:40 +07:00
Gravita
e8d3232dba [ANY] 1.19 support 2022-06-09 17:00:35 +07:00
Gravita
0b38781858 [FIX] HttpAuthCoreProvider small fixes 2022-06-08 23:17:35 +07:00
Gravita
6fac47f99d [ANY] 5.2.13 dev 2022-05-31 17:23:52 +07:00
Gravita
5b8260957a Merge tag 'v5.2.12' into dev
5.2.12 stable
2022-05-31 17:21:39 +07:00
Gravita
1e40ef94e4 Merge branch 'release/5.2.12' 2022-05-31 17:21:29 +07:00
Gravita
63625f0e60 [ANY] 5.2.12 stable 2022-05-31 17:16:15 +07:00
Gravita
6e9ac8af90 [ANY] Update modules 2022-05-31 17:09:19 +07:00
Gravita
44ba945b58 [FEATURE] DebugCommand 2022-05-31 17:07:52 +07:00
Gravit
2c1129aa5b
Merge pull request #587 from microwin7/patch-8
Java recommend don't valid for starting minecraft client
2022-05-29 14:51:15 +07:00
microwin7
3a43ebff45 [FIX][STYLE] Minimal recommend JAVA for 1.18+ set 17 2022-05-29 06:44:25 +03:00
Gravita
857901a667 [FEATURE] Isolate minecraft 2022-05-19 00:38:12 +07:00
Gravita
4ff13a67b9 [FIX] ServerWrapper set start.sh executable 2022-05-18 23:57:21 +07:00
Gravita
c75185f697 [ANY] Update modules 2022-05-13 23:24:54 +07:00
Gravita
5d332a10b5 [FIX] ServerWrapper config args 2022-05-11 22:05:34 +07:00
Gravita
95704fc1bf [FIX] OSSLSignCode 2022-05-02 23:08:30 +07:00
Gravit'a
4400cbe192 [ANY] Update modules 2022-04-18 21:32:30 +07:00
Gravit'a
ccb297a498 [ANY] Update modules 2022-04-18 21:06:48 +07:00
Gravit'a
126d23037e [ANY] Update modules 2022-04-18 20:03:02 +07:00
Gravita
600dd02703 [FIX] JLine complete NPE 2022-04-16 19:16:40 +07:00
Gravita
2ff46420a8 Merge tag 'v5.2.11' into dev
5.2.11 stable
2022-04-12 19:23:56 +07:00
Gravita
9b43592a27 Merge branch 'release/5.2.11' 2022-04-12 19:23:49 +07:00
Gravita
517108deb4 [ANY] 5.2.11 2022-04-12 19:23:28 +07:00
Gravita
83ae8a7e72 [ANY] Remove deprecated 2022-04-12 18:58:14 +07:00
Gravita
bf1967f32b [FIX] ServerWrapper shutdown fix 2022-04-12 18:46:58 +07:00
Gravita
8379a6efec [FIX] Client permissions 2022-04-11 20:43:35 +07:00
Gravita
7ef304c2cc [ANY] 5.2.11-SNAPSHOT 2022-04-11 19:32:39 +07:00
Gravita
8a51c98df8 [ANY] Remove deprecated 2022-04-11 19:28:47 +07:00
Gravita
d128c643ae [ANY] 5.2.1 dev 2022-04-11 17:12:44 +07:00
Gravita
344365d1ef Merge tag 'v5.2.10' into dev
5.2.10 stable
2022-04-11 17:12:21 +07:00
Gravita
7254a197a1 Merge branch 'release/5.2.10' 2022-04-11 17:12:11 +07:00
Gravita
f4280b89f4 [ANY] 5.2.10 stable 2022-04-11 17:11:53 +07:00
Gravita
fa50a6f64c [ANY] Update modules 2022-04-10 23:23:51 +07:00
Gravita
5e6ab3763e [FIX] DiscordBotConsole 2022-04-10 22:14:55 +07:00
Gravit'a
89b800c124 [ANY] Update modules 2022-04-10 01:26:42 +07:00
Gravit'a
b8b841cdd9 [ANY] Update modules 2022-04-09 20:40:58 +07:00
Gravit'a
b614ae6d69 [ANY] Update modules 2022-04-09 18:02:26 +07:00
Gravita
a20b70e2a1 [FEATURE] HttpAuthCoreProvider toString() methods 2022-04-07 22:16:25 +07:00
Gravita
dc27fc04aa [FIX] Forge 1.17+ exit crash 2022-04-07 22:09:20 +07:00
Gravita
2709cbe95f [FIX] getPackageFromClass 2022-04-07 01:39:40 +07:00
Gravita
be565e2218 [FEATURE][EXPERIMENTAL] ServerWrapper launch methods 2022-04-07 01:34:58 +07:00
Gravita
7cedaef90a [ANY] Gradle upgrade 2022-04-03 15:46:59 +07:00
Gravita
b135535147 Upgrade dependencies 2022-04-03 15:44:57 +07:00
Gravita
7cd6b9c1d9 [FIX] Bad login (MySQL + HWID) 2022-04-03 15:08:15 +07:00
Gravita
7bfa4ebdab [FIX] Bad login (MySQL + HWID) 2022-03-30 16:39:48 +07:00
Gravita
8afa630546 [FIX] Bad login (MySQL + HWID) 2022-03-30 15:52:28 +07:00
Gravita
c42f410bc4 [FIX] ServerWrapper main class lookup 2022-03-28 22:44:43 +07:00
Gravita
7522e156ae [ANY] Remove signedClientConfig and securityManagerConfig 2022-03-24 16:51:50 +07:00
Gravita
67a53218d4 [FIX] Old profiles crash 2022-03-24 16:49:50 +07:00
Gravita
b67ae753f0 [ANY] 5.2.10-dev 2022-03-22 15:37:21 +07:00
Gravita
6663f38241 [FEATURE] LwjglDownloadCommand, modules and modulePath 2022-03-22 15:36:26 +07:00
Gravita
e0f291fdac Merge tag 'v5.2.9' into dev
5.2.9 Stable
2022-03-19 16:34:58 +07:00
Gravita
b57118cd20 Merge branch 'release/5.2.9' 2022-03-19 16:34:43 +07:00
Gravita
638ae3d5fe [ANY] 5.2.9-stable release 2022-03-19 16:34:19 +07:00
Gravita
9b3b49966a [FIX] RefreshToken 2022-03-18 20:03:36 +07:00
Gravita
f6ee175acd [FIX] UTC Clock 2022-03-18 19:54:56 +07:00
Gravita
736bf51eaa [FIX] UTC Timezone 2022-03-18 19:50:46 +07:00
Gravita
dd3fd101f4 [FIX] Deprecate Request.session 2022-03-18 19:22:03 +07:00
Gravita
96915b0f78 [FIX] PostgresSQLCoreProvider and HttpAuthCoreProvider bug fixes 2022-03-18 19:02:58 +07:00
Gravita
6430b9e2d4 Merge branch 'feature/removelegacysession' into dev 2022-03-18 18:28:27 +07:00
Gravita
22aa7ad7ae [ANY] 5.2.9-dev 2022-03-18 00:13:27 +07:00
Gravita
922944dacc [ANY] Remove DiscordIntegration 2022-03-18 00:13:16 +07:00
Gravita
a54d7ba89a [FEATURE] PlayerProfile properties, User assets 2022-03-17 23:28:16 +07:00
Gravita
2b117f6717 [FEATURE] UserSupportProperties extension 2022-03-17 22:55:30 +07:00
Gravita
6086b15c9f [FIX] Deprecate RestoreSession 2022-03-17 21:27:08 +07:00
Gravita
d38feed952 [ANY] Update modules 2022-03-17 21:12:44 +07:00
Gravita
62094e0cf6 [FEATURE] Remove legacy session 2022-03-17 21:07:54 +07:00
Gravita
6aed114791 [FEATURE] MySQL and PostgreSQL AuthCoreProvider use OAuth 2022-03-17 20:57:20 +07:00
Gravita
afbed1345f [FEATURE] Support customJvmOptions 2022-03-17 19:30:10 +07:00
Gravit
c351846a43
[FIX] Github Actions compile 2022-03-10 17:19:41 +07:00
Gravit
26835c79bb
Merge pull request #563 from Dbarkovski/patch-4
Support 1.18.2
2022-03-03 19:51:27 +07:00
XuTpbl_JIuC
b72600656a
Support 1.18.2
Support 1.18.2
2022-03-03 11:51:33 +03:00
Gravita
e1f4f76f70 [ANY] Updated modules 2022-02-11 20:38:27 +07:00
Gravita
fecf458e82 [ANY] Remove authlib 2022-02-11 20:38:27 +07:00
Gravit
5fb3a40bbc
Merge pull request #560 from microwin7/patch-5
Update HTTPRequest.java
2022-02-09 19:26:12 +07:00
Gravit
10e7adc4b1
Merge pull request #561 from microwin7/patch-4
Update HTTPRequest.java
2022-02-09 19:25:48 +07:00
microwin7
6c8ba831c2
Update HTTPRequest.java 2022-02-07 12:29:34 +03:00
microwin7
cbeb02950a
Update HTTPRequest.java 2022-02-07 12:24:05 +03:00
Gravit
9b9531ce5e
Merge pull request #553 from microwin7/patch-3
[FIX] MySQL remove limit 1 for update query
2022-01-25 18:21:36 +07:00
microwin7
999d7b7795
[FIX] MySQL remove limit 1 for update query 2022-01-24 09:10:26 +03:00
Gravita
7bfa6f116b Merge tag 'v5.2.8' into dev
5.2.8-stable
2022-01-12 20:46:07 +07:00
Gravita
6ab0c7d6a2 Merge branch 'release/5.2.8' 2022-01-12 20:45:44 +07:00
Gravita
07bfd0b340 [ANY] Update mirror 2022-01-12 20:45:13 +07:00
Gravita
ba0eca5e1f [ANY] 5.2.8 stable 2022-01-12 20:35:36 +07:00
Gravita
788499a45c [FIX] Update modules 2022-01-12 02:15:17 +07:00
Gravita
2b95c58f28 [FIX] MicrosoftAuthCoreProvider 2022-01-12 02:15:01 +07:00
Gravita
4cb47d5891 [ANY] Set default mirror 2022-01-06 23:25:57 +07:00
Gravita
5dd5092957 [ANY] Update dependencies 2022-01-06 23:05:55 +07:00
Gravita
b559655486 [ANY] 5.2.8-dev 2022-01-06 22:25:36 +07:00
Gravita
8945037860 [FIX] InvalidPathException 2022-01-01 22:52:04 +07:00
Gravita
bde9fa958d [ANU] Modules update 2021-12-21 19:53:58 +07:00
Gravita
dd48074e9a [ANY] Update log4j2 2021-12-21 17:26:42 +07:00
Gravita
589477888f [FIX] Remove verifyUsername 2021-12-15 18:37:56 +07:00
Gravita
b496d60b40 [ANY] New client format support, fix fabric 2021-12-13 00:58:38 +07:00
Gravita
8c84a80372 Merge tag 'v5.2.7' into dev
5.2.7
2021-12-11 15:05:01 +07:00
Gravita
3a77a59eac Merge branch 'release/v5.2.7' 2021-12-11 15:04:25 +07:00
Gravita
f3f78b617a [ANY] 5.2.7 stable 2021-12-11 15:04:09 +07:00
Gravita
e40af46e8d [FIX] Remove codeql-analyze 2021-12-11 14:56:12 +07:00
Gravit
c88fb1d8e3
Merge pull request #544 from Dbarkovski/patch-2
Add 1.18.1 support
2021-12-11 14:55:00 +07:00
XuTpbl_JIuC
99812da816
Add 1.18.1 support 2021-12-10 23:26:00 +03:00
Gravita
ce4f4cab62 [FIX] Postgresql no hwidId need 2021-12-10 20:57:21 +07:00
Gravita
796b2e2533 [FEATURE] Postgresql support 2021-12-10 20:46:49 +07:00
Gravit
3b8a01fbb4
Merge pull request #542 from Dbarkovski/patch-1
Fix support ServerWrapper 1.18
2021-12-09 01:18:21 +07:00
XuTpbl_JIuC
9a1498716f
Fix support ServerWrapper 1.18 2021-12-08 17:17:14 +03:00
Gravita
9c289cd1cb [FEATURE] ServerWrapper 1.18 support 2021-12-02 16:45:36 +07:00
Gravit
9cc1cf5feb
Merge pull request #535 from Nalborr/patch-2
Support 1.18
2021-12-01 13:54:55 +07:00
Nalborr
0379477971
Support 1.18 2021-11-30 20:43:51 +02:00
Gravit
9f2c9bb589
Update ClientPermissions.java 2021-11-22 15:00:09 +07:00
Gravita
a8e9418e89 [FIX] OptionalView conflict mods 2021-11-17 23:54:23 +07:00
Gravita
81a94faaf6 [FIX] ServerWrapperSetup javaw.exe 2021-11-17 21:57:51 +07:00
Gravita
559f4f4de7 [FIX] ServerWrapperSetup .bat create 2021-11-17 21:54:51 +07:00
Gravita
3aedd4a2b8 [ANY] 5.2.7-dev 2021-11-17 21:53:46 +07:00
Gravita
d995b3508c [FIX] Client start hotfix 2021-11-17 01:00:31 +07:00
Gravita
4dc79f320d Merge tag 'v5.2.6' into dev
5.2.6-stable
2021-11-16 23:39:52 +07:00
Gravita
84a53304b3 Merge branch 'release/5.2.6' 2021-11-16 23:39:41 +07:00
Gravita
ca943ac24e [ANY] 5.2.6-stable 2021-11-16 23:22:26 +07:00
Gravita
d8d4448f1e [ANY] Gradle 7.3 2021-11-16 23:02:29 +07:00
Gravita
eecd61e6ba [FEATURE] Offline Mode 2021-11-16 23:00:41 +07:00
Gravita
111639b963 [FEATURE] RequestService 2021-11-16 18:32:52 +07:00
Gravita
fbb24bb904 [ANY] Deprecated old features 2021-11-10 23:44:19 +07:00
Gravita
f30daab0f7 [FIX] Proguard jdk dir specified twice 2021-11-10 23:32:26 +07:00
Gravita
50d8f12536 [FIX] Remove HWIDProvider 2021-11-10 23:14:26 +07:00
Gravita
ac1279ff5c [FIX] HttpAuthCoreProvider 2021-11-09 14:32:09 +07:00
Gravita
f8569f9165 [FEATURE] GSON serialize Record 2021-11-09 14:12:31 +07:00
Gravita
2d0957f834 [FIX] MemoryAuthCoreProvider 2021-11-09 12:22:18 +07:00
Gravita
c7cb512710 Merge tag 'v5.2.5' into dev
5.2.5 stable release
2021-11-05 22:26:58 +07:00
Gravita
0640fc6d60 Merge branch 'release/5.2.5' 2021-11-05 22:26:43 +07:00
Gravita
2ef3ca7324 [ANY] 5.2.5 stable 2021-11-05 22:23:59 +07:00
Gravita
2a2c2f6b93 [FEATURE] AuthCoreProvider rework 2021-11-05 22:19:00 +07:00
Gravita
15da924aa6 [FIX] AcceptPasswordVerifier 2021-10-23 17:27:08 +07:00
Gravita
618e981de5 [FIX] AuthProviderPair logger is null 2021-10-18 14:44:35 +07:00
Gravita
e5b603e2a9 [FEATURE] Use UUID for checkServer token 2021-10-17 16:53:33 +07:00
Gravit
7bcf5f10ab
Merge pull request #523 from BartolomeoDR/patch-1
[FIX] Fixed issue where user is unable to play/start the modpack after logout
2021-10-17 12:35:31 +07:00
BartolomeoDR
af2155c4bf
[FIX] Fixed user is unable to play after logout
Right now refCount value is set to 1, and inside handler.setClient its being incremented to 2. Because of that refCount check never goes to 0, and so current session is never stored ( as only single handler has this client, and not two ).
2021-10-16 14:32:57 +03:00
BartolomeoDR
d1bc03664b
Update Client.java
Added ability to customize refCount value of a client
2021-10-16 14:27:48 +03:00
Gravita
b543aaf4a6 Merge tag 'v5.2.4b2' into dev 2021-10-16 00:41:29 +07:00
Gravita
34e5b9027e Merge branch 'hotfix/serverwrapperfail' 2021-10-16 00:40:00 +07:00
Gravita
f3a095966b [ANY] 5.2.4b2 hotfix 2021-10-16 00:39:55 +07:00
Gravita
d99f24c389 [FIX] Invoke mainclass for non-vargs main method 2021-10-16 00:38:52 +07:00
Gravita
2b2aeedc20 Merge tag 'v5.2.4' into dev
5.2.4 stable release
2021-10-15 21:41:46 +07:00
Gravita
39bdab50ca Merge branch 'release/5.2.4' 2021-10-15 21:41:33 +07:00
Gravita
aed103e837 [ANY] 5.2.4 stable 2021-10-15 21:37:02 +07:00
Gravita
1a7160c394 [FIX] Small UI improvements 2021-10-15 21:35:47 +07:00
Gravita
7d8975041f [FIX] Deprecation warning 2021-10-15 21:29:26 +07:00
Gravita
d9a8e161f1 [FEATURE] Improvements for token command 2021-10-15 21:25:27 +07:00
Gravita
b5aeac3ff2 [FIX] Correct error in checkServer 2021-10-15 21:05:26 +07:00
Gravita
2171fb291f [ANY] Обновлены модули 2021-10-14 20:55:23 +07:00
Gravita
8b396dfb13 [ANY] Bug fixes 2021-10-14 20:54:44 +07:00
Gravita
a283f907d6 Merge tag 'v5.2.3' into dev
5.2.3 stable release
2021-10-13 21:44:31 +07:00
Gravita
b48059998f Merge branch 'release/5.2.3' 2021-10-13 21:43:41 +07:00
Gravita
a1ab2d404d [ANY] 5.2.3 stable 2021-10-13 21:43:26 +07:00
Gravita
112ac31803 [FIX] needMoreFactors 2021-10-13 21:28:43 +07:00
Gravita
26ed69e1a7 [FIX] FileAuthSystem module permissions 2021-10-13 20:01:12 +07:00
Gravita
d9b03d3c6a [FIX] Java 17 detect 2021-10-13 14:38:38 +07:00
Gravita
dabe2e8106 [FIX] Ping Server 2021-10-12 17:34:44 +07:00
Gravita
58dfc472ac [FIX] ServerWrapper connect to ServerProfile 2021-10-12 17:09:19 +07:00
Gravita
9841ef3157 [FEATURE] Permissions 2021-10-12 16:55:32 +07:00
Gravita
e782f0409d [ANY] Update modules 2021-09-30 19:55:27 +07:00
Gravita
94a1a2f464 [ANY] Modules update 2021-09-30 19:40:06 +07:00
Gravita
a94b6e8836 [FEATURE][EXPERIMENTAL] Microsoft account support 2021-09-30 19:39:35 +07:00
Gravita
d5cc2ee094 [FIX] FileAuthSystem command 2021-09-30 16:25:39 +07:00
Andrew Molchanov
a1284ff9cb
[FIX] GitHub Actions - Правки и оптимизации (#520)
* [FIX] GitHub Actions - Вернул Release.zip

* [FIX] GitHub Actions - Доработал LauncherBase.zip

Так всё ж куда проще, лол

* [FIX] GitHub Actions - фикс захода в папку

* [FIX] GitHub Actions - Немножко оптимизации
2021-09-27 13:54:27 +07:00
Gravita
baf2997089 [FIX] ProGuard update 2021-09-26 11:40:00 +07:00
Gravita
dfd565d44f [FIX] Bug fixes 2021-09-25 18:46:07 +07:00
Gravita
7a78cbb878 [FIX] ServerWrapper improvements 2021-09-25 18:23:23 +07:00
Gravita
958686a032 [FEATURE][EXPERIMENTAL] New ServerWrapper auth system 2021-09-25 17:40:08 +07:00
Gravita
4c78d00360 [FEATURE] Pattern-based permissions system 2021-09-22 16:50:05 +07:00
Gravita
d2e222d67d [ANY] Обновлены модули 2021-09-22 14:22:36 +07:00
Gravita
39ac6cdad8 [FEATURE] FileAuthSystem improvements 2021-09-22 13:52:17 +07:00
Gravita
4bd84728f3 [REFACTOR] Remove deprecated methods 2021-09-22 13:06:45 +07:00
Gravita
9b55e243e7 [REFACTOR] Remove AuthProvider/AuthHandler support 2021-09-22 12:45:48 +07:00
Gravita
fbbde7991e [REFACTOR] Upgrade to Java 17 2021-09-22 12:19:18 +07:00
Gravita
eec54ad4d9 [FIX] Create release 2021-09-22 11:25:59 +07:00
Gravita
36a6cd7e6a [FIX] Create release 2021-09-22 11:22:13 +07:00
Gravita
1c5a403d69 Merge tag 'v5.2.2' into dev
5.2.2 stable release
2021-09-22 11:16:18 +07:00
Gravita
6c5b02bdb1 Merge branch 'release/5.2.2' 2021-09-22 11:15:55 +07:00
Gravita
3cce0e93f3 [ANY] 5.2.2 stable 2021-09-22 11:15:10 +07:00
Gravita
77b5188b52 [FIX] HashedDir NPE 2021-09-22 11:02:23 +07:00
Gravita
7ab7c29fe4 [FIX] Java 7 found crash 2021-09-22 10:55:35 +07:00
Gravit
04c854703e
Merge pull request #518 from microwin7/patch-1
[FIX] Release Name
2021-09-16 17:48:14 +07:00
microwin7
222cbb2f7b
[FIX] Release Name 2021-09-15 17:29:20 +03:00
Gravita
a09c2dff54 [ANY] Update gradle 2021-09-14 18:06:14 +07:00
Gravita
11af2943db [ANY] Update gradle 2021-09-14 18:05:36 +07:00
Gravita
f0ae4cbb12 [FIX] AuthManager NPE 2021-09-12 17:52:28 +07:00
Gravit
9e7dedffc9
Merge pull request #513 from JoCat/dev
[FIX] GitHub Actions - Загрузка файлов в релиз
2021-09-06 10:02:38 +07:00
Gravit
acf106baa9
Merge pull request #515 from microwin7/dev
Update dependencies and fix HikariCP
2021-09-04 19:59:29 +07:00
Gravita
8694963313 [FIX] TextureProvider NPE error message 2021-09-04 19:54:48 +07:00
microwin7
c44162dbbe [FIX] build.gradle 2021-09-04 09:03:33 +03:00
microwin7
6211744b83 [FIX] build.gradle 2021-09-04 09:02:58 +03:00
microwin7
f279e97ea2 [ANY] Update common modules and FileAuthSystem_module README.md 2021-09-04 08:57:35 +03:00
microwin7
ad5c2382cf [FIX] AdditionalHash_module 2021-09-04 08:54:34 +03:00
microwin7
62991ed709 [FIX] MySQLSourceConfig HikariCP 2021-09-04 08:51:34 +03:00
Gravit
077e6f3053
Merge pull request #514 from microwin7/dev
Update dependencies
2021-09-04 11:45:09 +07:00
microwin7
c0b1373542 Update dependencies 2021-09-04 02:18:31 +03:00
Gravita
12b933c8bd [FIX] Java 11+ get build fix 2021-09-03 22:15:53 +07:00
Gravita
a91a578c58 [ANY] 5.2.2 dev 2021-09-03 21:54:45 +07:00
Gravita
e5cd39e544 [FEATURE][EXPERIMENTAL] JVMHelper.getBuild() 2021-09-03 21:53:27 +07:00
Andrew Molchanov
8b1f0a7d8c
[FIX] GitHub Actions - Немного поправил Prebuild 2021-09-03 16:48:15 +03:00
Andrew Molchanov
fe994cdcca
[FIX] GitHub Actions - Загрузка файлов в релиз 2021-09-03 16:23:51 +03:00
Gravit
438b62b3ca
Merge pull request #512 from JoCat/dev
[FIX] GitHub Actions
2021-09-01 17:57:37 +07:00
Andrew Molchanov
b424da0d62
[FIX] GitHub Actions - получение версии из тега 2021-09-01 13:49:54 +03:00
Andrew Molchanov
6f2f7bee13
[FIX] GitHub Actions 2021-09-01 00:25:40 +03:00
Gravita
5ea3788e7d Merge tag 'v5.2.1' into dev
v5.2.1 Stable
2021-08-31 18:28:18 +07:00
Gravita
92cdaa446e Merge branch 'release/5.2.1' 2021-08-31 18:28:01 +07:00
Gravita
27f7082568 [ANY] 5.2.1 2021-08-31 18:23:40 +07:00
Gravita
effbb18bbc [ANY] Обновлены модули 2021-08-23 15:58:22 +07:00
Gravita
fb7a552843 [FIX] JsonAuthCoreProvider Bearer header 2021-08-23 15:56:08 +07:00
Gravita
93fdd7f899 [ANY] Обновлены модули 2021-08-18 17:27:18 +07:00
Gravita
dc3d75c650 [FIX] getParamsFromUri UTF-8 2021-08-17 15:05:27 +07:00
Gravita
3e973c5de3 [FIX] NPE Issue #506 2021-08-14 03:25:31 +07:00
Gravita
21e92cffc6 [ANY] 5.2.0 stable 2021-08-10 04:50:05 +07:00
Gravita
b7621ef760 [FIX] pro.gravit.launcher.debug 2021-08-10 04:30:55 +07:00
Gravita
2221fadace [FIX] Auth messages 2021-08-10 03:59:04 +07:00
Gravita
214ad8bacb [ANY] Обновлены модули 2021-08-10 03:21:32 +07:00
Gravita
59ca2cb00f [ANY] Обновлены библиотеки 2021-08-10 03:15:21 +07:00
Gravita
a915d41b93 [FIX] JsonAuthCoreProvider joinServer/checkServer 2021-08-08 16:10:07 +07:00
Gravit'a
9fff11e887 [FIX] JsonCoreProvider verifyPassword 2021-08-08 14:53:25 +07:00
Gravit'a
7e77862cd4 [FIX] May be NPE 2021-08-07 11:59:36 +07:00
Gravit'a
5ba36c3a1c [FIX] JavaVersion cache 2021-08-07 11:58:16 +07:00
Gravit'a
1abab46ce4 [FIX] Eclipse and Liberica Java autodetect 2021-08-07 11:52:24 +07:00
Gravit'a
19344b9d27 [FIX] jsonRequest 2021-08-07 03:02:47 +07:00
Tenebrius
0ab9327c74 [ANY] Обновлены модули 2021-08-05 01:20:50 +05:00
Gravita
5f86dc2420 [ANY] Обновлены модули 2021-08-02 08:15:39 +07:00
Gravita
c3d39310b5 [ANY] Обновлены модули 2021-08-02 07:56:23 +07:00
Gravita
902f09324b [FEATURE] MojangCoreProvider 2021-08-02 07:56:01 +07:00
Gravita
aa9b84ba4a [FIX] DialogService 2021-08-02 03:40:09 +07:00
Gravita
711413c52f [FIX] Try fix Downloader cancel() 2021-08-02 02:52:35 +07:00
Gravit'a
9f44673809 [ANY] 5.2.0-beta 2021-08-01 03:11:42 +07:00
Gravita
ce9c13efcc [FIX] DiscordGame fixes 2021-08-01 01:44:21 +07:00
Gravita
3eb69d1284 [ANY] Update modules 2021-07-29 23:36:35 +07:00
Gravita
5d0ccdbde3 [ANY] Update modules 2021-07-27 04:26:18 +07:00
Gravita
4dd30faf7e [FEATURE][EXPERIMENTAL] Support slim skins 2021-07-27 04:26:10 +07:00
Gravita
b0538abe63 [FEATURE] Performance: Skin/Cloak local load 2021-07-27 04:26:10 +07:00
Gravit'a
fecc14010d [FEATURE] IDEA Code Clean 2021-07-21 23:58:58 +07:00
Zaxar163
eb9871123b
[ANY] Небольшой рефактор.
Обнаружил isClient и IS_CLIENT, ещё пару подобных пережитков...
2021-07-21 19:43:47 +03:00
Zaxar163
dfa2b042e4
[FIX] Проблемы с кодировкой в SecurityHelper
Closes #412
2021-07-21 19:14:57 +03:00
Gravit
7ad308cc23
[FIX] Java search in Windows 2021-07-14 00:09:08 +07:00
Gravit
f9c644ff4e
[FIX] Undo changes 2021-07-14 00:07:13 +07:00
Gravit
62e392359d
[FIX] Java search in Windows 2021-07-14 00:06:11 +07:00
Gravita
579fec1518 [ANY] Upgrade gradle to 7.1 2021-07-12 17:31:34 +07:00
Gravita
30ec6409fd [FIX] makeprofile fabric fix 2021-07-11 23:33:31 +07:00
Gravita
1912393a40 [FIX] Download ZIP fix 2021-07-11 23:08:39 +07:00
Gravita
ac15002327 [FEATURE] launcherinstaller forge WIP 2021-07-11 22:46:55 +07:00
Gravita
9112ca0c88 [FEATURE] launcherinstaller fabric 2021-07-11 18:35:36 +07:00
Gravita
3be2452ceb [ANY] Обновлены модули 2021-07-11 17:08:08 +07:00
Gravita
afa0cd0366 [FEATURE] Downloader ProgressBar 2021-07-11 17:07:54 +07:00
Gravita
7f1583c0a3 [ANY] Обновлены библиотеки 2021-07-11 16:00:58 +07:00
Gravita
72e0c55455 [ANY] Обновлены модули+ 2021-07-11 15:36:03 +07:00
Gravita
8085b50b3d [FEATURE] MakeProfileHelper 2021-07-11 15:35:51 +07:00
Gravita
1a8ec31a5b [FEATURE] SYSTEM_ARGS classLoaderConfig 2021-07-11 13:53:40 +07:00
Gravita
b901a5b9e4 [ANY] Support 1.17.1 2021-07-11 13:13:43 +07:00
Gravita
0cc47a7b06 [FIX] JavaHelper Windows 2021-07-09 17:18:29 +07:00
Gravita
736aa93106 [FEATURE] MakeProfileCommand 2021-07-09 15:35:00 +07:00
Gravita
442ff9b752 [FIX] Compilation fix 2021-07-04 22:19:33 +07:00
Gravita
602a4b17f8 [FEATURE] DialogService, AdditionalDataRequest 2021-07-04 20:01:24 +07:00
Gravita
7ca3889bb5 [ANY] 5.2.0-alpha 2021-06-23 11:06:26 +07:00
Gravita
3045619657 [ANY] Small IDEA Code Inspect 2021-06-23 11:06:23 +07:00
Gravita
f82d5dfa03 [ANY] Small IDEA Code Inspect 2021-06-23 11:05:44 +07:00
Gravit'a
27219f7dd7 [FEATURE] DiscordGame support MacOS 2021-06-22 21:10:17 +07:00
Gravita
3f0c972b08 [FEATURE] WhitelistComponent 2021-06-22 12:09:34 +07:00
Gravita
b4718a8062 [FIX] SecurityCheck command 2021-06-22 11:45:04 +07:00
Gravita
81be9d21de [FIX] MySQLCoreProvider HWID fix 2021-06-22 11:13:12 +07:00
Gravit
0c281ab50d
[FIX] Small fixes 2021-06-21 14:14:25 +07:00
Gravit
e36cfea4f9
[FIX] Bad Login (Clientside) in MySQLAuthCoreProvider 2021-06-20 12:01:39 +07:00
Gravit
867f367860
[FIX] Logger.error 2021-06-20 10:42:13 +07:00
Gravit
7133727edd
[FEATURE] Configurable query MySQL AuthCoreProvider 2021-06-20 10:13:53 +07:00
Gravita
c1f1dae7f2 [FIX] FileAuthSystem install init 2021-06-18 17:00:46 +07:00
Gravita
5abc456c3b [FIX] DoubleDigestPasswordVerifier 2021-06-18 16:57:30 +07:00
Gravita
b09cd5c456 [FIX] New trigger system 2021-06-18 11:34:32 +07:00
Gravita
661fb94594 [FIX] Downloader bug fixes 2021-06-18 10:07:00 +07:00
Gravita
d01c27177c [FEATURE] isAllowSave support 2021-06-16 16:35:55 +07:00
Gravita
8ea134dc27 [FIX] AuthCoreProvider checkServer bug fixes 2021-06-15 21:02:33 +07:00
Gravita
9fe1fc4f23 [FEATURE] AuthCoreProvider features 2021-06-15 19:24:28 +07:00
Gravita
201b6826ed [FEATURE] PasswordVerifier encrypt support 2021-06-15 18:24:29 +07:00
Gravit
fbd246a338
Merge pull request #448 from MagTemTenebrius/dev
[Fix] Хеширование
2021-06-14 16:48:41 +07:00
Tenebrius
8c259a7702 [Fix] Хеширование 2021-06-14 14:22:08 +05:00
Tenebrius
781ab27127 [Fix] Хеширование 2021-06-14 13:59:53 +05:00
Gravita
25ddb485ac Merge tag 'vdiscordlink' into dev
v5.1.12
2021-06-13 20:02:18 +07:00
Gravita
0f0d55ef07 Merge branch 'hotfix/discordlink' 2021-06-13 20:01:09 +07:00
Gravita
7ae24071cf [FIX] Discord link hotfix 2021-06-13 20:00:47 +07:00
Gravita
00bbad3451 [FIX] RequestAuthProvider 2021-06-13 17:24:11 +07:00
Gravita
3431fae109 [FEATURE] 1.17 support 2021-06-11 10:14:22 +07:00
Gravita
32a4729970 [FIX] NPE in ClientLauncherEntryPoint 2021-06-09 10:01:07 +07:00
Gravita
8ad6f7ccbe [FIX] Jwts token builder 2021-06-06 00:50:47 +07:00
Gravita
fcdaa36421 [FIX] Jwts token builder 2021-06-06 00:46:39 +07:00
Gravita
1b81d5956c [FIX] Downloader callback bad API 2021-06-05 08:43:43 +07:00
Gravita
5886d1ac48 [FEATURE] Downloader fix 2021-06-05 06:30:56 +07:00
Gravita
314eb8c09e [FEATURE] MySQLCoreProvider support HWID 2021-06-04 06:28:32 +07:00
Gravita
386239bfd7 [FEATURE] More commands 2021-06-04 01:13:12 +07:00
Gravita
80a7e1b593 [ANY] Update modules 2021-06-03 22:52:11 +07:00
Gravita
2d48219f61 [ANY] Update gradle 2021-06-03 22:52:02 +07:00
Gravita
c719f2448e [FEATURE] Updates cache 2021-06-01 05:48:33 +07:00
Gravita
8445ed2dca [FIX] Current Java 9+ javafx selector fix 2021-05-31 08:49:09 +07:00
Gravita
20a326a707 [FIX] CheckServer fix 2021-05-31 08:25:33 +07:00
Gravit
ffad6c0b74
[FIX] Java select 2021-05-31 05:51:40 +07:00
Gravita
a8573c5b57 [FEATURE] New downloader 2021-05-30 01:05:29 +07:00
Gravita
a700ec4ca7 [FIX] encrypted runtime (ZipException: duplicate) 2021-05-29 05:23:29 +07:00
Gravita
617f79cbdd [FIX] RuntimeInClient fixes 2021-05-29 04:42:24 +07:00
Gravita
8290321512 [FIX] RuntimeInClient fixes 2021-05-29 04:42:09 +07:00
Gravita
5920f524c7 [FIX] Java Version search 2021-05-29 03:14:03 +07:00
Gravita
265a872b08 [FIX] Bug fixes 2021-05-28 21:24:06 +07:00
Gravita
73c6c48f42 [FEATURE] AuthSupportHardware 2021-05-28 21:06:14 +07:00
Gravita
568852b951 [FEATURE] AuthSocialProvider 2021-05-28 19:13:16 +07:00
Gravita
6c966b6126 [FIX] WebView auth 2021-05-28 04:40:37 +07:00
Gravita
c2f55998e3 [FIX] JsonCoreProvider 2021-05-25 21:44:25 +07:00
Gravita
5a349afe9d [FIX] JsonCoreProvider: update accessToken 2021-05-25 17:54:45 +07:00
Gravita
be78caa841 [FEATURE] JsonCoreProvider: getDetails 2021-05-25 17:49:22 +07:00
Gravita
fc33ed11d0 [FEATURE] JsonCoreProvider 2021-05-25 17:38:20 +07:00
Gravita
aefb026952 [REFACTOR] Idea Reformat Code 2021-05-25 16:17:36 +07:00
Gravita
cd99641327 [REFACTOR] Idea Reformat Code 2021-05-25 16:17:29 +07:00
Gravita
2c41b510ad [FIX] Bug fixes 2021-05-23 21:46:52 +07:00
Gravita
b7a7156408 [FEATURE] AuthCoreProvider: OAuth Support Part 4 2021-05-23 18:14:57 +07:00
Gravita
ea3310b738 [FEATURE] AuthCoreProvider: OAuth Support Part 3 2021-05-23 17:17:56 +07:00
Gravita
d3751732b0 [FEATURE] AuthCoreProvider: OAuth Support Part 2 2021-05-23 03:22:04 +07:00
Gravita
d1d3f12abd [ANY] Обновлены модули 2021-05-23 02:46:41 +07:00
Gravita
b919020988 [FEATURE] AuthCoreProvider: OAuth Support Part 1 2021-05-23 02:46:31 +07:00
Gravita
f317912de7 [FEATURE] RuntimeInClient Java 9+ 2021-05-22 23:51:46 +07:00
Gravita
c37fb47795 [FIX] AccessToken null 2021-05-21 17:48:29 +07:00
Gravita
e1f356feb7 [FEATURE] Default commands in AuthCoreProvider 2021-05-20 22:35:34 +07:00
Gravita
aa9dc484e1 [ANY] Обновлены модули 2021-05-20 22:00:05 +07:00
Gravita
a8a360be03 [ANY] Обновлены модули 2021-05-20 15:26:00 +07:00
Gravita
96ef860c70 [FIX] MySQLCoreProvider 2021-05-20 02:47:22 +07:00
Gravita
ab7565e7d1 [FIX] AddHashModule PasswordVerifier 2021-05-20 02:27:47 +07:00
Gravita
d9f8b20a71 [FEATURE] PasswordVerifier and MySQLCoreProvider 2021-05-20 02:27:47 +07:00
Gravita
9da0ca8604 [FIX] Sentry modules 2021-05-20 02:27:47 +07:00
Gravita
b5ce3102a9 [FIX] Deprecated warning 2021-05-20 02:27:47 +07:00
Gravita
929a3a4ebb [FIX] GetAvailabilityAuthResponse 2021-05-20 02:27:47 +07:00
Gravita
c40f6f5883 [ANY] Обновлены модули 2021-05-20 02:27:47 +07:00
Gravita
6f6aa81554 [FEATURE] RejectAuthCoreProvider 2021-05-20 02:27:47 +07:00
Gravita
b647f49390 [FEATURE] New Core Auth System 2021-05-20 02:27:47 +07:00
Gravita
97faf5ef79 [FEATURE] Configure projectName and address from env 2021-05-20 02:27:47 +07:00
Gravita
30cabd25fd [FIX] Encrypted Runtime 2021-05-20 02:27:47 +07:00
Gravit
92a6947ab8
Update SaveProfilesCommand.java 2021-05-13 01:20:05 +07:00
Gravita
1667b00757 [FIX] Dockerfile 2021-05-11 01:11:39 +07:00
Gravita
4cd3d145c9 [ANY] Обновлены модули 2021-05-10 15:54:00 +07:00
Gravita
c2b499b2a9 [FEATURE] LogAppender 2021-05-10 15:53:18 +07:00
Gravita
7a1746281d [REFACTOR] Moving to log4j2 logger finally 2021-05-10 14:51:52 +07:00
Gravita
a4596b8c2f [REFACTOR] Moving to log4j2 logger 2021-05-10 14:34:27 +07:00
Gravita
7277aba7ee [FIX] makeProfile fix 2021-05-10 13:52:12 +07:00
Gravita
afe3dd543e [FEATURE] Support mittot/generate option in downloadClient 2021-05-07 20:19:02 +07:00
Gravita
21a203356f [FEATURE] Manual create ClientProfile 2021-05-07 19:25:04 +07:00
Gravita
9f8cd7070c [FIX] Return shuffle in AsyncDownloader 2021-05-07 16:05:56 +07:00
Gravita
43c45f6990 [FIX] DiscordIntegration NPE fix 2021-05-06 01:49:06 +07:00
Gravita
6482ab2c46 [FIX] DiscordIntegration logger fix 2021-05-06 01:31:41 +07:00
Gravita
d837658d36 [FIX] ComponentCommand 2021-05-04 20:28:26 +07:00
Gravita
5b5da76bff [FIX] Crash corrupted sessions.json 2021-05-04 19:39:10 +07:00
Gravita
ea7159dba6 [FEATURE] Print java version in version command 2021-05-04 19:26:00 +07:00
Gravita
f188378fd6 [FEATURE] Print launcher modules in modules command 2021-05-04 19:13:33 +07:00
Gravita
ae82964a24 [FIX] Duplicate profileUUID error 2021-05-04 18:08:13 +07:00
Gravita
e5f714e0f5 [ANY] Обновлены модули 2021-05-02 16:11:06 +07:00
Gravita
abdbf5039d [FIX] OptionalView 2021-05-02 16:10:33 +07:00
Gravita
8ab1dda430 [FIX] Double ProGuard task 2021-04-30 00:39:25 +07:00
Gravita
7724c904e5 [FEATURE] Method getClassLoader() in ClientService 2021-04-30 00:36:43 +07:00
Gravita
d522976f0b [FEATURE] Support icon in notifications 2021-04-30 00:20:10 +07:00
Gravita
a34b2c206b [FEATURE] Support "Runtime in Client" 2021-04-29 23:56:48 +07:00
Gravita
37ec6b525c [FIX] Profile UUID null error 2021-04-29 23:35:56 +07:00
Gravita
1b7e463ddb [FIX] Slf4j detect fix 2021-04-29 22:50:31 +07:00
Gravita
9f05594915 [FEATURE] Profile Settings 2021-04-26 23:22:32 +07:00
Gravita
68d7c0a947 [FEATURE] Support encrypted runtime 2021-04-26 22:21:28 +07:00
Gravita
203fc638dc Revert "[FEATURE] SizedFile Collection shuffle"
This reverts commit 2058e3f38b.
2021-04-23 22:03:28 +07:00
Gravita
2058e3f38b [FEATURE] SizedFile Collection shuffle 2021-04-23 15:46:46 +07:00
Gravita
2def9c20d9 [FIX] Support SPI in Launcher 2021-04-22 21:03:43 +07:00
Gravita
54cb175ae7 [FEATURE] HttpClient in HttpRequest (LauncherCore) 2021-04-18 18:57:02 +07:00
Gravita
5d35c0823e [REFACTOR] Switch to log4j 2 2021-04-18 18:35:46 +07:00
Gravita
66db1eb861 [FEATURE] Arch/Debian openjfx search 2021-04-18 18:03:47 +07:00
Gravita
64661abf0f [ANY] gradle.build 5.2.0-SNAPSHOT 2021-04-15 22:14:09 +07:00
Gravita
5bff221e7e [FEATURE] ClientLauncherWrapper reworked 2021-04-15 22:02:30 +07:00
Gravita
92e2a6c86d [FIX] NPE в clients 2021-04-15 21:02:46 +07:00
Gravita
5bbe63f78c [FIX] Bug fixes 2021-04-14 17:22:07 +07:00
Gravita
7886cce6f8 [FEATURE] Support 2FA in JsonAuthProvider 2021-04-13 21:27:37 +07:00
Gravita
d1e671a935 [FEATURE] AuthDetails support 2021-04-13 19:39:02 +07:00
Gravita
40e4949c47 [FEATURE] Use tmpdir for build 2021-04-13 18:23:39 +07:00
Gravita
5547909c16 [FEATURE][EXPERIMENTAL] LogHelper slf4j bridge 2021-04-13 18:04:00 +07:00
Gravita
3b6997a723 [FEATURE][EXPERIMENTAL] Log4j logging 2021-04-13 16:49:11 +07:00
Gravita
169df2a79a [FEATURE] OAuth type 2021-04-13 14:52:21 +07:00
Gravita
e5e8fa1463 [FIX] ProGuard pipeline fix 2021-04-07 15:48:21 +07:00
Gravita
7015d45088 [FEATURE] AuthRSAPassword AuthAESPassword 2021-04-07 15:20:09 +07:00
Gravita
70c2e1c1af [REFACTOR] Module init refactoring 2021-04-07 14:12:13 +07:00
Gravita
36ea9a9d3d [FEATURE] KeyAgreementManager 2021-04-07 14:00:35 +07:00
Gravita
e9b14c921c [FEATURE] KeyAgreementManager 2021-04-07 14:00:30 +07:00
Gravita
92f19ffc5d [ANY] Обновлены модули 2021-04-06 23:22:55 +07:00
Gravita
6c37dd6ed2 [FEATURE] Version comparing 2021-04-06 22:36:43 +07:00
Gravita
f1331b6d5d [REFACTOR] Mini refactoring 2021-04-06 17:39:14 +07:00
Gravita
1cb2369bfa [FIX] Bug fixes 2021-04-01 00:24:36 +07:00
Gravita
edbed3db1d [FIX] Remove default .crt file 2021-03-30 21:44:19 +07:00
Gravita
dc51ca1c9c [FEATURE] PrepareMode 2021-03-30 16:13:41 +07:00
Gravita
f8af81b141 [FIX] Compile fix 2021-03-27 17:35:12 +07:00
Gravita
fc13442c89 [FEATURE] Updates and profiles sync in new thread 2021-03-27 17:22:46 +07:00
Gravita
63891cd743 [FEATURE] ProGuard Component 2021-03-27 17:22:46 +07:00
Gravita
1452b59fa5 [FIX] Params Parsing 2021-03-27 17:22:46 +07:00
Gravita
0751d2fd28 [FEATURE] RemoteControlModule 2021-03-27 17:22:46 +07:00
Gravita
afc84ebae8 [FEATURE] RemoteControlModule 2021-03-27 17:22:46 +07:00
Gravita
6127e92ce5 [FIX] CommonHelper.newScriptEngine 2021-03-27 17:22:46 +07:00
Gravita
cd5c78c413 [ANY] 5.2.0 dev 2021-03-27 17:22:46 +07:00
Gravita
d7e44700f6 [ANY] Обновлены модули 2021-03-27 17:22:46 +07:00
Gravita
0b1f3f66af [REFACTOR] IDEA Code Reformat 2021-03-27 17:22:46 +07:00
Gravita
3b839da3d3 [ANY] Обновлены модули 2021-03-27 17:22:46 +07:00
Gravita
fb3241dd52 [REFACTOR] IDEA Clean 2021-03-27 17:22:46 +07:00
Gravita
0c97fdf45c [REFACTOR] IDEA Inspect 2021-03-27 17:22:46 +07:00
Gravita
6da27bdf99 [FEATURE] ServerWrapperSetup Agent 2021-03-27 17:22:46 +07:00
Zaxar163
25d88ba30d
Merge pull request #438 from ijo42/patch-1
[STYLE] Опечатка в названии команды
2021-03-21 21:12:24 +03:00
ijo42
155a60053c
[STYLE] Опечатка в названии команды 2021-03-21 15:12:56 +05:00
Gravita
bdd402051a [ANY] Обновлены модули 2021-03-19 23:23:46 +07:00
Gravita
bbdfc204e7 [FEATURE] Support GraphicCard info 2021-03-19 23:23:21 +07:00
Gravita
b545c099eb [FEATURE] Grand libraries update 2021-03-19 23:02:03 +07:00
Gravita
344c61eaee [FEATURE] DebugMain 2021-03-10 00:32:52 +07:00
Gravita
1e7f5c9eb4 [FEATURE] DiscordGame module fix 2021-03-06 17:28:37 +07:00
Gravita
9d06689f35 [FEATURE] CertificateService 2021-03-06 17:19:03 +07:00
Gravit'a
8a016b7d46 [FIX] DiscordGame 2021-02-28 00:59:54 +07:00
Gravit'a
6e54fcd2fb [FIX] DiscordGame fix 2021-02-27 20:16:09 +07:00
Gravita
4baccf5b77 [FIX] OneLauncherModule bug fix 2021-02-27 17:55:07 +07:00
Gravita
0ff47e9339 [FEATURE] DiscordIntegration module 2021-02-27 17:33:53 +07:00
Gravita
3ff39834d8 [FEATURE] OneLauncherModule 2021-02-27 02:57:26 +07:00
Gravita
75a965120a [FEATURE][EXPERIMENTAL] DiscordGame module 2021-02-15 23:42:51 +07:00
Gravita
99fb4002da [ANY] 5.1.11 dev 2021-02-12 21:43:02 +07:00
Gravita
8ff31888e9 Merge tag 'v5.1.10' into dev
5.1.10 Stable
2021-02-12 21:34:27 +07:00
Gravita
610593b0f0 Merge branch 'release/5.1.10' 2021-02-12 21:34:17 +07:00
Gravita
0e505a1feb [ANY] 5.1.10b2 stable 2021-02-12 21:33:49 +07:00
Gravita
b8be6d4ef6 [FEATURE] LaunchServer save command 2021-02-12 21:33:05 +07:00
Gravita
3cd9ddee0d [FIX] autoDump и завершение работы 2021-02-12 21:19:49 +07:00
Gravita
9084361a38 [FIX] Защита от дурака (отсутствие библиотек) 2021-02-12 20:56:55 +07:00
Gravita
f6d954827e [FIX] GitHub Actions 2021-02-08 18:56:26 +07:00
Gravita
7154989827 [FIX] OutOfMemory при скачивании с certificatePinning 2021-02-08 17:58:24 +07:00
Gravita
3ccb0c5c8a [FIX] Оптимизация certificatePinning 2021-02-08 14:42:07 +07:00
Gravita
bca195ca96 [ANY] Release 5.0.10 stable 2021-02-07 22:01:26 +07:00
Gravita
f4ef2b25e1 [ANY] Обновлены модули 2021-02-07 21:58:32 +07:00
Gravita
87cd668522 [ANY] Deprecated AutoSaveSessions API 2021-02-07 21:44:14 +07:00
Gravita
d0f13743ed [FEATURE] Улучшение API Client/FeaturesManager 2021-02-07 21:42:04 +07:00
Gravita
bac794b912 [ANY] Обновлены модули 2021-02-07 21:26:24 +07:00
Gravita
f780db3e57 [FEATURE] Module FileAuthSystem 2021-01-31 02:01:54 +07:00
Gravita
4592eee953 [FEATURE] CertificatePinning, AGENT, min/max/recommend Java and other 2021-01-29 00:13:21 +07:00
Gravita
78265a7576 [FIX] OptionalFile permissions 2021-01-28 19:15:42 +07:00
Gravita
7f86d65d9c [FEATURE] 1.16.5 2021-01-28 19:10:37 +07:00
Gravita
b8ca4daadf [FEATURE] ClientProfile Properties API (issue #433) 2021-01-28 19:08:44 +07:00
Gravita
e2960393fb [ANY] Обновлены модуля 2021-01-14 23:56:39 +07:00
Gravita
84c459bb0d [FIX] PaperSpigot no patch worked 2021-01-14 23:55:55 +07:00
Gravita
4cc22faa55 [ANY] 5.1.10 dev 2021-01-09 21:13:17 +07:00
Gravita
5d39e168cd [FEATURE] Certificate verification refactoring 2021-01-09 21:06:35 +07:00
Gravita
a4927dae4d [FIX] MemorySessionStorage autoDump 2020-12-28 21:39:41 +07:00
Gravita
669a7eb391 [FIX] clientSet & uuidIndex serialize 2020-12-28 21:29:12 +07:00
Gravita
9d6c9c0abf Merge tag 'v5.1.9' into dev
5.1.9 Stable
2020-12-25 11:45:55 +07:00
Gravita
1b2be8389a Merge branch 'release/5.1.9' 2020-12-25 11:45:35 +07:00
Gravita
098a429c13 [ANY] 5.1.9-stable 2020-12-25 11:45:17 +07:00
Gravita
3cf5ee6d2c [FIX] NPE в ProfileByUUIDResponse 2020-12-22 15:41:21 +07:00
Gravita
b897fa55fd [FIX] Invalid field InstantSeconds for get() method, use getLong() instead 2020-12-21 02:38:24 +07:00
Gravita
de95a66742 [FIX] CorruptedWebSocketFrameException: Max frame length of 65536 has been exceeded 2020-12-20 18:12:28 +07:00
Gravita
826eb4c80f [FEATURE] FeaturesResponse and FeaturesRequest 2020-12-20 15:31:17 +07:00
Gravita
cb5535b8ec [FEATURE] FeaturesManager 2020-12-20 15:29:20 +07:00
Gravita
4f5c3370ac [ANY] Dockerfile 2020-12-19 21:24:28 +07:00
Gravita
cafe8df3a9 [FEATURE] final SessionStorage API 2020-12-17 23:56:11 +07:00
Gravita
ce70cab736 [FEATURE] serverName in ServerWrapper 2020-12-17 17:54:28 +07:00
Gravita
62c8ca878e [FIX] SessionStorage NPE 2020-12-17 01:39:24 +07:00
Gravita
aced575921 [FIX] SessionManager NPE 2020-12-16 14:07:46 +07:00
Gravita
c596c30ff6 [ANY] New Sessions system completed 2020-12-15 19:27:38 +07:00
KR33PY
acf2d2d9cd
[FIX] NPE в BatchProfileByUsernameRequest (#424) 2020-12-01 19:44:16 +07:00
Gravit
c24b74ab91
Create codeql-analysis.yml (#423)
* Create codeql-analysis.yml

* Update codeql-analysis.yml
2020-12-01 16:12:16 +07:00
Gravit
08b23db5c6
[FIX] Small fix 2020-11-23 22:30:04 +07:00
Gravit
f971356955
[FEATURE] pingServersCommand 2020-11-22 17:33:37 +07:00
Gravit
06aa6976e0
[FIX] Small fixes 2020-11-22 16:09:54 +07:00
Gravit
2688270fa0
[FEATURE] Advanced Client API 2020-11-21 21:30:21 +07:00
Gravit
5e048905cc
[FEATURE] Netty maxWebSocketRequestBytes 2020-11-21 20:33:10 +07:00
xxDark
ae2ef5e7c2
[FIX] Remove Xms to make peeps stop crying (#422) 2020-11-21 20:10:04 +07:00
Gravit
0f8f51ea97
[FEATURE] SessionManager: uuidIndex 2020-11-21 20:08:39 +07:00
Gravit
447866d001
[FIX] daoObject null 2020-11-08 23:27:23 +07:00
XuTpbl_JIuC
86d3b3365d
[FEATURE] Добавлена поддержка 1.16.4 (#420) 2020-11-03 11:56:38 +03:00
Gravit
fb5609b864
[FIX] ClientProcessLaunchEvent 2020-10-28 01:55:08 +07:00
Gravit
aee051627b
[FIX] NPE in SessionManager 2020-10-28 00:55:32 +07:00
Gravit
94e60c3e51
[FIX] MemoryHWIDProvider 2020-10-27 20:13:31 +07:00
Gravit
18b23b195a
[FEATURE][EXPERIMENTAL] Работа с сессиями 2020-10-26 23:17:05 +07:00
Gravit
3a80a74912
[ANY] Обновлены модули 2020-10-26 21:23:36 +07:00
Gravit
2164fda288
[ANY] Обновлены модули 2020-10-26 20:42:08 +07:00
Gravit
d9e71d08d7
[ANY] 5.1.9-dev 2020-10-26 20:39:39 +07:00
Zaxar163
a1cdd6e22c
[ANY] Обновлены модули. 2020-10-23 19:48:45 +03:00
Gravit
3de3ac24a9
Merge tag 'v5.1.8' into dev
5.1.8 Stable
2020-09-29 01:35:36 +07:00
681 changed files with 20987 additions and 14217 deletions

86
.gitattributes vendored
View file

@ -1,26 +1,78 @@
* text eol=lf * text=auto eol=lf
*.bat text eol=crlf *.[cC][mM][dD] text eol=crlf
*.sh text eol=lf *.[bB][aA][tT] text eol=crlf
*.[pP][sS]1 text eol=crlf
*.[sS][hH] text eol=lf
*.patch text eol=lf *.patch text eol=lf
*.java text eol=lf
*.scala text eol=lf
*.groovy text eol=lf
*.gradle text eol=crlf
gradle.properties text eol=crlf
/gradle/wrapper/gradle-wrapper.properties text eol=crlf
*.cfg text eol=lf
*.png binary *.png binary
*.jar binary
*.war binary
*.lzma binary *.lzma binary
*.zip binary *.zip binary
*.gzip binary *.gzip binary
*.dll binary
*.so binary
*.exe binary *.exe binary
*.ico binary
*.eot binary
*.ttf binary
*.woff binary
*.woff2 binary
*.a binary
*.lib binary
*.icns binary
*.jpg binary
*.jpeg binary
*.gif binary
*.mov binary
*.mp4 binary
*.mp3 binary
*.flv binary
*.fla binary
*.swf binary
*.gz binary
*.tar binary
*.tar.gz binary
*.7z binary
*.pyc binary
*.gpg binary
*.bin binary
*.gitattributes text eol=crlf *.gitattributes text
*.gitignore text eol=crlf .gitignore text
# Java sources
*.java text diff=java
*.kt text diff=kotlin
*.groovy text diff=java
*.scala text diff=java
*.gradle text diff=java
*.gradle.kts text diff=kotlin
# These files are text and should be normalized (Convert crlf => lf)
*.css text diff=css
*.scss text diff=css
*.sass text
*.df text
*.htm text diff=html
*.html text diff=html
*.js text
*.jsp text
*.jspf text
*.jspx text
*.properties text
*.tld text
*.tag text
*.tagx text
*.xml text
# These files are binary and should be left untouched
# (binary is a macro for -text -diff)
*.class binary
*.dll binary
*.ear binary
*.jar binary
*.so binary
*.war binary
*.jks binary
mvnw text eol=lf
gradlew text eol=lf

View file

@ -1,29 +1,26 @@
name: push name: push
on: on: push
push:
create:
tags:
- v*
jobs: jobs:
launcher: launcher:
name: Launcher name: Launcher
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v4
with: with:
submodules: recursive submodules: recursive
- name: Cache Gradle - name: Cache Gradle
uses: actions/cache@v1 uses: actions/cache@v4
with: with:
path: ~/.gradle/caches path: ~/.gradle/caches
key: gravit-${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }}-launcher key: gravit-${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }}-launcher
- name: Set up JDK 11 - name: Set up JDK 21
uses: actions/setup-java@v1 uses: actions/setup-java@v4
with: with:
java-version: 11 java-version: 21
distribution: temurin
- name: Grant execute permission for gradlew - name: Grant execute permission for gradlew
run: chmod +x gradlew run: chmod +x gradlew
@ -31,52 +28,52 @@ jobs:
- name: Build with Gradle - name: Build with Gradle
run: ./gradlew build run: ./gradlew build
- name: Generate and submit dependency graph
uses: gradle/actions/dependency-submission@417ae3ccd767c252f5661f1ace9f835f9654f2b5
- name: Create artifacts - name: Create artifacts
run: | run: |
mkdir -p artifacts/modules mkdir -p artifacts/modules
cd LaunchServer/build/libs/ cd LaunchServer/build/libs
zip -r -9 ../../../artifacts/libraries.zip * -x "LaunchServer.jar" -x "LaunchServer-clean.jar" zip -r -9 ../../../artifacts/libraries.zip * -x "LaunchServer.jar" -x "LaunchServer-clean.jar"
cp LaunchServer.jar ../../../artifacts/LaunchServer.jar cp LaunchServer.jar ../../../artifacts/LaunchServer.jar
cd ../../../ServerWrapper/build/libs cd ../../..
cp ServerWrapper.jar ../../../artifacts/ServerWrapper.jar cp ServerWrapper/build/libs/ServerWrapper.jar artifacts/ServerWrapper.jar
cd ../../../LauncherAuthlib/build/libs cp LauncherAuthlib/build/libs/LauncherAuthlib.jar artifacts/LauncherAuthlib.jar || true
cp LauncherAuthlib.jar ../../../artifacts/LauncherAuthlib.jar
cd ../../../
cp modules/*_module/build/libs/*.jar artifacts/modules || true cp modules/*_module/build/libs/*.jar artifacts/modules || true
cp modules/*_swmodule/build/libs/*.jar artifacts/modules || true
cp modules/*_lmodule/build/libs/*.jar artifacts/modules || true cp modules/*_lmodule/build/libs/*.jar artifacts/modules || true
- name: Upload artifacts - name: Upload artifacts
uses: actions/upload-artifact@v1 uses: actions/upload-artifact@v4
with: with:
name: Launcher name: Launcher
path: artifacts path: artifacts
- name: Get version value, set to env
if: startsWith(github.event.ref, 'refs/tags')
run: echo "LAUNCHER_VERSION=$(echo ${{ github.event.ref }} | awk -F\/ '{print $3}')" >> $GITHUB_ENV
- name: Prebuild release files
if: startsWith(github.event.ref, 'refs/tags')
run: |
cd artifacts
zip -r -9 Release.zip *
zip -j -9 LaunchServerModules.zip ../modules/*_module/build/libs/*.jar
zip -j -9 LauncherModules.zip ../modules/*_lmodule/build/libs/*.jar
cd ../LaunchServer/build/libs
zip -r -9 ../../../artifacts/LauncherBase.zip * -x "LaunchServer-clean.jar"
- name: Create release - name: Create release
id: create_release id: create_release
uses: actions/create-release@v1 uses: softprops/action-gh-release@v2
if: github.event_name == 'create' if: startsWith(github.event.ref, 'refs/tags')
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Список настроек тута: https://github.com/softprops/action-gh-release#-customizing
# Можно сделать пуш описания релиза из файла
with: with:
tag_name: ${{ github.ref }} name: GravitLauncher ${{ env.LAUNCHER_VERSION }}
release_name: GravitLauncher ${{ github.ref }}
draft: false draft: false
prerelease: false prerelease: false
files: |
- name: Pack release artifacts/*
if: github.event_name == 'create'
run: |
cd artifacts/
zip -r -9 ../Release.zip *
- name: Upload release
if: github.event_name == 'create'
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./Release.zip
asset_name: Release.zip
asset_content_type: application/zip

View file

@ -1,4 +1,5 @@
# GravitLauncher GitConvention # # GravitLauncher GitConvention #
Цель конвенции — внедрить простые, прозрачные и эффективные правила работы с Git. Цель конвенции — внедрить простые, прозрачные и эффективные правила работы с Git.
Разработка GravitLauncher идёт на базе [Git Flow](https://leanpub.com/git-flow/read). Подробности ниже. Разработка GravitLauncher идёт на базе [Git Flow](https://leanpub.com/git-flow/read). Подробности ниже.
@ -14,29 +15,33 @@
| **bugfix-*** | Исправляет баг нового функционала | **release** | *bugfix-auth* | | **bugfix-*** | Исправляет баг нового функционала | **release** | *bugfix-auth* |
| **feature-*** | Добавляет новую возможность | **develop** | *feature-auth* | | **feature-*** | Добавляет новую возможность | **develop** | *feature-auth* |
| **hotfix-*** | Вносит срочное исправление для production-а | **master** | *hotfix-auth* | | **hotfix-*** | Вносит срочное исправление для production-а | **master** | *hotfix-auth* |
----- -----
![Image of GitFlow](https://i.ytimg.com/vi/w2r0oLFtXAw/maxresdefault.jpg) ![Image of GitFlow](https://i.ytimg.com/vi/w2r0oLFtXAw/maxresdefault.jpg)
----- -----
## Коммиты ## ## Коммиты ##
**Основные правила:** **Основные правила:**
1. Все коммиты должны быть на русском языке.
1. Все коммиты должны быть на английском языке.
2. Запрещено использовать прошедшее время. 2. Запрещено использовать прошедшее время.
3. Обязательно должен быть использован префикс. 3. Обязательно должен быть использован префикс.
4. В конце не должно быть лишнего знака препинания. 4. В конце не должно быть лишнего знака препинания.
5. Длина любой части не должна превышать 100 символов. 5. Длина любой части не должна превышать 100 символов.
**Структура:** **Структура:**
``` ```
[Префикс] <Сообщение> [Префикс] <Сообщение>
``` ```
| Префикс | Значение | Пример | | Префикс | Значение | Пример |
| ------- | -------- | ------ | | ------- | -------- | ------ |
| **[FIX]** | Всё, что касается исправления багов | [FIX] Баг с неудачной авторизацией | | **[FIX]** | Всё, что касается исправления багов | [FIX] Bug with failed authorization |
| **[DOCS]** | Всё, что касается документации | [DOCS] Документирование API авторизации | | **[DOCS]** | Всё, что касается документации | [DOCS] Documenting Authorization API |
| **[FEATURE]** | Всё, что касается новых возможностей | [FEATURE] 2FA при авторизации | | **[FEATURE]** | Всё, что касается новых возможностей | [FEATURE] 2FA on authorization |
| **[STYLE]** | Всё, что касается опечаток и форматирования | [STYLE] Опечатки в модуле авторизации | | **[STYLE]** | Всё, что касается опечаток и форматирования | [STYLE] Typos in the authorization module |
| **[REFACTOR]** | Всё, что касается рефакторинга | [REFACTOR] Переход на EDA в модуле авторизации | | **[REFACTOR]** | Всё, что касается рефакторинга | [REFACTOR] Switching to EDA in the authorization module |
| **[TEST]** | Всё, что касается тестирования | [TEST] Покрытие модуля авторизации тестами | | **[TEST]** | Всё, что касается тестирования | [TEST] Coverage of the authorization module with tests |
| **[ANY]** | Всё, что не подходит к предыдущему. | [ANY] Подключение Travis CI | | **[ANY]** | Всё, что не подходит к предыдущему. | [ANY] Connecting Travis CI |

12
Dockerfile Normal file
View file

@ -0,0 +1,12 @@
FROM ubuntu:latest
RUN apt-get update && apt-get install -y osslsigncode openjdk-11-jdk unzip jq screen
ADD https://download2.gluonhq.com/openjfx/11.0.2/openjfx-11.0.2_linux-x64_bin-jmods.zip .
RUN unzip openjfx-11.0.2_linux-x64_bin-jmods.zip && mv javafx-jmods-11.0.2/* /usr/lib/jvm/java-11-openjdk-amd64/jmods/ && rmdir javafx-jmods-11.0.2 && rm openjfx-11.0.2_linux-x64_bin-jmods.zip
RUN mkdir ./libraries ./launcher-libraries ./launcher-libraries-compile ./compat ./compat/modules
COPY ./LaunchServer/build/libs/LaunchServer.jar .
COPY ./LaunchServer/build/libs/libraries ./libraries
COPY ./LaunchServer/build/libs/launcher-libraries ./launcher-libraries
COPY ./LaunchServer/build/libs/launcher-libraries-compile ./launcher-libraries-compile
COPY ./compat/authlib/authlib-clean.jar ./LauncherAuthlib/build/libs/* ./ServerWrapper/build/libs/ServerWrapper.jar ./compat/
COPY ./modules/*_module/build/libs/* ./modules/*_lmodule/build/libs/* ./compat/modules/
CMD screen -DmS launchserver java -javaagent:LaunchServer.jar -jar LaunchServer.jar

View file

@ -1,4 +1,4 @@
def mainClassName = "pro.gravit.launchserver.LaunchServerStarter" def mainClassName = "pro.gravit.launchserver.Main"
def mainAgentName = "pro.gravit.launchserver.StarterAgent" def mainAgentName = "pro.gravit.launchserver.StarterAgent"
evaluationDependsOn(':Launcher') evaluationDependsOn(':Launcher')
@ -8,22 +8,29 @@
url "https://oss.sonatype.org/content/repositories/snapshots" url "https://oss.sonatype.org/content/repositories/snapshots"
} }
maven { maven {
url "https://maven.geomajas.org/" url "https://jcenter.bintray.com/"
}
maven {
url "https://jitpack.io/"
}
maven {
url 'https://maven.gravit-support.ru/repository/jitpack'
credentials {
username = 'gravitlauncher'
password = 'gravitlauncher'
}
} }
} }
sourceCompatibility = '11' sourceCompatibility = '21'
targetCompatibility = '11' targetCompatibility = '21'
configurations { configurations {
compileOnlyA
bundleOnly bundleOnly
bundle bundle
hikari
pack pack
launch4j
bundleOnly.extendsFrom bundle bundleOnly.extendsFrom bundle
api.extendsFrom bundle, hikari, pack, launch4j api.extendsFrom bundle, pack
} }
jar { jar {
@ -34,9 +41,6 @@
manifest.attributes("Main-Class": mainClassName, manifest.attributes("Main-Class": mainClassName,
"Premain-Class": mainAgentName, "Premain-Class": mainAgentName,
"Multi-Release": "true", "Multi-Release": "true",
"Can-Redefine-Classes": "true",
"Can-Retransform-Classes": "true",
"Can-Set-Native-Method-Prefix": "true"
) )
} }
@ -47,23 +51,21 @@
} }
} }
task sourcesJar(type: Jar) { tasks.register('sourcesJar', Jar) {
from sourceSets.main.allJava from sourceSets.main.allJava
archiveClassifier = 'sources' archiveClassifier.set('sources')
} }
task javadocJar(type: Jar) { tasks.register('javadocJar', Jar) {
from javadoc from javadoc
archiveClassifier = 'javadoc' archiveClassifier.set('javadoc')
} }
task cleanjar(type: Jar, dependsOn: jar) { tasks.register('cleanjar', Jar) {
classifier = 'clean' dependsOn jar
archiveClassifier.set('clean')
manifest.attributes("Main-Class": mainClassName, manifest.attributes("Main-Class": mainClassName,
"Premain-Class": mainAgentName, "Automatic-Module-Name": "launchserver"
"Can-Redefine-Classes": "true",
"Can-Retransform-Classes": "true",
"Can-Set-Native-Method-Prefix": "true"
) )
from sourceSets.main.output from sourceSets.main.output
} }
@ -71,100 +73,71 @@ task cleanjar(type: Jar, dependsOn: jar) {
dependencies { dependencies {
pack project(':LauncherAPI') pack project(':LauncherAPI')
bundle group: 'me.tongfei', name: 'progressbar', version: '0.10.1'
bundle group: 'org.fusesource.jansi', name: 'jansi', version: rootProject['verJansi'] bundle group: 'org.fusesource.jansi', name: 'jansi', version: rootProject['verJansi']
bundle group: 'org.jline', name: 'jline', version: rootProject['verJline'] bundle group: 'org.jline', name: 'jline', version: rootProject['verJline']
bundle group: 'org.jline', name: 'jline-reader', version: rootProject['verJline'] bundle group: 'org.jline', name: 'jline-reader', version: rootProject['verJline']
bundle group: 'org.jline', name: 'jline-terminal', version: rootProject['verJline'] bundle group: 'org.jline', name: 'jline-terminal', version: rootProject['verJline']
bundle group: 'org.bouncycastle', name: 'bcpkix-jdk15on', version: rootProject['verBcpkix'] bundle group: 'org.bouncycastle', name: 'bcprov-jdk18on', version: rootProject['verBcpkix']
bundle group: 'org.bouncycastle', name: 'bcpkix-jdk18on', version: rootProject['verBcpkix']
bundle group: 'org.ow2.asm', name: 'asm-commons', version: rootProject['verAsm'] bundle group: 'org.ow2.asm', name: 'asm-commons', version: rootProject['verAsm']
bundle group: 'io.netty', name: 'netty-all', version: rootProject['verNetty'] bundle group: 'io.netty', name: 'netty-codec-http', version: rootProject['verNetty']
bundle group: 'org.slf4j', name: 'slf4j-simple', version: rootProject['verSlf4j'] bundle group: 'io.netty', name: 'netty-transport-classes-epoll', version: rootProject['verNetty']
bundle group: 'io.netty', name: 'netty-transport-native-epoll', version: rootProject['verNetty'], classifier: 'linux-x86_64'
bundle group: 'io.netty', name: 'netty-transport-native-epoll', version: rootProject['verNetty'], classifier: 'linux-aarch_64'
bundle group: 'io.netty', name: 'netty-transport-classes-io_uring', version: rootProject['verNetty']
bundle group: 'io.netty', name: 'netty-transport-native-io_uring', version: rootProject['verNetty'], classifier: 'linux-x86_64'
bundle group: 'io.netty', name: 'netty-transport-native-io_uring', version: rootProject['verNetty'], classifier: 'linux-aarch_64'
// Netty
bundle 'org.jboss.marshalling:jboss-marshalling:1.4.11.Final'
bundle 'com.google.protobuf.nano:protobuf-javanano:3.1.0'
//
bundle group: 'org.slf4j', name: 'slf4j-api', version: rootProject['verSlf4j'] bundle group: 'org.slf4j', name: 'slf4j-api', version: rootProject['verSlf4j']
bundle group: 'org.hibernate', name: 'hibernate-core', version: rootProject['verHibernate'] bundle group: 'com.mysql', name: 'mysql-connector-j', version: rootProject['verMySQLConn']
bundle group: 'org.hibernate', name: 'hibernate-hikaricp', version: rootProject['verHibernate'] bundle group: 'org.mariadb.jdbc', name: 'mariadb-java-client', version: rootProject['verMariaDBConn']
bundle group: 'mysql', name: 'mysql-connector-java', version: rootProject['verMySQLConn']
bundle group: 'org.postgresql', name: 'postgresql', version: rootProject['verPostgreSQLConn'] bundle group: 'org.postgresql', name: 'postgresql', version: rootProject['verPostgreSQLConn']
bundle group: 'net.sf.proguard', name: 'proguard-base', version: rootProject['verProguard'] bundle group: 'com.h2database', name: 'h2', version: rootProject['verH2Conn']
bundle group: 'com.guardsquare', name: 'proguard-base', version: rootProject['verProguard']
bundle group: 'org.apache.logging.log4j', name: 'log4j-core', version: rootProject['verLog4j']
bundle group: 'org.apache.logging.log4j', name: 'log4j-slf4j2-impl', version: rootProject['verLog4j']
bundle group: 'io.jsonwebtoken', name: 'jjwt-api', version: rootProject['verJwt']
bundle group: 'io.jsonwebtoken', name: 'jjwt-impl', version: rootProject['verJwt']
bundle group: 'io.jsonwebtoken', name: 'jjwt-gson', version: rootProject['verJwt']
bundle group: 'com.google.code.gson', name: 'gson', version: rootProject['verGson']
annotationProcessor(group: 'org.apache.logging.log4j', name: 'log4j-core', version: rootProject['verLog4j'])
testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter', version: rootProject['verJunit'] testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter', version: rootProject['verJunit']
hikari 'io.micrometer:micrometer-core:1.0.6' bundle 'io.micrometer:micrometer-core:1.14.4'
hikari('com.zaxxer:HikariCP:3.4.1') { bundle('com.zaxxer:HikariCP:6.2.1') {
exclude group: 'javassist' exclude group: 'javassist'
exclude group: 'io.micrometer' exclude group: 'io.micrometer'
exclude group: 'org.slf4j' exclude group: 'org.slf4j'
} }
launch4j('net.sf.launch4j:launch4j:' + rootProject['verLaunch4j']) {
exclude group: 'org.apache.ant'
exclude group: 'net.java.abeille'
exclude group: 'foxtrot'
exclude group: 'com.jgoodies'
exclude group: 'org.slf4j'
}
launch4j('net.sf.launch4j:launch4j:' + rootProject['verLaunch4j'] + ':workdir-win32') { transitive = false }
launch4j('net.sf.launch4j:launch4j:' + rootProject['verLaunch4j'] + ':workdir-linux64') { transitive = false }
compileOnlyA group: 'com.google.guava', name: 'guava', version: rootProject['verGuavaC']
// Do not update (laggy deps).
compileOnlyA 'log4j:log4j:1.2.17'
compileOnlyA 'org.apache.logging.log4j:log4j-core:2.11.2'
} }
task hikari(type: Copy) { tasks.register('dumpLibs', Copy) {
duplicatesStrategy = 'EXCLUDE' duplicatesStrategy = 'EXCLUDE'
into "$buildDir/libs/libraries/hikaricp"
from configurations.hikari
}
task launch4j(type: Copy) {
duplicatesStrategy = 'EXCLUDE'
into "$buildDir/libs/libraries/launch4j"
from(configurations.launch4j.collect {
it.isDirectory() ? it : ((it.getName().startsWith("launch4j") && it.getName().contains("workdir")) ? zipTree(it) : it)
})
includeEmptyDirs false
eachFile { FileCopyDetails fcp ->
if (fcp.relativePath.pathString.startsWith("launch4j-") &&
fcp.relativePath.pathString.contains("workdir")) {
def segments = fcp.relativePath.segments
def pathSegments = segments[1..-1] as String[]
fcp.relativePath = new RelativePath(!fcp.file.isDirectory(), pathSegments)
} else if (fcp.relativePath.pathString.contains("META-INF")) fcp.exclude()
fcp.mode = 0755
}
}
task dumpLibs(type: Copy) {
duplicatesStrategy = 'EXCLUDE'
dependsOn tasks.hikari, tasks.launch4j
into "$buildDir/libs/libraries" into "$buildDir/libs/libraries"
from configurations.bundleOnly from configurations.bundleOnly
} }
task dumpCompileOnlyLibs(type: Copy) { tasks.register('bundle', Zip) {
duplicatesStrategy = 'EXCLUDE' duplicatesStrategy = 'EXCLUDE'
into "$buildDir/libs/launcher-libraries-compile" dependsOn parent.childProjects.Launcher.tasks.build, tasks.dumpLibs, tasks.jar
from configurations.compileOnlyA
}
task bundle(type: Zip) {
duplicatesStrategy = 'EXCLUDE'
dependsOn parent.childProjects.Launcher.tasks.build, tasks.dumpLibs, tasks.dumpCompileOnlyLibs, tasks.jar
archiveFileName = 'LaunchServer.zip' archiveFileName = 'LaunchServer.zip'
destinationDirectory = file("$buildDir") destinationDirectory = file("$buildDir")
from(tasks.dumpLibs.destinationDir) { into 'libraries' } from(tasks.dumpLibs.destinationDir) { into 'libraries' }
from(tasks.dumpCompileOnlyLibs.destinationDir) { into 'launcher-libraries-compile' }
from(tasks.jar) from(tasks.jar)
from(parent.childProjects.Launcher.tasks.dumpLibs) { into 'launcher-libraries' } from(parent.childProjects.Launcher.tasks.dumpLibs) { into 'launcher-libraries' }
} }
task dumpClientLibs(type: Copy) { tasks.register('dumpClientLibs', Copy) {
dependsOn parent.childProjects.Launcher.tasks.build dependsOn parent.childProjects.Launcher.tasks.build
into "$buildDir/libs/launcher-libraries" into "$buildDir/libs/launcher-libraries"
from parent.childProjects.Launcher.tasks.dumpLibs from parent.childProjects.Launcher.tasks.dumpLibs
} }
assemble.dependsOn tasks.dumpLibs, tasks.dumpCompileOnlyLibs, tasks.dumpClientLibs, tasks.bundle, tasks.cleanjar assemble.dependsOn tasks.dumpLibs, tasks.dumpClientLibs, tasks.bundle, tasks.cleanjar
publishing { publishing {
@ -179,7 +152,7 @@ task dumpClientLibs(type: Copy) {
pom { pom {
name = 'GravitLauncher LaunchServer API' name = 'GravitLauncher LaunchServer API'
description = 'GravitLauncher LaunchServer Module API' description = 'GravitLauncher LaunchServer Module API'
url = 'https://launcher.gravit.pro' url = 'https://gravitlauncher.com'
licenses { licenses {
license { license {
name = 'GNU General Public License, Version 3.0' name = 'GNU General Public License, Version 3.0'
@ -202,7 +175,7 @@ task dumpClientLibs(type: Copy) {
scm { scm {
connection = 'scm:git:https://github.com/GravitLauncher/Launcher.git' connection = 'scm:git:https://github.com/GravitLauncher/Launcher.git'
developerConnection = 'scm:git:ssh://git@github.com:GravitLauncher/Launcher.git' developerConnection = 'scm:git:ssh://git@github.com:GravitLauncher/Launcher.git'
url = 'https://launcher.gravit.pro/' url = 'https://gravitlauncher.com/'
} }
} }
} }

View file

@ -0,0 +1,106 @@
package pro.gravit.launchserver;
import com.google.gson.JsonElement;
import pro.gravit.launcher.base.Launcher;
import pro.gravit.launchserver.helper.HttpHelper;
import java.io.IOException;
import java.lang.reflect.Type;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.time.Duration;
public class HttpRequester {
private transient final HttpClient httpClient = HttpClient.newBuilder().build();
public HttpRequester() {
}
public <T> SimpleErrorHandler<T> makeEH(Class<T> clazz) {
return new SimpleErrorHandler<>(clazz);
}
public <T> SimpleErrorHandler<T> makeEH(Type clazz) {
return new SimpleErrorHandler<>(clazz);
}
public <T> HttpRequest get(String url, String token) {
try {
var requestBuilder = HttpRequest.newBuilder()
.method("GET", HttpRequest.BodyPublishers.noBody())
.uri(new URI(url))
.header("Content-Type", "application/json; charset=UTF-8")
.header("Accept", "application/json")
.timeout(Duration.ofMillis(10000));
if (token != null) {
requestBuilder.header("Authorization", "Bearer ".concat(token));
}
return requestBuilder.build();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public <T> HttpRequest post(String url, T request, String token) {
try {
var requestBuilder = HttpRequest.newBuilder()
.method("POST", HttpRequest.BodyPublishers.ofString(Launcher.gsonManager.gson.toJson(request)))
.uri(new URI(url))
.header("Content-Type", "application/json; charset=UTF-8")
.header("Accept", "application/json")
.timeout(Duration.ofMillis(10000));
if (token != null) {
requestBuilder.header("Authorization", "Bearer ".concat(token));
}
return requestBuilder.build();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public <T> HttpHelper.HttpOptional<T, SimpleError> send(HttpRequest request, Class<T> clazz) throws IOException {
return HttpHelper.send(httpClient, request, makeEH(clazz));
}
public <T> HttpHelper.HttpOptional<T, SimpleError> send(HttpRequest request, Type type) throws IOException {
return HttpHelper.send(httpClient, request, makeEH(type));
}
public static class SimpleErrorHandler<T> implements HttpHelper.HttpJsonErrorHandler<T, SimpleError> {
private final Type type;
private SimpleErrorHandler(Type type) {
this.type = type;
}
@Override
public HttpHelper.HttpOptional<T, SimpleError> applyJson(JsonElement response, int statusCode) {
if (statusCode < 200 || statusCode >= 300) {
return new HttpHelper.HttpOptional<>(null, Launcher.gsonManager.gson.fromJson(response, SimpleError.class), statusCode);
}
if (type == Void.class) {
return new HttpHelper.HttpOptional<>(null, null, statusCode);
}
return new HttpHelper.HttpOptional<>(Launcher.gsonManager.gson.fromJson(response, type), null, statusCode);
}
}
public static class SimpleError {
public String error;
public int code;
public SimpleError(String error) {
this.error = error;
}
@Override
public String toString() {
return "SimpleError{" +
"error='" + error + '\'' +
", code=" + code +
'}';
}
}
}

View file

@ -1,196 +1,174 @@
package pro.gravit.launchserver; package pro.gravit.launchserver;
import org.bouncycastle.crypto.util.PrivateKeyFactory; import org.apache.logging.log4j.LogManager;
import org.bouncycastle.operator.OperatorCreationException; import org.apache.logging.log4j.Logger;
import pro.gravit.launcher.Launcher; import pro.gravit.launcher.base.events.RequestEvent;
import pro.gravit.launcher.NeedGarbageCollection; import pro.gravit.launcher.base.events.request.ProfilesRequestEvent;
import pro.gravit.launcher.hasher.HashedDir; import pro.gravit.launcher.base.modules.events.ClosePhase;
import pro.gravit.launcher.managers.ConfigManager; import pro.gravit.launcher.base.profiles.ClientProfile;
import pro.gravit.launcher.managers.GarbageManager;
import pro.gravit.launcher.modules.events.ClosePhase;
import pro.gravit.launcher.profiles.ClientProfile;
import pro.gravit.launchserver.auth.AuthProviderPair; import pro.gravit.launchserver.auth.AuthProviderPair;
import pro.gravit.launchserver.binary.*; import pro.gravit.launchserver.auth.core.RejectAuthCoreProvider;
import pro.gravit.launchserver.binary.EXELauncherBinary;
import pro.gravit.launchserver.binary.JARLauncherBinary;
import pro.gravit.launchserver.binary.LauncherBinary;
import pro.gravit.launchserver.config.LaunchServerConfig; import pro.gravit.launchserver.config.LaunchServerConfig;
import pro.gravit.launchserver.config.LaunchServerRuntimeConfig; import pro.gravit.launchserver.config.LaunchServerRuntimeConfig;
import pro.gravit.launchserver.helper.SignHelper;
import pro.gravit.launchserver.launchermodules.LauncherModuleLoader; import pro.gravit.launchserver.launchermodules.LauncherModuleLoader;
import pro.gravit.launchserver.manangers.*; import pro.gravit.launchserver.manangers.*;
import pro.gravit.launchserver.manangers.hook.AuthHookManager; import pro.gravit.launchserver.manangers.hook.AuthHookManager;
import pro.gravit.launchserver.modules.events.LaunchServerFullInitEvent; import pro.gravit.launchserver.modules.events.*;
import pro.gravit.launchserver.modules.events.LaunchServerInitPhase;
import pro.gravit.launchserver.modules.events.LaunchServerPostInitPhase;
import pro.gravit.launchserver.modules.events.NewLaunchServerInstanceEvent;
import pro.gravit.launchserver.modules.impl.LaunchServerModulesManager; import pro.gravit.launchserver.modules.impl.LaunchServerModulesManager;
import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.SocketCommandServer;
import pro.gravit.launchserver.socket.handlers.NettyServerSocketHandler; import pro.gravit.launchserver.socket.handlers.NettyServerSocketHandler;
import pro.gravit.launchserver.socket.response.auth.RestoreResponse;
import pro.gravit.utils.command.Command; import pro.gravit.utils.command.Command;
import pro.gravit.utils.command.CommandHandler; import pro.gravit.utils.command.CommandHandler;
import pro.gravit.utils.command.SubCommand; import pro.gravit.utils.command.SubCommand;
import pro.gravit.utils.helper.CommonHelper; import pro.gravit.utils.helper.CommonHelper;
import pro.gravit.utils.helper.IOHelper;
import pro.gravit.utils.helper.JVMHelper; import pro.gravit.utils.helper.JVMHelper;
import pro.gravit.utils.helper.LogHelper; import pro.gravit.utils.helper.SecurityHelper;
import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
import java.lang.ProcessBuilder.Redirect;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.nio.file.*; import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes; import java.security.KeyStore;
import java.security.InvalidAlgorithmParameterException; import java.time.Duration;
import java.security.KeyPair; import java.time.Instant;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.util.*; import java.util.*;
import java.util.Map.Entry; import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Stream;
/**
* The main LaunchServer class. Contains links to all necessary objects
* Not a singletron
*/
public final class LaunchServer implements Runnable, AutoCloseable, Reconfigurable { public final class LaunchServer implements Runnable, AutoCloseable, Reconfigurable {
/**
public static final Class<? extends LauncherBinary> defaultLauncherEXEBinaryClass = null; * Working folder path
*/
public final Path dir; public final Path dir;
/**
* Environment type (test / production)
*/
public final LaunchServerEnv env; public final LaunchServerEnv env;
/**
* The path to the folder with libraries for the launcher
*/
public final Path launcherLibraries; public final Path launcherLibraries;
/**
* The path to the folder with compile-only libraries for the launcher
*/
public final Path launcherLibrariesCompile; public final Path launcherLibrariesCompile;
public final Path caCertFile; public final Path launcherPack;
/**
* The path to the folder with updates/webroot
*/
@Deprecated
public final Path updatesDir;
// Constant paths // Constant paths
public final Path caKeyFile; /**
public final Path serverCertFile; * Save/Reload LaunchServer config
public final Path serverKeyFile; */
public final Path updatesDir;
public final LaunchServerConfigManager launchServerConfigManager; public final LaunchServerConfigManager launchServerConfigManager;
public final Path profilesDir; /**
* The path to the folder with profiles
*/
public final Path tmpDir;
public final Path modulesDir;
public final Path launcherModulesDir;
public final Path librariesDir;
public final Path controlFile;
/**
* This object contains runtime configuration
*/
public final LaunchServerRuntimeConfig runtime; public final LaunchServerRuntimeConfig runtime;
public final ECPublicKey publicKey; /**
public final ECPrivateKey privateKey; * Pipeline for building JAR
*/
public final JARLauncherBinary launcherBinary; public final JARLauncherBinary launcherBinary;
/**
//public static LaunchServer server = null; * Pipeline for building EXE
public final Class<? extends LauncherBinary> launcherEXEBinaryClass; */
// Server config
public final LauncherBinary launcherEXEBinary; public final LauncherBinary launcherEXEBinary;
public final SessionManager sessionManager; // Server config
public final AuthHookManager authHookManager; public final AuthHookManager authHookManager;
public final LaunchServerModulesManager modulesManager; public final LaunchServerModulesManager modulesManager;
// Launcher binary // Launcher binary
public final MirrorManager mirrorManager; public final MirrorManager mirrorManager;
public final AuthManager authManager;
public final ReconfigurableManager reconfigurableManager; public final ReconfigurableManager reconfigurableManager;
public final ConfigManager configManager; public final ConfigManager configManager;
public final PingServerManager pingServerManager; public final FeaturesManager featuresManager;
public final KeyAgreementManager keyAgreementManager;
public final UpdatesManager updatesManager;
// HWID ban + anti-brutforce // HWID ban + anti-brutforce
public final CertificateManager certificateManager; public final CertificateManager certificateManager;
public final ProguardConf proguardConf;
// Server // Server
public final CommandHandler commandHandler; public final CommandHandler commandHandler;
public final NettyServerSocketHandler nettyServerSocketHandler; public final NettyServerSocketHandler nettyServerSocketHandler;
public final Timer taskPool; public final SocketCommandServer socketCommandServer;
public final ScheduledExecutorService service;
public final AtomicBoolean started = new AtomicBoolean(false); public final AtomicBoolean started = new AtomicBoolean(false);
public final LauncherModuleLoader launcherModuleLoader; public final LauncherModuleLoader launcherModuleLoader;
private final Logger logger = LogManager.getLogger();
public final int shardId;
public LaunchServerConfig config; public LaunchServerConfig config;
public volatile Map<String, HashedDir> updatesDirMap;
// Updates and profiles
private volatile List<ClientProfile> profilesList;
public LaunchServer(LaunchServerDirectories directories, LaunchServerEnv env, LaunchServerConfig config, LaunchServerRuntimeConfig runtimeConfig, LaunchServerConfigManager launchServerConfigManager, LaunchServerModulesManager modulesManager, ECPublicKey publicKey, ECPrivateKey privateKey, CommandHandler commandHandler, CertificateManager certificateManager) throws IOException { public LaunchServer(LaunchServerDirectories directories, LaunchServerEnv env, LaunchServerConfig config, LaunchServerRuntimeConfig runtimeConfig, LaunchServerConfigManager launchServerConfigManager, LaunchServerModulesManager modulesManager, KeyAgreementManager keyAgreementManager, CommandHandler commandHandler, CertificateManager certificateManager, int shardId) throws IOException {
this.dir = directories.dir; this.dir = directories.dir;
this.tmpDir = directories.tmpDir;
this.env = env; this.env = env;
this.config = config; this.config = config;
this.launchServerConfigManager = launchServerConfigManager; this.launchServerConfigManager = launchServerConfigManager;
this.modulesManager = modulesManager; this.modulesManager = modulesManager;
this.profilesDir = directories.profilesDir;
this.updatesDir = directories.updatesDir; this.updatesDir = directories.updatesDir;
this.publicKey = publicKey; this.keyAgreementManager = keyAgreementManager;
this.privateKey = privateKey;
this.commandHandler = commandHandler; this.commandHandler = commandHandler;
this.runtime = runtimeConfig; this.runtime = runtimeConfig;
this.certificateManager = certificateManager; this.certificateManager = certificateManager;
taskPool = new Timer("Timered task worker thread", true); this.service = Executors.newScheduledThreadPool(config.netty.performance.schedulerThread);
launcherLibraries = directories.launcherLibrariesDir; launcherLibraries = directories.launcherLibrariesDir;
launcherLibrariesCompile = directories.launcherLibrariesCompileDir; launcherLibrariesCompile = directories.launcherLibrariesCompileDir;
launcherPack = directories.launcherPackDir;
modulesDir = directories.modules;
launcherModulesDir = directories.launcherModules;
librariesDir = directories.librariesDir;
controlFile = directories.controlFile;
this.shardId = shardId;
if(!Files.isDirectory(launcherPack)) {
Files.createDirectories(launcherPack);
}
config.setLaunchServer(this); config.setLaunchServer(this);
caCertFile = dir.resolve("ca.crt");
caKeyFile = dir.resolve("ca.key");
serverCertFile = dir.resolve("server.crt");
serverKeyFile = dir.resolve("server.key");
modulesManager.invokeEvent(new NewLaunchServerInstanceEvent(this)); modulesManager.invokeEvent(new NewLaunchServerInstanceEvent(this));
// Print keypair fingerprints // Print keypair fingerprints
// Load class bindings.
launcherEXEBinaryClass = defaultLauncherEXEBinaryClass;
runtime.verify(); runtime.verify();
config.verify(); config.verify();
if (config.components != null) {
LogHelper.debug("PreInit components");
config.components.forEach((k, v) -> {
LogHelper.subDebug("PreInit component %s", k);
v.preInit(this);
});
LogHelper.debug("PreInit components successful");
}
// build hooks, anti-brutforce and other // build hooks, anti-brutforce and other
proguardConf = new ProguardConf(this);
sessionManager = new SessionManager();
mirrorManager = new MirrorManager(); mirrorManager = new MirrorManager();
reconfigurableManager = new ReconfigurableManager(); reconfigurableManager = new ReconfigurableManager();
authHookManager = new AuthHookManager(); authHookManager = new AuthHookManager();
configManager = new ConfigManager(); configManager = new ConfigManager();
pingServerManager = new PingServerManager(this); featuresManager = new FeaturesManager(this);
//Generate or set new Certificate API authManager = new AuthManager(this);
certificateManager.orgName = config.projectName; updatesManager = new UpdatesManager(this);
if (config.certificate != null && config.certificate.enabled) { RestoreResponse.registerProviders(this);
if (IOHelper.isFile(caCertFile) && IOHelper.isFile(caKeyFile)) {
certificateManager.ca = certificateManager.readCertificate(caCertFile);
certificateManager.caKey = certificateManager.readPrivateKey(caKeyFile);
} else {
try {
certificateManager.generateCA();
certificateManager.writeCertificate(caCertFile, certificateManager.ca);
certificateManager.writePrivateKey(caKeyFile, certificateManager.caKey);
} catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException | OperatorCreationException e) {
LogHelper.error(e);
}
}
if (IOHelper.isFile(serverCertFile) && IOHelper.isFile(serverKeyFile)) {
certificateManager.server = certificateManager.readCertificate(serverCertFile);
certificateManager.serverKey = certificateManager.readPrivateKey(serverKeyFile);
} else {
try {
KeyPair pair = certificateManager.generateKeyPair();
certificateManager.server = certificateManager.generateCertificate(config.projectName.concat(" Server"), pair.getPublic());
certificateManager.serverKey = PrivateKeyFactory.createKey(pair.getPrivate().getEncoded());
certificateManager.writePrivateKey(serverKeyFile, pair.getPrivate());
certificateManager.writeCertificate(serverCertFile, certificateManager.server);
} catch (InvalidAlgorithmParameterException | NoSuchAlgorithmException | OperatorCreationException e) {
LogHelper.error(e);
}
}
}
config.init(ReloadType.FULL); config.init(ReloadType.FULL);
registerObject("launchServer", this); registerObject("launchServer", this);
GarbageManager.registerNeedGC(sessionManager);
pro.gravit.launchserver.command.handler.CommandHandler.registerCommands(commandHandler, this); pro.gravit.launchserver.command.handler.CommandHandler.registerCommands(commandHandler, this);
// init modules // init modules
modulesManager.invokeEvent(new LaunchServerInitPhase(this)); modulesManager.invokeEvent(new LaunchServerInitPhase(this));
if (config.components != null) {
LogHelper.debug("Init components");
config.components.forEach((k, v) -> {
LogHelper.subDebug("Init component %s", k);
v.init(this);
});
LogHelper.debug("Init components successful");
}
// Set launcher EXE binary // Set launcher EXE binary
launcherBinary = new JARLauncherBinary(this); launcherBinary = new JARLauncherBinary(this);
@ -200,27 +178,24 @@ public LaunchServer(LaunchServerDirectories directories, LaunchServerEnv env, La
launcherEXEBinary.init(); launcherEXEBinary.init();
syncLauncherBinaries(); syncLauncherBinaries();
launcherModuleLoader = new LauncherModuleLoader(this); launcherModuleLoader = new LauncherModuleLoader(this);
// Sync updates dir if (config.components != null) {
if (!IOHelper.isDir(updatesDir)) logger.debug("Init components");
Files.createDirectory(updatesDir); config.components.forEach((k, v) -> {
syncUpdatesDir(null); logger.debug("Init component {}", k);
v.setComponentName(k);
// Sync profiles dir v.init(this);
if (!IOHelper.isDir(profilesDir)) });
Files.createDirectory(profilesDir); logger.debug("Init components successful");
syncProfilesDir(); }
launcherModuleLoader.init(); launcherModuleLoader.init();
nettyServerSocketHandler = new NettyServerSocketHandler(this); nettyServerSocketHandler = new NettyServerSocketHandler(this);
socketCommandServer = new SocketCommandServer(commandHandler, controlFile);
if(config.sign.checkCertificateExpired) {
checkCertificateExpired();
service.scheduleAtFixedRate(this::checkCertificateExpired, 24, 24, TimeUnit.HOURS);
}
// post init modules // post init modules
modulesManager.invokeEvent(new LaunchServerPostInitPhase(this)); modulesManager.invokeEvent(new LaunchServerPostInitPhase(this));
if (config.components != null) {
LogHelper.debug("PostInit components");
config.components.forEach((k, v) -> {
LogHelper.subDebug("PostInit component %s", k);
v.postInit(this);
});
LogHelper.debug("PostInit components successful");
}
} }
public void reload(ReloadType type) throws Exception { public void reload(ReloadType type) throws Exception {
@ -229,7 +204,7 @@ public void reload(ReloadType type) throws Exception {
if (type.equals(ReloadType.NO_AUTH)) { if (type.equals(ReloadType.NO_AUTH)) {
pairs = config.auth; pairs = config.auth;
} }
LogHelper.info("Reading LaunchServer config file"); logger.info("Reading LaunchServer config file");
config = launchServerConfigManager.readConfig(); config = launchServerConfigManager.readConfig();
config.setLaunchServer(this); config.setLaunchServer(this);
if (type.equals(ReloadType.NO_AUTH)) { if (type.equals(ReloadType.NO_AUTH)) {
@ -238,32 +213,28 @@ public void reload(ReloadType type) throws Exception {
config.verify(); config.verify();
config.init(type); config.init(type);
if (type.equals(ReloadType.FULL) && config.components != null) { if (type.equals(ReloadType.FULL) && config.components != null) {
LogHelper.debug("PreInit components"); logger.debug("Init components");
config.components.forEach((k, v) -> { config.components.forEach((k, v) -> {
LogHelper.subDebug("PreInit component %s", k); logger.debug("Init component {}", k);
v.preInit(this); v.setComponentName(k);
});
LogHelper.debug("PreInit components successful");
LogHelper.debug("Init components");
config.components.forEach((k, v) -> {
LogHelper.subDebug("Init component %s", k);
v.init(this); v.init(this);
}); });
LogHelper.debug("Init components successful"); logger.debug("Init components successful");
LogHelper.debug("PostInit components"); }
config.components.forEach((k, v) -> { if(!type.equals(ReloadType.NO_AUTH)) {
LogHelper.subDebug("PostInit component %s", k); nettyServerSocketHandler.nettyServer.service.forEachActiveChannels((channel, wsHandler) -> {
v.postInit(this); Client client = wsHandler.getClient();
}); if(client.auth != null) {
LogHelper.debug("PostInit components successful"); client.auth = config.getAuthProviderPair(client.auth_id);
}
});
} }
} }
@Override @Override
public Map<String, Command> getCommands() { public Map<String, Command> getCommands() {
Map<String, Command> commands = new HashMap<>(); Map<String, Command> commands = new HashMap<>();
SubCommand reload = new SubCommand() { SubCommand reload = new SubCommand("[type]", "reload launchserver config") {
@Override @Override
public void invoke(String... args) throws Exception { public void invoke(String... args) throws Exception {
if (args.length == 0) { if (args.length == 0) {
@ -271,38 +242,65 @@ public void invoke(String... args) throws Exception {
return; return;
} }
switch (args[0]) { switch (args[0]) {
case "full": case "full" -> reload(ReloadType.FULL);
reload(ReloadType.FULL); case "no_components" -> reload(ReloadType.NO_COMPONENTS);
break; default -> reload(ReloadType.NO_AUTH);
case "no_auth":
reload(ReloadType.NO_AUTH);
break;
case "no_components":
reload(ReloadType.NO_COMPONENTS);
break;
default:
reload(ReloadType.FULL);
break;
} }
} }
}; };
commands.put("reload", reload); commands.put("reload", reload);
SubCommand save = new SubCommand("[]", "save launchserver config") {
@Override
public void invoke(String... args) throws Exception {
launchServerConfigManager.writeConfig(config);
launchServerConfigManager.writeRuntimeConfig(runtime);
logger.info("LaunchServerConfig saved");
}
};
commands.put("save", save);
LaunchServer instance = this;
SubCommand resetauth = new SubCommand("authId", "reset auth by id") {
@Override
public void invoke(String... args) throws Exception {
verifyArgs(args, 1);
AuthProviderPair pair = config.getAuthProviderPair(args[0]);
if (pair == null) {
logger.error("Pair not found");
return;
}
pair.core.close();
pair.core = new RejectAuthCoreProvider();
pair.core.init(instance, pair);
}
};
commands.put("resetauth", resetauth);
return commands; return commands;
} }
private LauncherBinary binary() { public void checkCertificateExpired() {
if (launcherEXEBinaryClass != null) { if(!config.sign.enabled) {
return;
}
try { try {
return (LauncherBinary) MethodHandles.publicLookup().findConstructor(launcherEXEBinaryClass, MethodType.methodType(void.class, LaunchServer.class)).invoke(this); KeyStore keyStore = SignHelper.getStore(Paths.get(config.sign.keyStore), config.sign.keyStorePass, config.sign.keyStoreType);
Instant date = SignHelper.getCertificateExpired(keyStore, config.sign.keyAlias);
if(date == null) {
logger.debug("The certificate will expire at unlimited");
} else if(date.minus(Duration.ofDays(30)).isBefore(Instant.now())) {
logger.warn("The certificate will expire at {}", date.toString());
} else {
logger.debug("The certificate will expire at {}", date.toString());
}
} catch (Throwable e) { } catch (Throwable e) {
LogHelper.error(e); logger.error("Can't get certificate expire date", e);
} }
} }
try {
Class.forName("net.sf.launch4j.Builder"); private LauncherBinary binary() {
if (config.launch4j.enabled) return new EXEL4JLauncherBinary(this); LaunchServerLauncherExeInit event = new LaunchServerLauncherExeInit(this, null);
} catch (ClassNotFoundException ignored) { modulesManager.invokeEvent(event);
LogHelper.warning("Launch4J isn't in classpath."); if(event.binary != null) {
return event.binary;
} }
return new EXELauncherBinary(this); return new EXELauncherBinary(this);
} }
@ -313,30 +311,26 @@ public void buildLauncherBinaries() throws IOException {
} }
public void close() throws Exception { public void close() throws Exception {
service.shutdownNow();
logger.info("Close server socket");
nettyServerSocketHandler.close();
// Close handlers & providers // Close handlers & providers
config.close(ReloadType.FULL); config.close(ReloadType.FULL);
modulesManager.invokeEvent(new ClosePhase()); modulesManager.invokeEvent(new ClosePhase());
LogHelper.info("Save LaunchServer runtime config"); logger.info("Save LaunchServer runtime config");
launchServerConfigManager.writeRuntimeConfig(runtime); launchServerConfigManager.writeRuntimeConfig(runtime);
// Print last message before death :( // Print last message before death :(
LogHelper.info("LaunchServer stopped"); logger.info("LaunchServer stopped");
} }
public List<ClientProfile> getProfiles() { @Deprecated
return profilesList; public Set<ClientProfile> getProfiles() {
return config.profileProvider.getProfiles();
} }
public void setProfiles(List<ClientProfile> profilesList) { @Deprecated
this.profilesList = Collections.unmodifiableList(profilesList); public void setProfiles(Set<ClientProfile> profilesList) {
} throw new UnsupportedOperationException();
public HashedDir getUpdateDir(String name) {
return updatesDirMap.get(name);
}
public Set<Entry<String, HashedDir>> getUpdateDirs() {
return updatesDirMap.entrySet();
} }
public void rebindNettyServerSocket() { public void rebindNettyServerSocket() {
@ -355,113 +349,90 @@ public void run() {
try { try {
close(); close();
} catch (Exception e) { } catch (Exception e) {
LogHelper.error(e); logger.error("LaunchServer close error", e);
} }
})); }));
CommonHelper.newThread("Command Thread", true, commandHandler).start(); CommonHelper.newThread("Command Thread", true, commandHandler).start();
CommonHelper.newThread("Socket Command Thread", true, socketCommandServer).start();
// Sync updates dir
CommonHelper.newThread("Profiles and updates sync", true, () -> {
try {
// Sync profiles dir
syncProfilesDir();
// Sync updates dir
config.updatesProvider.syncInitially();
modulesManager.invokeEvent(new LaunchServerProfilesSyncEvent(this));
} catch (IOException e) {
logger.error("Updates/Profiles not synced", e);
}
}).start();
} }
if (config.netty != null) if (config.netty != null)
rebindNettyServerSocket(); rebindNettyServerSocket();
try {
modulesManager.fullInitializedLaunchServer(this); modulesManager.fullInitializedLaunchServer(this);
modulesManager.invokeEvent(new LaunchServerFullInitEvent(this)); modulesManager.invokeEvent(new LaunchServerFullInitEvent(this));
logger.info("LaunchServer started");
} catch (Throwable e) {
logger.error("LaunchServer startup failed", e);
JVMHelper.RUNTIME.exit(-1);
}
} }
public void syncLauncherBinaries() throws IOException { public void syncLauncherBinaries() throws IOException {
LogHelper.info("Syncing launcher binaries"); logger.info("Syncing launcher binaries");
// Syncing launcher binary // Syncing launcher binary
LogHelper.info("Syncing launcher binary file"); logger.info("Syncing launcher binary file");
if (!launcherBinary.sync()) LogHelper.warning("Missing launcher binary file"); if (!launcherBinary.sync()) logger.warn("Missing launcher binary file");
// Syncing launcher EXE binary // Syncing launcher EXE binary
LogHelper.info("Syncing launcher EXE binary file"); logger.info("Syncing launcher EXE binary file");
if (!launcherEXEBinary.sync() && config.launch4j.enabled) if (!launcherEXEBinary.sync())
LogHelper.warning("Missing launcher EXE binary file"); logger.warn("Missing launcher EXE binary file");
} }
public void syncProfilesDir() throws IOException { public void syncProfilesDir() throws IOException {
LogHelper.info("Syncing profiles dir"); logger.info("Syncing profiles dir");
List<ClientProfile> newProfies = new LinkedList<>(); config.profileProvider.sync();
IOHelper.walk(profilesDir, new ProfilesFileVisitor(newProfies), false); if (config.netty.sendProfileUpdatesEvent) {
sendUpdateProfilesEvent();
}
}
// Sort and set new profiles private void sendUpdateProfilesEvent() {
newProfies.sort(Comparator.comparing(a -> a)); if (nettyServerSocketHandler == null || nettyServerSocketHandler.nettyServer == null || nettyServerSocketHandler.nettyServer.service == null) {
profilesList = Collections.unmodifiableList(newProfies); return;
if (pingServerManager != null) }
pingServerManager.syncServers(); nettyServerSocketHandler.nettyServer.service.forEachActiveChannels((ch, handler) -> {
Client client = handler.getClient();
if (client == null || !client.isAuth) {
return;
}
ProfilesRequestEvent event = new ProfilesRequestEvent(config.profileProvider.getProfiles(client));
event.requestUUID = RequestEvent.eventUUID;
handler.service.sendObject(ch, event);
});
} }
public void syncUpdatesDir(Collection<String> dirs) throws IOException { public void syncUpdatesDir(Collection<String> dirs) throws IOException {
LogHelper.info("Syncing updates dir"); updatesManager.syncUpdatesDir(dirs);
Map<String, HashedDir> newUpdatesDirMap = new HashMap<>(16);
try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(updatesDir)) {
for (final Path updateDir : dirStream) {
if (Files.isHidden(updateDir))
continue; // Skip hidden
// Resolve name and verify is dir
String name = IOHelper.getFileName(updateDir);
if (!IOHelper.isDir(updateDir)) {
if (!IOHelper.isFile(updateDir) && Stream.of(".jar", ".exe", ".hash").noneMatch(e -> updateDir.toString().endsWith(e)))
LogHelper.warning("Not update dir: '%s'", name);
continue;
}
// Add from previous map (it's guaranteed to be non-null)
if (dirs != null && !dirs.contains(name)) {
HashedDir hdir = updatesDirMap.get(name);
if (hdir != null) {
newUpdatesDirMap.put(name, hdir);
continue;
}
}
// Sync and sign update dir
LogHelper.info("Syncing '%s' update dir", name);
HashedDir updateHDir = new HashedDir(updateDir, null, true, true);
newUpdatesDirMap.put(name, updateHDir);
}
}
updatesDirMap = Collections.unmodifiableMap(newUpdatesDirMap);
}
public void restart() {
ProcessBuilder builder = new ProcessBuilder();
if (config.startScript != null) builder.command(Collections.singletonList(config.startScript));
else throw new IllegalArgumentException("Please create start script and link it as startScript in config.");
builder.directory(this.dir.toFile());
builder.inheritIO();
builder.redirectErrorStream(true);
builder.redirectOutput(Redirect.PIPE);
try {
builder.start();
} catch (IOException e) {
LogHelper.error(e);
}
} }
public void registerObject(String name, Object object) { public void registerObject(String name, Object object) {
if (object instanceof Reconfigurable) { if (object instanceof Reconfigurable) {
reconfigurableManager.registerReconfigurable(name, (Reconfigurable) object); reconfigurableManager.registerReconfigurable(name, (Reconfigurable) object);
} }
if (object instanceof NeedGarbageCollection) {
GarbageManager.registerNeedGC((NeedGarbageCollection) object);
}
} }
public void unregisterObject(String name, Object object) { public void unregisterObject(String name, Object object) {
if (object instanceof Reconfigurable) { if (object instanceof Reconfigurable) {
reconfigurableManager.unregisterReconfigurable(name); reconfigurableManager.unregisterReconfigurable(name);
} }
if (object instanceof NeedGarbageCollection) {
GarbageManager.unregisterNeedGC((NeedGarbageCollection) object);
}
}
public void fullyRestart() {
restart();
JVMHelper.RUNTIME.exit(0);
} }
@ -488,48 +459,44 @@ public interface LaunchServerConfigManager {
void writeRuntimeConfig(LaunchServerRuntimeConfig config) throws IOException; void writeRuntimeConfig(LaunchServerRuntimeConfig config) throws IOException;
} }
private static final class ProfilesFileVisitor extends SimpleFileVisitor<Path> {
private final Collection<ClientProfile> result;
private ProfilesFileVisitor(Collection<ClientProfile> result) {
this.result = result;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
LogHelper.info("Syncing '%s' profile", IOHelper.getFileName(file));
// Read profile
ClientProfile profile;
try (BufferedReader reader = IOHelper.newReader(file)) {
profile = Launcher.gsonManager.gson.fromJson(reader, ClientProfile.class);
}
profile.verify();
// Add SIGNED profile to result list
result.add(profile);
return super.visitFile(file, attrs);
}
}
public static class LaunchServerDirectories { public static class LaunchServerDirectories {
public static final String UPDATES_NAME = "updates", PROFILES_NAME = "profiles", public static final String UPDATES_NAME = "updates",
TRUSTSTORE_NAME = "truststore", LAUNCHERLIBRARIES_NAME = "launcher-libraries", TRUSTSTORE_NAME = "truststore", LAUNCHERLIBRARIES_NAME = "launcher-libraries",
LAUNCHERLIBRARIESCOMPILE_NAME = "launcher-libraries-compile"; LAUNCHERLIBRARIESCOMPILE_NAME = "launcher-libraries-compile", LAUNCHERPACK_NAME = "launcher-pack", KEY_NAME = ".keys", MODULES = "modules", LAUNCHER_MODULES = "launcher-modules", LIBRARIES = "libraries", CONTROL_FILE = "control-file";
public Path updatesDir; public Path updatesDir;
public Path profilesDir; public Path librariesDir;
public Path launcherLibrariesDir; public Path launcherLibrariesDir;
public Path launcherLibrariesCompileDir; public Path launcherLibrariesCompileDir;
public Path launcherPackDir;
public Path keyDirectory;
public Path dir; public Path dir;
public Path trustStore; public Path trustStore;
public Path tmpDir;
public Path modules;
public Path launcherModules;
public Path controlFile;
public void collect() { public void collect() {
if (updatesDir == null) updatesDir = dir.resolve(UPDATES_NAME); if (updatesDir == null) updatesDir = getPath(UPDATES_NAME);
if (profilesDir == null) profilesDir = dir.resolve(PROFILES_NAME); if (trustStore == null) trustStore = getPath(TRUSTSTORE_NAME);
if (trustStore == null) trustStore = dir.resolve(TRUSTSTORE_NAME); if (launcherLibrariesDir == null) launcherLibrariesDir = getPath(LAUNCHERLIBRARIES_NAME);
if (launcherLibrariesDir == null) launcherLibrariesDir = dir.resolve(LAUNCHERLIBRARIES_NAME);
if (launcherLibrariesCompileDir == null) if (launcherLibrariesCompileDir == null)
launcherLibrariesCompileDir = dir.resolve(LAUNCHERLIBRARIESCOMPILE_NAME); launcherLibrariesCompileDir = getPath(LAUNCHERLIBRARIESCOMPILE_NAME);
if (launcherPackDir == null)
launcherPackDir = getPath(LAUNCHERPACK_NAME);
if (keyDirectory == null) keyDirectory = getPath(KEY_NAME);
if (modules == null) modules = getPath(MODULES);
if (launcherModules == null) launcherModules = getPath(LAUNCHER_MODULES);
if (librariesDir == null) librariesDir = getPath(LIBRARIES);
if (controlFile == null) controlFile = getPath(CONTROL_FILE);
if (tmpDir == null)
tmpDir = Paths.get(System.getProperty("java.io.tmpdir")).resolve("launchserver-%s".formatted(SecurityHelper.randomStringToken()));
}
private Path getPath(String dirName) {
String property = System.getProperty("launchserver.dir." + dirName, null);
if (property == null) return dir.resolve(dirName);
else return Paths.get(property);
} }
} }
} }

View file

@ -3,12 +3,11 @@
import pro.gravit.launchserver.config.LaunchServerConfig; import pro.gravit.launchserver.config.LaunchServerConfig;
import pro.gravit.launchserver.config.LaunchServerRuntimeConfig; import pro.gravit.launchserver.config.LaunchServerRuntimeConfig;
import pro.gravit.launchserver.manangers.CertificateManager; import pro.gravit.launchserver.manangers.CertificateManager;
import pro.gravit.launchserver.manangers.KeyAgreementManager;
import pro.gravit.launchserver.modules.impl.LaunchServerModulesManager; import pro.gravit.launchserver.modules.impl.LaunchServerModulesManager;
import pro.gravit.utils.command.CommandHandler; import pro.gravit.utils.command.CommandHandler;
import java.nio.file.Path; import java.nio.file.Path;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
public class LaunchServerBuilder { public class LaunchServerBuilder {
private LaunchServerConfig config; private LaunchServerConfig config;
@ -17,10 +16,10 @@ public class LaunchServerBuilder {
private LaunchServer.LaunchServerEnv env; private LaunchServer.LaunchServerEnv env;
private LaunchServerModulesManager modulesManager; private LaunchServerModulesManager modulesManager;
private LaunchServer.LaunchServerDirectories directories = new LaunchServer.LaunchServerDirectories(); private LaunchServer.LaunchServerDirectories directories = new LaunchServer.LaunchServerDirectories();
private ECPublicKey publicKey; private KeyAgreementManager keyAgreementManager;
private ECPrivateKey privateKey;
private CertificateManager certificateManager; private CertificateManager certificateManager;
private LaunchServer.LaunchServerConfigManager launchServerConfigManager; private LaunchServer.LaunchServerConfigManager launchServerConfigManager;
private Integer shardId;
public LaunchServerBuilder setConfig(LaunchServerConfig config) { public LaunchServerBuilder setConfig(LaunchServerConfig config) {
this.config = config; this.config = config;
@ -57,13 +56,8 @@ public LaunchServerBuilder setDir(Path dir) {
return this; return this;
} }
public LaunchServerBuilder setPublicKey(ECPublicKey publicKey) { public LaunchServerBuilder setShardId(Integer shardId) {
this.publicKey = publicKey; this.shardId = shardId;
return this;
}
public LaunchServerBuilder setPrivateKey(ECPrivateKey privateKey) {
this.privateKey = privateKey;
return this; return this;
} }
@ -73,11 +67,29 @@ public LaunchServerBuilder setLaunchServerConfigManager(LaunchServer.LaunchServe
} }
public LaunchServer build() throws Exception { public LaunchServer build() throws Exception {
//if(updatesDir == null) updatesDir = dir.resolve("updates");
//if(profilesDir == null) profilesDir = dir.resolve("profiles");
directories.collect(); directories.collect();
if (launchServerConfigManager == null) { if (launchServerConfigManager == null) {
launchServerConfigManager = new LaunchServer.LaunchServerConfigManager() { launchServerConfigManager = new NullLaunchServerConfigManager();
}
if (keyAgreementManager == null) {
keyAgreementManager = new KeyAgreementManager(directories.keyDirectory);
}
if(shardId == null) {
shardId = Integer.parseInt(System.getProperty("launchserver.shardId", "0"));
}
return new LaunchServer(directories, env, config, runtimeConfig, launchServerConfigManager, modulesManager, keyAgreementManager, commandHandler, certificateManager, shardId);
}
public LaunchServerBuilder setCertificateManager(CertificateManager certificateManager) {
this.certificateManager = certificateManager;
return this;
}
public void setKeyAgreementManager(KeyAgreementManager keyAgreementManager) {
this.keyAgreementManager = keyAgreementManager;
}
private static class NullLaunchServerConfigManager implements LaunchServer.LaunchServerConfigManager {
@Override @Override
public LaunchServerConfig readConfig() { public LaunchServerConfig readConfig() {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
@ -97,13 +109,5 @@ public void writeConfig(LaunchServerConfig config) {
public void writeRuntimeConfig(LaunchServerRuntimeConfig config) { public void writeRuntimeConfig(LaunchServerRuntimeConfig config) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
};
}
return new LaunchServer(directories, env, config, runtimeConfig, launchServerConfigManager, modulesManager, publicKey, privateKey, commandHandler, certificateManager);
}
public LaunchServerBuilder setCertificateManager(CertificateManager certificateManager) {
this.certificateManager = certificateManager;
return this;
} }
} }

View file

@ -1,20 +1,25 @@
package pro.gravit.launchserver; package pro.gravit.launchserver;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.jce.provider.BouncyCastleProvider;
import pro.gravit.launcher.Launcher; import pro.gravit.launcher.base.Launcher;
import pro.gravit.launcher.LauncherTrustManager; import pro.gravit.launcher.core.LauncherTrustManager;
import pro.gravit.launcher.modules.events.PreConfigPhase; import pro.gravit.launcher.base.modules.events.PreConfigPhase;
import pro.gravit.launcher.profiles.optional.actions.OptionalAction; import pro.gravit.launcher.base.profiles.optional.actions.OptionalAction;
import pro.gravit.launcher.request.auth.AuthRequest; import pro.gravit.launcher.base.profiles.optional.triggers.OptionalTrigger;
import pro.gravit.launchserver.auth.handler.AuthHandler; import pro.gravit.launcher.base.request.auth.AuthRequest;
import pro.gravit.launcher.base.request.auth.GetAvailabilityAuthRequest;
import pro.gravit.launchserver.auth.core.AuthCoreProvider;
import pro.gravit.launchserver.auth.mix.MixProvider;
import pro.gravit.launchserver.auth.password.PasswordVerifier;
import pro.gravit.launchserver.auth.profiles.ProfileProvider;
import pro.gravit.launchserver.auth.protect.ProtectHandler; import pro.gravit.launchserver.auth.protect.ProtectHandler;
import pro.gravit.launchserver.auth.protect.hwid.HWIDProvider;
import pro.gravit.launchserver.auth.provider.AuthProvider;
import pro.gravit.launchserver.auth.texture.TextureProvider; import pro.gravit.launchserver.auth.texture.TextureProvider;
import pro.gravit.launchserver.auth.updates.UpdatesProvider;
import pro.gravit.launchserver.components.Component; import pro.gravit.launchserver.components.Component;
import pro.gravit.launchserver.config.LaunchServerConfig; import pro.gravit.launchserver.config.LaunchServerConfig;
import pro.gravit.launchserver.config.LaunchServerRuntimeConfig; import pro.gravit.launchserver.config.LaunchServerRuntimeConfig;
import pro.gravit.launchserver.dao.provider.DaoProvider;
import pro.gravit.launchserver.manangers.CertificateManager; import pro.gravit.launchserver.manangers.CertificateManager;
import pro.gravit.launchserver.manangers.LaunchServerGsonManager; import pro.gravit.launchserver.manangers.LaunchServerGsonManager;
import pro.gravit.launchserver.modules.impl.LaunchServerModulesManager; import pro.gravit.launchserver.modules.impl.LaunchServerModulesManager;
@ -25,63 +30,65 @@
import pro.gravit.utils.helper.IOHelper; import pro.gravit.utils.helper.IOHelper;
import pro.gravit.utils.helper.JVMHelper; import pro.gravit.utils.helper.JVMHelper;
import pro.gravit.utils.helper.LogHelper; import pro.gravit.utils.helper.LogHelper;
import pro.gravit.utils.helper.SecurityHelper;
import java.io.BufferedReader; import java.io.*;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.Writer;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.security.KeyPair;
import java.security.SecureRandom;
import java.security.Security; import java.security.Security;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
import java.security.interfaces.ECPrivateKey; import java.util.List;
import java.security.interfaces.ECPublicKey;
public class LaunchServerStarter { public class LaunchServerStarter {
public static final boolean allowUnsigned = Boolean.getBoolean("launchserver.allowUnsigned"); public static final boolean allowUnsigned = Boolean.getBoolean("launchserver.allowUnsigned");
public static final boolean inDocker = Boolean.getBoolean("launchserver.dockered"); public static final boolean prepareMode = Boolean.getBoolean("launchserver.prepareMode");
private static final Logger logger = LogManager.getLogger();
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
JVMHelper.checkStackTrace(LaunchServerStarter.class); JVMHelper.verifySystemProperties(LaunchServer.class, false);
JVMHelper.verifySystemProperties(LaunchServer.class, true); //LogHelper.addOutput(IOHelper.WORKING_DIR.resolve("LaunchServer.log"));
LogHelper.addOutput(IOHelper.WORKING_DIR.resolve("LaunchServer.log"));
LogHelper.printVersion("LaunchServer"); LogHelper.printVersion("LaunchServer");
LogHelper.printLicense("LaunchServer"); LogHelper.printLicense("LaunchServer");
if (!StarterAgent.isAgentStarted()) {
LogHelper.error("StarterAgent is not started!");
LogHelper.error("You should add to JVM options this option: `-javaagent:LaunchServer.jar`");
}
Path dir = IOHelper.WORKING_DIR; Path dir = IOHelper.WORKING_DIR;
Path configFile, runtimeConfigFile; Path configFile, runtimeConfigFile;
Path publicKeyFile = dir.resolve("public.key"); try {
Path privateKeyFile = dir.resolve("private.key"); Class.forName("org.bouncycastle.jce.provider.BouncyCastleProvider");
ECPublicKey publicKey;
ECPrivateKey privateKey;
Security.addProvider(new BouncyCastleProvider()); Security.addProvider(new BouncyCastleProvider());
} catch (ClassNotFoundException | NoClassDefFoundError ex) {
LogHelper.error("Library BouncyCastle not found! Is directory 'libraries' empty?");
return;
}
LaunchServer.LaunchServerDirectories directories = new LaunchServer.LaunchServerDirectories();
directories.dir = dir;
directories.collect();
CertificateManager certificateManager = new CertificateManager(); CertificateManager certificateManager = new CertificateManager();
try { try {
certificateManager.readTrustStore(dir.resolve("truststore")); certificateManager.readTrustStore(directories.trustStore);
} catch (CertificateException e) { } catch (CertificateException e) {
throw new IOException(e); throw new IOException(e);
} }
{ {
//LauncherTrustManager.CheckMode mode = (Version.RELEASE == Version.Type.LTS || Version.RELEASE == Version.Type.STABLE) ? LauncherTrustManager.CheckClassResult result = certificateManager.checkClass(LaunchServer.class);
// (allowUnsigned ? LauncherTrustManager.CheckMode.WARN_IN_NOT_SIGNED : LauncherTrustManager.CheckMode.EXCEPTION_IN_NOT_SIGNED) : if (result.type == LauncherTrustManager.CheckClassResultType.SUCCESS) {
// (allowUnsigned ? LauncherTrustManager.CheckMode.NONE_IN_NOT_SIGNED : LauncherTrustManager.CheckMode.WARN_IN_NOT_SIGNED); logger.info("LaunchServer signed by {}", result.endCertificate.getSubjectX500Principal().getName());
certificateManager.checkClass(LaunchServer.class, LauncherTrustManager.CheckMode.NONE_IN_NOT_SIGNED); } else if (result.type == LauncherTrustManager.CheckClassResultType.NOT_SIGNED) {
// None
} else {
if (result.exception != null) {
logger.error(result.exception);
}
logger.warn("LaunchServer signed incorrectly. Status: {}", result.type.name());
}
} }
LaunchServerRuntimeConfig runtimeConfig; LaunchServerRuntimeConfig runtimeConfig;
LaunchServerConfig config; LaunchServerConfig config;
LaunchServer.LaunchServerEnv env = LaunchServer.LaunchServerEnv.PRODUCTION; LaunchServer.LaunchServerEnv env = LaunchServer.LaunchServerEnv.PRODUCTION;
LaunchServerModulesManager modulesManager = new LaunchServerModulesManager(dir.resolve("modules"), dir.resolve("config"), certificateManager.trustManager); LaunchServerModulesManager modulesManager = new LaunchServerModulesManager(directories.modules, dir.resolve("config"), certificateManager.trustManager);
modulesManager.autoload(); modulesManager.autoload();
modulesManager.initModules(null); modulesManager.initModules(null);
registerAll(); registerAll();
initGson(modulesManager); initGson(modulesManager);
printExperimentalBranch();
if (IOHelper.exists(dir.resolve("LaunchServer.conf"))) { if (IOHelper.exists(dir.resolve("LaunchServer.conf"))) {
configFile = dir.resolve("LaunchServer.conf"); configFile = dir.resolve("LaunchServer.conf");
} else { } else {
@ -98,44 +105,178 @@ public static void main(String[] args) throws Exception {
// JLine2 available // JLine2 available
localCommandHandler = new JLineCommandHandler(); localCommandHandler = new JLineCommandHandler();
LogHelper.info("JLine2 terminal enabled"); logger.info("JLine2 terminal enabled");
} catch (ClassNotFoundException ignored) { } catch (ClassNotFoundException ignored) {
localCommandHandler = new StdCommandHandler(true); localCommandHandler = new StdCommandHandler(true);
LogHelper.warning("JLine2 isn't in classpath, using std"); logger.warn("JLine2 isn't in classpath, using std");
}
if (IOHelper.isFile(publicKeyFile) && IOHelper.isFile(privateKeyFile)) {
LogHelper.info("Reading EC keypair");
publicKey = SecurityHelper.toPublicECKey(IOHelper.read(publicKeyFile));
privateKey = SecurityHelper.toPrivateECKey(IOHelper.read(privateKeyFile));
} else {
LogHelper.info("Generating EC keypair");
KeyPair pair = SecurityHelper.genECKeyPair(new SecureRandom());
publicKey = (ECPublicKey) pair.getPublic();
privateKey = (ECPrivateKey) pair.getPrivate();
// Write key pair list
LogHelper.info("Writing EC keypair list");
IOHelper.write(publicKeyFile, publicKey.getEncoded());
IOHelper.write(privateKeyFile, privateKey.getEncoded());
} }
modulesManager.invokeEvent(new PreConfigPhase()); modulesManager.invokeEvent(new PreConfigPhase());
generateConfigIfNotExists(configFile, localCommandHandler, env); generateConfigIfNotExists(configFile, localCommandHandler, env);
LogHelper.info("Reading LaunchServer config file"); logger.info("Reading LaunchServer config file");
try (BufferedReader reader = IOHelper.newReader(configFile)) { try (BufferedReader reader = IOHelper.newReader(configFile)) {
config = Launcher.gsonManager.gson.fromJson(reader, LaunchServerConfig.class); config = Launcher.gsonManager.gson.fromJson(reader, LaunchServerConfig.class);
} }
if (!Files.exists(runtimeConfigFile)) { if (!Files.exists(runtimeConfigFile)) {
LogHelper.info("Reset LaunchServer runtime config file"); logger.info("Reset LaunchServer runtime config file");
runtimeConfig = new LaunchServerRuntimeConfig(); runtimeConfig = new LaunchServerRuntimeConfig();
runtimeConfig.reset(); runtimeConfig.reset();
} else { } else {
LogHelper.info("Reading LaunchServer runtime config file"); logger.info("Reading LaunchServer runtime config file");
try (BufferedReader reader = IOHelper.newReader(runtimeConfigFile)) { try (BufferedReader reader = IOHelper.newReader(runtimeConfigFile)) {
runtimeConfig = Launcher.gsonManager.gson.fromJson(reader, LaunchServerRuntimeConfig.class); runtimeConfig = Launcher.gsonManager.gson.fromJson(reader, LaunchServerRuntimeConfig.class);
} }
} }
LaunchServer.LaunchServerConfigManager launchServerConfigManager = new LaunchServer.LaunchServerConfigManager() { LaunchServer.LaunchServerConfigManager launchServerConfigManager = new BasicLaunchServerConfigManager(configFile, runtimeConfigFile);
LaunchServer server = new LaunchServerBuilder()
.setDirectories(directories)
.setEnv(env)
.setCommandHandler(localCommandHandler)
.setRuntimeConfig(runtimeConfig)
.setConfig(config)
.setModulesManager(modulesManager)
.setLaunchServerConfigManager(launchServerConfigManager)
.setCertificateManager(certificateManager)
.build();
List<String> allArgs = List.of(args);
boolean isPrepareMode = prepareMode || allArgs.contains("--prepare");
boolean isRunCommand = false;
String runCommand = null;
for(var e : allArgs) {
if(e.equals("--run")) {
isRunCommand = true;
continue;
}
if(isRunCommand) {
runCommand = e;
isRunCommand = false;
}
}
if(runCommand != null) {
localCommandHandler.eval(runCommand, false);
}
if (!isPrepareMode) {
server.run();
} else {
server.close();
}
}
public static void initGson(LaunchServerModulesManager modulesManager) {
Launcher.gsonManager = new LaunchServerGsonManager(modulesManager);
Launcher.gsonManager.initGson();
}
public static void registerAll() {
AuthCoreProvider.registerProviders();
PasswordVerifier.registerProviders();
TextureProvider.registerProviders();
Component.registerComponents();
ProtectHandler.registerHandlers();
WebSocketService.registerResponses();
AuthRequest.registerProviders();
GetAvailabilityAuthRequest.registerProviders();
OptionalAction.registerProviders();
OptionalTrigger.registerProviders();
MixProvider.registerProviders();
ProfileProvider.registerProviders();
UpdatesProvider.registerProviders();
}
private static void printExperimentalBranch() {
try(Reader reader = IOHelper.newReader(IOHelper.getResourceURL("experimental-build.json"))) {
ExperimentalBuild info = Launcher.gsonManager.configGson.fromJson(reader, ExperimentalBuild.class);
if(info.features == null || info.features.isEmpty()) {
return;
}
logger.warn("This is experimental build. Please do not use this in production");
logger.warn("Experimental features: [{}]", String.join(",", info.features));
for(var e : info.info) {
logger.warn(e);
}
} catch (Throwable e) {
logger.warn("Build information not found");
}
}
record ExperimentalBuild(List<String> features, List<String> info) {
}
public static void generateConfigIfNotExists(Path configFile, CommandHandler commandHandler, LaunchServer.LaunchServerEnv env) throws IOException {
if (IOHelper.isFile(configFile))
return;
// Create new config
logger.info("Creating LaunchServer config");
LaunchServerConfig newConfig = LaunchServerConfig.getDefault(env);
// Set server address
String address;
if (env.equals(LaunchServer.LaunchServerEnv.TEST)) {
address = "localhost";
newConfig.setProjectName("test");
} else {
address = System.getenv("ADDRESS");
if (address == null) {
address = System.getProperty("launchserver.address", null);
}
if (address == null) {
System.out.println("External launchServer address:port (default: localhost:9274): ");
address = commandHandler.readLine();
}
String projectName = System.getenv("PROJECTNAME");
if (projectName == null) {
projectName = System.getProperty("launchserver.projectname", null);
}
if (projectName == null) {
System.out.println("LaunchServer projectName: ");
projectName = commandHandler.readLine();
}
newConfig.setProjectName(projectName);
}
if (address == null || address.isEmpty()) {
logger.error("Address null. Using localhost:9274");
address = "localhost:9274";
}
if (newConfig.projectName == null || newConfig.projectName.isEmpty()) {
logger.error("ProjectName null. Using MineCraft");
newConfig.projectName = "MineCraft";
}
int port = 9274;
if(address.contains(":")) {
String portString = address.substring(address.indexOf(':')+1);
try {
port = Integer.parseInt(portString);
} catch (NumberFormatException e) {
logger.warn("Unknown port {}, using 9274", portString);
}
} else {
logger.info("Address {} doesn't contains port (you want to use nginx?)", address);
}
newConfig.netty.address = "ws://" + address + "/api";
newConfig.netty.downloadURL = "http://" + address + "/%dirname%/";
newConfig.netty.launcherURL = "http://" + address + "/Launcher.jar";
newConfig.netty.launcherEXEURL = "http://" + address + "/Launcher.exe";
newConfig.netty.binds[0].port = port;
// Write LaunchServer config
logger.info("Writing LaunchServer config file");
try (BufferedWriter writer = IOHelper.newWriter(configFile)) {
Launcher.gsonManager.configGson.toJson(newConfig, writer);
}
}
private static class BasicLaunchServerConfigManager implements LaunchServer.LaunchServerConfigManager {
private final Path configFile;
private final Path runtimeConfigFile;
public BasicLaunchServerConfigManager(Path configFile, Path runtimeConfigFile) {
this.configFile = configFile;
this.runtimeConfigFile = runtimeConfigFile;
}
@Override @Override
public LaunchServerConfig readConfig() throws IOException { public LaunchServerConfig readConfig() throws IOException {
LaunchServerConfig config1; LaunchServerConfig config1;
@ -156,105 +297,34 @@ public LaunchServerRuntimeConfig readRuntimeConfig() throws IOException {
@Override @Override
public void writeConfig(LaunchServerConfig config) throws IOException { public void writeConfig(LaunchServerConfig config) throws IOException {
try (Writer writer = IOHelper.newWriter(configFile)) { ByteArrayOutputStream output = new ByteArrayOutputStream();
try (Writer writer = IOHelper.newWriter(output)) {
if (Launcher.gsonManager.configGson != null) { if (Launcher.gsonManager.configGson != null) {
Launcher.gsonManager.configGson.toJson(config, writer); Launcher.gsonManager.configGson.toJson(config, writer);
} else { } else {
LogHelper.error("Error writing LaunchServer runtime config file. Gson is null"); logger.error("Error writing LaunchServer config file. Gson is null");
} }
} }
byte[] bytes = output.toByteArray();
if(bytes.length > 0) {
IOHelper.write(configFile, bytes);
}
} }
@Override @Override
public void writeRuntimeConfig(LaunchServerRuntimeConfig config) throws IOException { public void writeRuntimeConfig(LaunchServerRuntimeConfig config) throws IOException {
try (Writer writer = IOHelper.newWriter(runtimeConfigFile)) { ByteArrayOutputStream output = new ByteArrayOutputStream();
try (Writer writer = IOHelper.newWriter(output)) {
if (Launcher.gsonManager.configGson != null) { if (Launcher.gsonManager.configGson != null) {
Launcher.gsonManager.configGson.toJson(config, writer); Launcher.gsonManager.configGson.toJson(config, writer);
} else { } else {
LogHelper.error("Error writing LaunchServer runtime config file. Gson is null"); logger.error("Error writing LaunchServer runtime config file. Gson is null");
} }
} }
byte[] bytes = output.toByteArray();
if(bytes.length > 0) {
IOHelper.write(runtimeConfigFile, bytes);
} }
};
LaunchServer.LaunchServerDirectories directories = new LaunchServer.LaunchServerDirectories();
directories.dir = dir;
if (inDocker) {
Path parentLibraries = StarterAgent.libraries.toAbsolutePath().normalize().getParent();
directories.launcherLibrariesCompileDir = parentLibraries.resolve(LaunchServer.LaunchServerDirectories.LAUNCHERLIBRARIESCOMPILE_NAME);
directories.launcherLibrariesDir = parentLibraries.resolve(LaunchServer.LaunchServerDirectories.LAUNCHERLIBRARIES_NAME);
}
LaunchServer server = new LaunchServerBuilder()
.setDirectories(directories)
.setEnv(env)
.setCommandHandler(localCommandHandler)
.setPrivateKey(privateKey)
.setPublicKey(publicKey)
.setRuntimeConfig(runtimeConfig)
.setConfig(config)
.setModulesManager(modulesManager)
.setLaunchServerConfigManager(launchServerConfigManager)
.setCertificateManager(certificateManager)
.build();
server.run();
}
public static void initGson(LaunchServerModulesManager modulesManager) {
Launcher.gsonManager = new LaunchServerGsonManager(modulesManager);
Launcher.gsonManager.initGson();
}
public static void registerAll() {
AuthHandler.registerHandlers();
AuthProvider.registerProviders();
TextureProvider.registerProviders();
Component.registerComponents();
ProtectHandler.registerHandlers();
WebSocketService.registerResponses();
DaoProvider.registerProviders();
AuthRequest.registerProviders();
HWIDProvider.registerProviders();
OptionalAction.registerProviders();
}
public static void generateConfigIfNotExists(Path configFile, CommandHandler commandHandler, LaunchServer.LaunchServerEnv env) throws IOException {
if (IOHelper.isFile(configFile))
return;
// Create new config
LogHelper.info("Creating LaunchServer config");
LaunchServerConfig newConfig = LaunchServerConfig.getDefault(env);
// Set server address
String address;
if (env.equals(LaunchServer.LaunchServerEnv.TEST)) {
address = "localhost";
newConfig.setProjectName("test");
} else {
System.out.println("LaunchServer address(default: localhost): ");
address = commandHandler.readLine();
System.out.println("LaunchServer projectName: ");
newConfig.setProjectName(commandHandler.readLine());
}
if (address == null || address.isEmpty()) {
LogHelper.error("Address null. Using localhost");
address = "localhost";
}
if (newConfig.projectName == null || newConfig.projectName.isEmpty()) {
LogHelper.error("ProjectName null. Using MineCraft");
newConfig.projectName = "MineCraft";
}
newConfig.netty.address = "ws://" + address + ":9274/api";
newConfig.netty.downloadURL = "http://" + address + ":9274/%dirname%/";
newConfig.netty.launcherURL = "http://" + address + ":9274/Launcher.jar";
newConfig.netty.launcherEXEURL = "http://" + address + ":9274/Launcher.exe";
// Write LaunchServer config
LogHelper.info("Writing LaunchServer config file");
try (BufferedWriter writer = IOHelper.newWriter(configFile)) {
Launcher.gsonManager.configGson.toJson(newConfig, writer);
} }
} }
} }

View file

@ -0,0 +1,94 @@
package pro.gravit.launchserver;
import pro.gravit.launchserver.holder.LaunchServerControlHolder;
import pro.gravit.utils.helper.IOHelper;
import pro.gravit.utils.launch.ClassLoaderControl;
import pro.gravit.utils.launch.LaunchOptions;
import pro.gravit.utils.launch.ModuleLaunch;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.FileVisitOption;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
public class Main {
private static final List<String> classpathOnly = List.of("proguard", "jline", "progressbar", "kotlin");
private static final String LOG4J_PROPERTY = "log4j2.configurationFile";
private static final String DEBUG_PROPERTY = "launchserver.main.debug";
private static final String LIBRARIES_PROPERTY = "launchserver.dir.libraries";
private static boolean isClasspathOnly(Path path) {
var fileName = path.getFileName().toString();
for(var e : classpathOnly) {
if(fileName.contains(e)) {
return true;
}
}
return false;
}
private static void unpackLog4j() {
String log4jConfigurationFile = System.getProperty(LOG4J_PROPERTY);
if(log4jConfigurationFile == null) {
Path log4jConfigPath = Path.of("log4j2.xml");
if(!Files.exists(log4jConfigPath)) {
try(FileOutputStream output = new FileOutputStream(log4jConfigPath.toFile())) {
try(InputStream input = Main.class.getResourceAsStream("/log4j2.xml")) {
if(input == null) {
return;
}
input.transferTo(output);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
System.setProperty(LOG4J_PROPERTY, log4jConfigPath.toAbsolutePath().toString());
}
}
public static void main(String[] args) throws Throwable {
unpackLog4j();
ModuleLaunch launch = new ModuleLaunch();
LaunchOptions options = new LaunchOptions();
options.moduleConf = new LaunchOptions.ModuleConf();
Path librariesPath = Path.of(System.getProperty(LIBRARIES_PROPERTY, "libraries"));
List<Path> libraries;
try(Stream<Path> files = Files.walk(librariesPath, FileVisitOption.FOLLOW_LINKS)) {
libraries = new ArrayList<>(files.filter(e -> e.getFileName().toString().endsWith(".jar")).toList());
}
List<Path> classpath = new ArrayList<>();
List<String> modulepath = new ArrayList<>();
for(var l : libraries) {
if(isClasspathOnly(l)) {
classpath.add(l);
} else {
modulepath.add(l.toAbsolutePath().toString());
}
}
classpath.add(IOHelper.getCodeSource(LaunchServerStarter.class));
options.moduleConf.modulePath.addAll(modulepath);
options.moduleConf.modules.add("ALL-MODULE-PATH");
ClassLoaderControl control = launch.init(classpath, "natives", options);
control.clearLauncherPackages();
control.addLauncherPackage("pro.gravit.utils.launch");
control.addLauncherPackage("pro.gravit.launchserver.holder");
ModuleLayer.Controller controller = (ModuleLayer.Controller) control.getJava9ModuleController();
LaunchServerControlHolder.setControl(control);
LaunchServerControlHolder.setController(controller);
if(Boolean.getBoolean(DEBUG_PROPERTY)) {
for(var e : controller.layer().modules()) {
System.out.printf("Module %s\n", e.getName());
for(var p : e.getPackages()) {
System.out.printf("Package %s\n", p);
}
}
}
launch.launch("pro.gravit.launchserver.LaunchServerStarter", null, Arrays.asList(args));
}
}

View file

@ -2,6 +2,7 @@
import pro.gravit.utils.command.Command; import pro.gravit.utils.command.Command;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
/** /**
@ -15,4 +16,8 @@ public interface Reconfigurable {
* Value is a command object * Value is a command object
*/ */
Map<String, Command> getCommands(); Map<String, Command> getCommands();
default Map<String, Command> defaultCommandsMap() {
return new HashMap<>();
}
} }

View file

@ -1,13 +1,7 @@
package pro.gravit.launchserver; package pro.gravit.launchserver;
import java.io.IOException;
import java.lang.instrument.Instrumentation; import java.lang.instrument.Instrumentation;
import java.nio.file.*; import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.PosixFileAttributeView;
import java.nio.file.attribute.PosixFilePermission;
import java.util.*;
import java.util.jar.JarFile;
public final class StarterAgent { public final class StarterAgent {
@ -20,47 +14,6 @@ public static boolean isAgentStarted() {
} }
public static void premain(String agentArgument, Instrumentation inst) { public static void premain(String agentArgument, Instrumentation inst) {
StarterAgent.inst = inst; throw new UnsupportedOperationException("Please remove -javaagent option from start.sh");
libraries = Paths.get(Optional.ofNullable(agentArgument).map(String::trim).filter(e -> !e.isEmpty()).orElse("libraries"));
isStarted = true;
try {
Files.walkFileTree(libraries, Collections.singleton(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE, new StarterVisitor());
} catch (IOException e) {
e.printStackTrace(System.err);
}
}
private static final class StarterVisitor extends SimpleFileVisitor<Path> {
private static final Set<PosixFilePermission> DPERMS;
static {
Set<PosixFilePermission> perms = new HashSet<>(Arrays.asList(PosixFilePermission.values()));
perms.remove(PosixFilePermission.OTHERS_WRITE);
perms.remove(PosixFilePermission.GROUP_WRITE);
DPERMS = Collections.unmodifiableSet(perms);
}
private final boolean fixLib;
private StarterVisitor() {
Path filef = StarterAgent.libraries.resolve(".libraries_chmoded");
this.fixLib = !Files.exists(filef) && !Boolean.getBoolean("launcher.noLibrariesPosixPermsFix");
if (fixLib) {
try {
Files.deleteIfExists(filef);
Files.createFile(filef);
} catch (Throwable ignored) {
}
}
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
if (fixLib && Files.getFileAttributeView(file, PosixFileAttributeView.class) != null)
Files.setPosixFilePermissions(file, DPERMS);
if (file.toFile().getName().endsWith(".jar"))
inst.appendToSystemClassLoaderSearch(new JarFile(file.toFile()));
return super.visitFile(file, attrs);
}
} }
} }

View file

@ -4,14 +4,13 @@
import org.objectweb.asm.Opcodes; import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type; import org.objectweb.asm.Type;
import org.objectweb.asm.tree.*; import org.objectweb.asm.tree.*;
import pro.gravit.launcher.LauncherInject; import pro.gravit.launcher.core.LauncherInject;
import pro.gravit.launcher.LauncherInjectionConstructor; import pro.gravit.launcher.core.LauncherInjectionConstructor;
import pro.gravit.launchserver.binary.BuildContext; import pro.gravit.launchserver.binary.BuildContext;
import pro.gravit.launchserver.binary.tasks.MainBuildTask; import pro.gravit.launchserver.binary.tasks.MainBuildTask;
import java.util.*; import java.util.*;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
public class InjectClassAcceptor implements MainBuildTask.ASMTransformer { public class InjectClassAcceptor implements MainBuildTask.ASMTransformer {
@ -65,13 +64,19 @@ private static void visit(ClassNode classNode, Map<String, Object> values) {
return newClinitMethod; return newClinitMethod;
}); });
List<MethodNode> constructors = classNode.methods.stream().filter(method -> "<init>".equals(method.name)) List<MethodNode> constructors = classNode.methods.stream().filter(method -> "<init>".equals(method.name))
.collect(Collectors.toList()); .toList();
MethodNode initMethod = constructors.stream().filter(method -> method.invisibleAnnotations != null MethodNode initMethod = constructors.stream().filter(method -> method.invisibleAnnotations != null
&& method.invisibleAnnotations.stream().anyMatch(annotation -> INJECTED_CONSTRUCTOR_DESC.equals(annotation.desc))).findFirst() && method.invisibleAnnotations.stream().anyMatch(annotation -> INJECTED_CONSTRUCTOR_DESC.equals(annotation.desc))).findFirst()
.orElseGet(() -> constructors.stream().filter(method -> method.desc.equals("()V")).findFirst().orElse(null)); .orElseGet(() -> constructors.stream().filter(method -> method.desc.equals("()V")).findFirst().orElse(null));
classNode.fields.forEach(field -> { classNode.fields.forEach(field -> {
// Notice that fields that will be used with this algo should not have default // Notice that fields that will be used with this algo should not have default
// value by = ...; // value by = ...;
boolean isStatic = (field.access & Opcodes.ACC_STATIC) != 0;
injectTo(isStatic ? clinitMethod : initMethod, classNode, field, isStatic, values);
});
}
public static void injectTo(MethodNode initMethod, ClassNode classNode, FieldNode field, boolean isStatic, Map<String, Object> values) {
AnnotationNode valueAnnotation = field.invisibleAnnotations != null ? field.invisibleAnnotations.stream() AnnotationNode valueAnnotation = field.invisibleAnnotations != null ? field.invisibleAnnotations.stream()
.filter(annotation -> INJECTED_FIELD_DESC.equals(annotation.desc)).findFirst() .filter(annotation -> INJECTED_FIELD_DESC.equals(annotation.desc)).findFirst()
.orElse(null) : null; .orElse(null) : null;
@ -86,7 +91,7 @@ public void visit(final String name, final Object value) {
if ("value".equals(name)) { if ("value".equals(name)) {
if (value.getClass() != String.class) if (value.getClass() != String.class)
throw new IllegalArgumentException( throw new IllegalArgumentException(
String.format("Invalid annotation with value class %s", field.getClass().getName())); "Invalid annotation with value class %s".formatted(field.getClass().getName()));
valueName.set(value.toString()); valueName.set(value.toString());
} }
} }
@ -98,32 +103,33 @@ public void visit(final String name, final Object value) {
return; return;
} }
Object value = values.get(valueName.get()); Object value = values.get(valueName.get());
if ((field.access & Opcodes.ACC_STATIC) != 0) { //if ((field.access & Opcodes.ACC_STATIC) != 0) {
if (isStatic) {
if (primitiveLDCDescriptors.contains(field.desc) && primitiveLDCClasses.contains(value.getClass())) { if (primitiveLDCDescriptors.contains(field.desc) && primitiveLDCClasses.contains(value.getClass())) {
field.value = value; field.value = value;
return; return;
} }
List<FieldInsnNode> putStaticNodes = Arrays.stream(clinitMethod.instructions.toArray()) List<FieldInsnNode> putStaticNodes = Arrays.stream(initMethod.instructions.toArray())
.filter(node -> node instanceof FieldInsnNode && node.getOpcode() == Opcodes.PUTSTATIC).map(p -> (FieldInsnNode) p) .filter(node -> node instanceof FieldInsnNode && node.getOpcode() == Opcodes.PUTSTATIC).map(p -> (FieldInsnNode) p)
.filter(node -> node.owner.equals(classNode.name) && node.name.equals(field.name) && node.desc.equals(field.desc)).collect(Collectors.toList()); .filter(node -> node.owner.equals(classNode.name) && node.name.equals(field.name) && node.desc.equals(field.desc)).toList();
InsnList setter = serializeValue(value); InsnList setter = serializeValue(value);
if (putStaticNodes.isEmpty()) { if (putStaticNodes.isEmpty()) {
setter.add(new FieldInsnNode(Opcodes.PUTSTATIC, classNode.name, field.name, field.desc)); setter.add(new FieldInsnNode(Opcodes.PUTSTATIC, classNode.name, field.name, field.desc));
Arrays.stream(clinitMethod.instructions.toArray()).filter(node -> node.getOpcode() == Opcodes.RETURN) Arrays.stream(initMethod.instructions.toArray()).filter(node -> node.getOpcode() == Opcodes.RETURN)
.forEach(node -> clinitMethod.instructions.insertBefore(node, setter)); .forEach(node -> initMethod.instructions.insertBefore(node, setter));
} else { } else {
setter.insert(new InsnNode(Type.getType(field.desc).getSize() == 1 ? Opcodes.POP : Opcodes.POP2)); setter.insert(new InsnNode(Type.getType(field.desc).getSize() == 1 ? Opcodes.POP : Opcodes.POP2));
for (FieldInsnNode fieldInsnNode : putStaticNodes) { for (FieldInsnNode fieldInsnNode : putStaticNodes) {
clinitMethod.instructions.insertBefore(fieldInsnNode, setter); initMethod.instructions.insertBefore(fieldInsnNode, setter);
} }
} }
} else { } else {
if (initMethod == null) { if (initMethod == null) {
throw new IllegalArgumentException(String.format("Not found init in target: %s", classNode.name)); throw new IllegalArgumentException("Not found init in target: %s".formatted(classNode.name));
} }
List<FieldInsnNode> putFieldNodes = Arrays.stream(initMethod.instructions.toArray()) List<FieldInsnNode> putFieldNodes = Arrays.stream(initMethod.instructions.toArray())
.filter(node -> node instanceof FieldInsnNode && node.getOpcode() == Opcodes.PUTFIELD).map(p -> (FieldInsnNode) p) .filter(node -> node instanceof FieldInsnNode && node.getOpcode() == Opcodes.PUTFIELD).map(p -> (FieldInsnNode) p)
.filter(node -> node.owner.equals(classNode.name) && node.name.equals(field.name) && node.desc.equals(field.desc)).collect(Collectors.toList()); .filter(node -> node.owner.equals(classNode.name) && node.name.equals(field.name) && node.desc.equals(field.desc)).toList();
InsnList setter = serializeValue(value); InsnList setter = serializeValue(value);
if (putFieldNodes.isEmpty()) { if (putFieldNodes.isEmpty()) {
setter.insert(new VarInsnNode(Opcodes.ALOAD, 0)); setter.insert(new VarInsnNode(Opcodes.ALOAD, 0));
@ -138,7 +144,6 @@ public void visit(final String name, final Object value) {
} }
} }
} }
});
} }
private static Serializer<?> serializerClass(int opcode) { private static Serializer<?> serializerClass(int opcode) {
@ -167,8 +172,7 @@ private static InsnList serializeValue(Object value) {
return ((Serializer) serializerEntry.getValue()).serialize(value); return ((Serializer) serializerEntry.getValue()).serialize(value);
} }
} }
throw new UnsupportedOperationException(String.format("Serialization of type %s is not supported", throw new UnsupportedOperationException("Serialization of type %s is not supported".formatted(value.getClass()));
value.getClass()));
} }
public static boolean isSerializableValue(Object value) { public static boolean isSerializableValue(Object value) {

View file

@ -149,10 +149,7 @@ public static int opcodeEmulation(AbstractInsnNode e) {
break; break;
case INVOKEVIRTUAL: case INVOKEVIRTUAL:
case INVOKESPECIAL: case INVOKESPECIAL:
case INVOKEINTERFACE: case INVOKEINTERFACE, INVOKESTATIC:
stackSize += doMethodEmulation(((MethodInsnNode) e).desc);
break;
case INVOKESTATIC:
stackSize += doMethodEmulation(((MethodInsnNode) e).desc); stackSize += doMethodEmulation(((MethodInsnNode) e).desc);
break; break;
case INVOKEDYNAMIC: case INVOKEDYNAMIC:

View file

@ -1,8 +1,14 @@
package pro.gravit.launchserver.auth; package pro.gravit.launchserver.auth;
import pro.gravit.launcher.base.events.request.AuthRequestEvent;
import java.io.IOException; import java.io.IOException;
import java.io.Serial;
import java.util.List;
import java.util.stream.Collectors;
public final class AuthException extends IOException { public final class AuthException extends IOException {
@Serial
private static final long serialVersionUID = -2586107832847245863L; private static final long serialVersionUID = -2586107832847245863L;
@ -10,6 +16,28 @@ public AuthException(String message) {
super(message); super(message);
} }
public AuthException(String message, Throwable cause) {
super(message, cause);
}
public static AuthException need2FA() {
return new AuthException(AuthRequestEvent.TWO_FACTOR_NEED_ERROR_MESSAGE);
}
public static AuthException needMFA(List<Integer> factors) {
String message = AuthRequestEvent.ONE_FACTOR_NEED_ERROR_MESSAGE_PREFIX
.concat(factors.stream().map(String::valueOf).collect(Collectors.joining(".")));
return new AuthException(message);
}
public static AuthException wrongPassword() {
return new AuthException(AuthRequestEvent.WRONG_PASSWORD_ERROR_MESSAGE);
}
public static AuthException userNotFound() {
return new AuthException(AuthRequestEvent.USER_NOT_FOUND_ERROR_MESSAGE);
}
@Override @Override
public String toString() { public String toString() {
return getMessage(); return getMessage();

View file

@ -1,64 +1,113 @@
package pro.gravit.launchserver.auth; package pro.gravit.launchserver.auth;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import pro.gravit.launchserver.LaunchServer; import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.auth.handler.AuthHandler; import pro.gravit.launchserver.auth.core.AuthCoreProvider;
import pro.gravit.launchserver.auth.provider.AuthProvider; import pro.gravit.launchserver.auth.mix.MixProvider;
import pro.gravit.launchserver.auth.texture.TextureProvider; import pro.gravit.launchserver.auth.texture.TextureProvider;
import java.io.IOException; import java.io.IOException;
import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set;
public class AuthProviderPair { public final class AuthProviderPair {
public final boolean isDefault = true; private transient final Logger logger = LogManager.getLogger();
public AuthProvider provider; public boolean isDefault = true;
public AuthHandler handler; public AuthCoreProvider core;
public TextureProvider textureProvider; public TextureProvider textureProvider;
public Map<String, MixProvider> mixes;
public Map<String, String> links; public Map<String, String> links;
public transient String name; public transient String name;
public transient Set<String> features;
public String displayName; public String displayName;
public boolean visible = true;
public AuthProviderPair(AuthProvider provider, AuthHandler handler, TextureProvider textureProvider) { public AuthProviderPair() {
this.provider = provider; }
this.handler = handler;
public AuthProviderPair(AuthCoreProvider core, TextureProvider textureProvider) {
this.core = core;
this.textureProvider = textureProvider; this.textureProvider = textureProvider;
} }
public static Set<String> getFeatures(Class<?> clazz) {
Set<String> list = new HashSet<>();
getFeatures(clazz, list);
return list;
}
public Set<String> getFeatures() {
return features;
}
public static void getFeatures(Class<?> clazz, Set<String> list) {
Feature[] features = clazz.getAnnotationsByType(Feature.class);
for (Feature feature : features) {
list.add(feature.value());
}
Class<?> superClass = clazz.getSuperclass();
if (superClass != null && superClass != Object.class) {
getFeatures(superClass, list);
}
Class<?>[] interfaces = clazz.getInterfaces();
for (Class<?> i : interfaces) {
getFeatures(i, list);
}
}
public <T> T isSupport(Class<T> clazz) {
if (core == null) return null;
T result = core.isSupport(clazz);
if (result == null && mixes != null) {
for(var m : mixes.values()) {
result = m.isSupport(clazz);
if(result != null) {
break;
}
}
}
return result;
}
public void init(LaunchServer srv, String name) { public void init(LaunchServer srv, String name) {
this.name = name; this.name = name;
if (links != null) link(srv); if (links != null) link(srv);
if (provider == null) throw new NullPointerException(String.format("Auth %s provider null", name)); core.init(srv, this);
if (handler == null) throw new NullPointerException(String.format("Auth %s handler null", name)); features = new HashSet<>();
if (textureProvider == null) getFeatures(core.getClass(), features);
throw new NullPointerException(String.format("Auth %s textureProvider null", name)); if(mixes != null) {
provider.init(srv); for(var m : mixes.values()) {
handler.init(srv); m.init(srv, core);
getFeatures(m.getClass(), features);
}
}
} }
public void link(LaunchServer srv) { public void link(LaunchServer srv) {
links.forEach((k, v) -> { links.forEach((k, v) -> {
AuthProviderPair pair = srv.config.getAuthProviderPair(v); AuthProviderPair pair = srv.config.getAuthProviderPair(v);
if (pair == null) { if (pair == null) {
throw new NullPointerException(String.format("Auth %s link failed. Pair %s not found", name, v)); throw new NullPointerException("Auth %s link failed. Pair %s not found".formatted(name, v));
} }
if ("provider".equals(k)) { if ("core".equals(k)) {
if (pair.provider == null) if (pair.core == null)
throw new NullPointerException(String.format("Auth %s link failed. %s.provider is null", name, v)); throw new NullPointerException("Auth %s link failed. %s.core is null".formatted(name, v));
provider = pair.provider; core = pair.core;
} else if ("handler".equals(k)) {
if (pair.handler == null)
throw new NullPointerException(String.format("Auth %s link failed. %s.handler is null", name, v));
handler = pair.handler;
} else if ("textureProvider".equals(k)) {
if (pair.textureProvider == null)
throw new NullPointerException(String.format("Auth %s link failed. %s.textureProvider is null", name, v));
textureProvider = pair.textureProvider;
} }
}); });
} }
public void close() throws IOException { public void close() throws IOException {
provider.close(); core.close();
handler.close(); if (textureProvider != null) {
textureProvider.close(); textureProvider.close();
} }
if(mixes != null) {
for(var m : mixes.values()) {
m.close();
}
}
}
} }

View file

@ -0,0 +1,10 @@
package pro.gravit.launchserver.auth;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Repeatable(Features.class)
public @interface Feature {
String value();
}

View file

@ -0,0 +1,12 @@
package pro.gravit.launchserver.auth;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Features {
Feature[] value();
}

View file

@ -0,0 +1,62 @@
package pro.gravit.launchserver.auth;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
import java.util.function.Consumer;
public class HikariSQLSourceConfig implements SQLSourceConfig {
private transient volatile HikariDataSource dataSource;
private String dsClass;
private Properties dsProps;
private String driverClass;
private String jdbcUrl;
private String username;
private String password;
private boolean initializeAtStart;
public void init() {
if(initializeAtStart) {
initializeConnection();
}
}
private void initializeConnection() {
if (dataSource != null) {
return;
}
HikariConfig config = new HikariConfig();
consumeIfNotNull(config::setDataSourceClassName, dsClass);
consumeIfNotNull(config::setDataSourceProperties, dsProps);
consumeIfNotNull(config::setDriverClassName, driverClass);
consumeIfNotNull(config::setJdbcUrl, jdbcUrl);
consumeIfNotNull(config::setUsername, username);
consumeIfNotNull(config::setPassword, password);
this.dataSource = new HikariDataSource(config);
}
@Override
public Connection getConnection() throws SQLException {
if(dataSource == null && !initializeAtStart) {
synchronized (this) {
initializeConnection();
}
}
return dataSource.getConnection();
}
@Override
public void close() {
dataSource.close();
}
private static <T> void consumeIfNotNull(Consumer<T> consumer, T val) {
if (val != null) {
consumer.accept(val);
}
}
}

View file

@ -3,14 +3,17 @@
import com.mysql.cj.jdbc.MysqlDataSource; import com.mysql.cj.jdbc.MysqlDataSource;
import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource; import com.zaxxer.hikari.HikariDataSource;
import pro.gravit.utils.helper.LogHelper; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import pro.gravit.utils.helper.VerifyHelper; import pro.gravit.utils.helper.VerifyHelper;
import javax.sql.DataSource; import javax.sql.DataSource;
import java.sql.Connection; import java.sql.Connection;
import java.sql.SQLException; import java.sql.SQLException;
public final class MySQLSourceConfig implements AutoCloseable { import static java.util.concurrent.TimeUnit.MINUTES;
public final class MySQLSourceConfig implements AutoCloseable, SQLSourceConfig {
public static final int TIMEOUT = VerifyHelper.verifyInt( public static final int TIMEOUT = VerifyHelper.verifyInt(
Integer.parseUnsignedInt(System.getProperty("launcher.mysql.idleTimeout", Integer.toString(5000))), Integer.parseUnsignedInt(System.getProperty("launcher.mysql.idleTimeout", Integer.toString(5000))),
@ -21,6 +24,7 @@ public final class MySQLSourceConfig implements AutoCloseable {
// Instance // Instance
private transient final String poolName; private transient final String poolName;
private transient final Logger logger = LogManager.getLogger();
// Config // Config
private String address; private String address;
@ -30,8 +34,9 @@ public final class MySQLSourceConfig implements AutoCloseable {
private String username; private String username;
private String password; private String password;
private String database; private String database;
private String timeZone; private String timezone;
private boolean enableHikari; private long hikariMaxLifetime = MINUTES.toMillis(30);
private boolean useHikari;
// Cache // Cache
private transient DataSource source; private transient DataSource source;
@ -91,24 +96,28 @@ public synchronized Connection getConnection() throws SQLException {
mysqlSource.setPassword(password); mysqlSource.setPassword(password);
mysqlSource.setDatabaseName(database); mysqlSource.setDatabaseName(database);
mysqlSource.setTcpNoDelay(true); mysqlSource.setTcpNoDelay(true);
if (timeZone != null) mysqlSource.setServerTimezone(timeZone); if (timezone != null) mysqlSource.setServerTimezone(timezone);
hikari = false; hikari = false;
// Try using HikariCP // Try using HikariCP
source = mysqlSource; source = mysqlSource;
if (enableHikari) { if (useHikari) {
try { try {
Class.forName("com.zaxxer.hikari.HikariDataSource"); Class.forName("com.zaxxer.hikari.HikariDataSource");
hikari = true; // Used for shutdown. Not instanceof because of possible classpath error hikari = true; // Used for shutdown. Not instanceof because of possible classpath error
HikariConfig cfg = new HikariConfig(); HikariConfig hikariConfig = new HikariConfig();
cfg.setDataSource(mysqlSource); hikariConfig.setDataSource(mysqlSource);
cfg.setPoolName(poolName); hikariConfig.setPoolName(poolName);
cfg.setMaximumPoolSize(MAX_POOL_SIZE); hikariConfig.setMinimumIdle(1);
hikariConfig.setMaximumPoolSize(MAX_POOL_SIZE);
hikariConfig.setConnectionTestQuery("SELECT 1");
hikariConfig.setConnectionTimeout(1000);
hikariConfig.setLeakDetectionThreshold(2000);
hikariConfig.setMaxLifetime(hikariMaxLifetime);
// Set HikariCP pool // Set HikariCP pool
// Replace source with hds // Replace source with hds
source = new HikariDataSource(cfg); source = new HikariDataSource(hikariConfig);
LogHelper.warning("HikariCP pooling enabled for '%s'", poolName);
} catch (ClassNotFoundException ignored) { } catch (ClassNotFoundException ignored) {
LogHelper.debug("HikariCP isn't in classpath for '%s'", poolName); logger.debug("HikariCP isn't in classpath for '{}'", poolName);
} }
} }

View file

@ -1,25 +1,28 @@
package pro.gravit.launchserver.auth; package pro.gravit.launchserver.auth;
import com.zaxxer.hikari.HikariDataSource; import com.zaxxer.hikari.HikariDataSource;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.postgresql.ds.PGSimpleDataSource; import org.postgresql.ds.PGSimpleDataSource;
import pro.gravit.utils.helper.LogHelper;
import pro.gravit.utils.helper.VerifyHelper; import pro.gravit.utils.helper.VerifyHelper;
import javax.sql.DataSource; import javax.sql.DataSource;
import java.sql.Connection; import java.sql.Connection;
import java.sql.SQLException; import java.sql.SQLException;
public final class PostgreSQLSourceConfig implements AutoCloseable { import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.concurrent.TimeUnit.SECONDS;
public final class PostgreSQLSourceConfig implements AutoCloseable, SQLSourceConfig {
public static final int TIMEOUT = VerifyHelper.verifyInt( public static final int TIMEOUT = VerifyHelper.verifyInt(
Integer.parseUnsignedInt(System.getProperty("launcher.postgresql.idleTimeout", Integer.toString(5000))), Integer.parseUnsignedInt(System.getProperty("launcher.postgresql.idleTimeout", Integer.toString(5000))),
VerifyHelper.POSITIVE, "launcher.postgresql.idleTimeout can't be <= 5000"); VerifyHelper.POSITIVE, "launcher.postgresql.idleTimeout can't be <= 5000");
private static final int MAX_POOL_SIZE = VerifyHelper.verifyInt( private static final int MAX_POOL_SIZE = VerifyHelper.verifyInt(
Integer.parseUnsignedInt(System.getProperty("launcher.postgresql.maxPoolSize", Integer.toString(3))), Integer.parseUnsignedInt(System.getProperty("launcher.postgresql.maxPoolSize", Integer.toString(3))),
VerifyHelper.POSITIVE, "launcher.postgresql.maxPoolSize can't be <= 0"); VerifyHelper.POSITIVE, "launcher.postgresql.maxPoolSize can't be <= 0");
private transient final Logger logger = LogManager.getLogger();
// Instance // Instance
private String poolName; private String poolName;
// Config // Config
private String[] addresses; private String[] addresses;
private int[] ports; private int[] ports;
@ -27,9 +30,11 @@ public final class PostgreSQLSourceConfig implements AutoCloseable {
private String password; private String password;
private String database; private String database;
private long hikariMaxLifetime = MINUTES.toMillis(30); // 30 minutes
// Cache // Cache
private DataSource source; private transient DataSource source;
private boolean hikari; private transient boolean hikari;
@Override @Override
public synchronized void close() { public synchronized void close() {
@ -65,13 +70,14 @@ public synchronized Connection getConnection() throws SQLException {
hikariSource.setPoolName(poolName); hikariSource.setPoolName(poolName);
hikariSource.setMinimumIdle(0); hikariSource.setMinimumIdle(0);
hikariSource.setMaximumPoolSize(MAX_POOL_SIZE); hikariSource.setMaximumPoolSize(MAX_POOL_SIZE);
hikariSource.setIdleTimeout(TIMEOUT * 1000L); hikariSource.setIdleTimeout(SECONDS.toMillis(TIMEOUT));
hikariSource.setMaxLifetime(hikariMaxLifetime);
// Replace source with hds // Replace source with hds
source = hikariSource; source = hikariSource;
LogHelper.info("HikariCP pooling enabled for '%s'", poolName); logger.info("HikariCP pooling enabled for '{}'", poolName);
} catch (ClassNotFoundException ignored) { } catch (ClassNotFoundException ignored) {
LogHelper.warning("HikariCP isn't in classpath for '%s'", poolName); logger.warn("HikariCP isn't in classpath for '{}'", poolName);
} }
} }
return source.getConnection(); return source.getConnection();

View file

@ -0,0 +1,10 @@
package pro.gravit.launchserver.auth;
import java.sql.Connection;
import java.sql.SQLException;
public interface SQLSourceConfig {
Connection getConnection() throws SQLException;
void close();
}

View file

@ -0,0 +1,420 @@
package pro.gravit.launchserver.auth.core;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.JwtException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import pro.gravit.launcher.base.ClientPermissions;
import pro.gravit.launcher.base.request.auth.AuthRequest;
import pro.gravit.launcher.base.request.auth.password.AuthPlainPassword;
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.auth.AuthException;
import pro.gravit.launchserver.auth.AuthProviderPair;
import pro.gravit.launchserver.auth.MySQLSourceConfig;
import pro.gravit.launchserver.auth.SQLSourceConfig;
import pro.gravit.launchserver.auth.core.interfaces.provider.AuthSupportSudo;
import pro.gravit.launchserver.auth.password.PasswordVerifier;
import pro.gravit.launchserver.helper.LegacySessionHelper;
import pro.gravit.launchserver.manangers.AuthManager;
import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.response.auth.AuthResponse;
import pro.gravit.utils.helper.SecurityHelper;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.Clock;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import static java.util.concurrent.TimeUnit.HOURS;
import static java.util.concurrent.TimeUnit.SECONDS;
public abstract class AbstractSQLCoreProvider extends AuthCoreProvider implements AuthSupportSudo {
public final transient Logger logger = LogManager.getLogger();
public long expireSeconds = HOURS.toSeconds(1);
public String uuidColumn;
public String usernameColumn;
public String accessTokenColumn;
public String passwordColumn;
public String serverIDColumn;
public String table;
public String permissionsTable;
public String permissionsPermissionColumn;
public String permissionsUUIDColumn;
public String rolesTable;
public String rolesNameColumn;
public String rolesUUIDColumn;
public PasswordVerifier passwordVerifier;
public String customQueryByUUIDSQL;
public String customQueryByUsernameSQL;
public String customQueryByLoginSQL;
public String customQueryPermissionsByUUIDSQL;
public String customQueryRolesByUserUUID;
public String customUpdateAuthSQL;
public String customUpdateServerIdSQL;
// Prepared SQL queries
public transient String queryByUUIDSQL;
public transient String queryByUsernameSQL;
public transient String queryByLoginSQL;
public transient String queryPermissionsByUUIDSQL;
public transient String queryRolesByUserUUID;
public transient String updateAuthSQL;
public transient String updateServerIDSQL;
public abstract SQLSourceConfig getSQLConfig();
@Override
public User getUserByUsername(String username) {
try {
return queryUser(queryByUsernameSQL, username);
} catch (Exception e) {
logger.error("SQL error", e);
return null;
}
}
@Override
public User getUserByUUID(UUID uuid) {
try {
return queryUser(queryByUUIDSQL, uuid.toString());
} catch (Exception e) {
logger.error("SQL error", e);
return null;
}
}
@Override
public User getUserByLogin(String login) {
try {
return queryUser(queryByLoginSQL, login);
} catch (Exception e) {
logger.error("SQL error", e);
return null;
}
}
@Override
public UserSession getUserSessionByOAuthAccessToken(String accessToken) throws OAuthAccessTokenExpired {
try {
var info = LegacySessionHelper.getJwtInfoFromAccessToken(accessToken, server.keyAgreementManager.ecdsaPublicKey);
var user = (SQLUser) getUserByUUID(info.uuid());
if (user == null) {
return null;
}
return createSession(user);
} catch (ExpiredJwtException e) {
throw new OAuthAccessTokenExpired();
} catch (JwtException e) {
return null;
}
}
@Override
public AuthManager.AuthReport refreshAccessToken(String refreshToken, AuthResponse.AuthContext context) {
String[] parts = refreshToken.split("\\.");
if (parts.length != 2) {
return null;
}
String username = parts[0];
String token = parts[1];
var user = (SQLUser) getUserByUsername(username);
if (user == null || user.password == null) {
return null;
}
var realToken = LegacySessionHelper.makeRefreshTokenFromPassword(username, user.password, server.keyAgreementManager.legacySalt);
if (!token.equals(realToken)) {
return null;
}
var accessToken = LegacySessionHelper.makeAccessJwtTokenFromString(user, LocalDateTime.now(Clock.systemUTC()).plusSeconds(expireSeconds), server.keyAgreementManager.ecdsaPrivateKey);
return new AuthManager.AuthReport(null, accessToken, refreshToken, SECONDS.toMillis(expireSeconds), createSession(user));
}
@Override
public AuthManager.AuthReport authorize(String login, AuthResponse.AuthContext context, AuthRequest.AuthPasswordInterface password, boolean minecraftAccess) throws IOException {
SQLUser user = (SQLUser) getUserByLogin(login);
if (user == null) {
throw AuthException.userNotFound();
}
AuthPlainPassword plainPassword = (AuthPlainPassword) password;
if (plainPassword == null) {
throw AuthException.wrongPassword();
}
if (!passwordVerifier.check(user.password, plainPassword.password)) {
throw AuthException.wrongPassword();
}
SQLUserSession session = createSession(user);
var accessToken = LegacySessionHelper.makeAccessJwtTokenFromString(user, LocalDateTime.now(Clock.systemUTC()).plusSeconds(expireSeconds), server.keyAgreementManager.ecdsaPrivateKey);
var refreshToken = user.username.concat(".").concat(LegacySessionHelper.makeRefreshTokenFromPassword(user.username, user.password, server.keyAgreementManager.legacySalt));
if (minecraftAccess) {
String minecraftAccessToken = SecurityHelper.randomStringToken();
updateAuth(user, minecraftAccessToken);
return AuthManager.AuthReport.ofOAuthWithMinecraft(minecraftAccessToken, accessToken, refreshToken, SECONDS.toMillis(expireSeconds), session);
} else {
return AuthManager.AuthReport.ofOAuth(accessToken, refreshToken, SECONDS.toMillis(expireSeconds), session);
}
}
@Override
public AuthManager.AuthReport sudo(User user, boolean shadow) throws IOException {
SQLUser sqlUser = (SQLUser) user;
SQLUserSession session = createSession(sqlUser);
var accessToken = LegacySessionHelper.makeAccessJwtTokenFromString(sqlUser, LocalDateTime.now(Clock.systemUTC()).plusSeconds(expireSeconds), server.keyAgreementManager.ecdsaPrivateKey);
var refreshToken = sqlUser.username.concat(".").concat(LegacySessionHelper.makeRefreshTokenFromPassword(sqlUser.username, sqlUser.password, server.keyAgreementManager.legacySalt));
String minecraftAccessToken = SecurityHelper.randomStringToken();
updateAuth(user, minecraftAccessToken);
return AuthManager.AuthReport.ofOAuthWithMinecraft(minecraftAccessToken, accessToken, refreshToken, SECONDS.toMillis(expireSeconds), session);
}
@Override
public User checkServer(Client client, String username, String serverID) {
SQLUser user = (SQLUser) getUserByUsername(username);
if (user == null) {
return null;
}
if (user.getUsername().equals(username) && user.getServerId().equals(serverID)) {
return user;
}
return null;
}
@Override
public boolean joinServer(Client client, String username, UUID uuid, String accessToken, String serverID) throws IOException {
SQLUser user = (SQLUser) client.getUser();
if (user == null) return false;
return (uuid == null ? user.getUsername().equals(username) : user.getUUID().equals(uuid)) && user.getAccessToken().equals(accessToken) && updateServerID(user, serverID);
}
@Override
public void init(LaunchServer server, AuthProviderPair pair) {
super.init(server, pair);
if (getSQLConfig() == null) logger.error("SQLHolder cannot be null");
if (uuidColumn == null) logger.error("uuidColumn cannot be null");
if (usernameColumn == null) logger.error("usernameColumn cannot be null");
if (accessTokenColumn == null) logger.error("accessTokenColumn cannot be null");
if (serverIDColumn == null) logger.error("serverIDColumn cannot be null");
if (table == null) logger.error("table cannot be null");
// Prepare SQL queries
String userInfoCols = makeUserCols();
queryByUUIDSQL = customQueryByUUIDSQL != null ? customQueryByUUIDSQL :
"SELECT %s FROM %s WHERE %s=? LIMIT 1".formatted(userInfoCols, table, uuidColumn);
queryByUsernameSQL = customQueryByUsernameSQL != null ? customQueryByUsernameSQL :
"SELECT %s FROM %s WHERE %s=? LIMIT 1".formatted(userInfoCols, table, usernameColumn);
queryByLoginSQL = customQueryByLoginSQL != null ? customQueryByLoginSQL : queryByUsernameSQL;
updateAuthSQL = customUpdateAuthSQL != null ? customUpdateAuthSQL :
"UPDATE %s SET %s=?, %s=NULL WHERE %s=?".formatted(table, accessTokenColumn, serverIDColumn, uuidColumn);
updateServerIDSQL = customUpdateServerIdSQL != null ? customUpdateServerIdSQL :
"UPDATE %s SET %s=? WHERE %s=?".formatted(table, serverIDColumn, uuidColumn);
if (isEnabledPermissions()) {
if(isEnabledRoles()) {
queryPermissionsByUUIDSQL = customQueryPermissionsByUUIDSQL != null ? customQueryPermissionsByUUIDSQL :
"WITH RECURSIVE req AS (\n" +
"SELECT p."+permissionsPermissionColumn+" FROM "+permissionsTable+" p WHERE p."+permissionsUUIDColumn+" = ?\n" +
"UNION ALL\n" +
"SELECT p."+permissionsPermissionColumn+" FROM "+permissionsTable+" p\n" +
"INNER JOIN "+rolesTable+" r ON p."+permissionsUUIDColumn+" = r."+rolesUUIDColumn+"\n" +
"INNER JOIN req ON r."+rolesUUIDColumn+"=substring(req."+permissionsPermissionColumn+" from 6) or r.name=substring(req."+permissionsPermissionColumn+" from 6)\n" +
") SELECT * FROM req";
queryRolesByUserUUID = customQueryRolesByUserUUID != null ? customQueryRolesByUserUUID : "SELECT r." + rolesNameColumn + " FROM " + rolesTable + " r\n" +
"INNER JOIN " + permissionsTable + " pr ON r." + rolesUUIDColumn + "=substring(pr." + permissionsPermissionColumn + " from 6) or r." + rolesNameColumn + "=substring(pr." + permissionsPermissionColumn + " from 6)\n" +
"WHERE pr." + permissionsUUIDColumn + " = ?";
} else {
queryPermissionsByUUIDSQL = customQueryPermissionsByUUIDSQL != null ? customQueryPermissionsByUUIDSQL :
"SELECT (%s) FROM %s WHERE %s=?".formatted(permissionsPermissionColumn, permissionsTable, permissionsUUIDColumn);
}
}
}
protected String makeUserCols() {
return "%s, %s, %s, %s, %s".formatted(uuidColumn, usernameColumn, accessTokenColumn, serverIDColumn, passwordColumn);
}
protected void updateAuth(User user, String accessToken) throws IOException {
try (Connection c = getSQLConfig().getConnection()) {
SQLUser SQLUser = (SQLUser) user;
SQLUser.accessToken = accessToken;
PreparedStatement s = c.prepareStatement(updateAuthSQL);
s.setString(1, accessToken);
s.setString(2, user.getUUID().toString());
s.setQueryTimeout(MySQLSourceConfig.TIMEOUT);
s.executeUpdate();
} catch (SQLException e) {
throw new IOException(e);
}
}
protected boolean updateServerID(User user, String serverID) throws IOException {
try (Connection c = getSQLConfig().getConnection()) {
SQLUser SQLUser = (SQLUser) user;
SQLUser.serverId = serverID;
PreparedStatement s = c.prepareStatement(updateServerIDSQL);
s.setString(1, serverID);
s.setString(2, user.getUUID().toString());
s.setQueryTimeout(MySQLSourceConfig.TIMEOUT);
return s.executeUpdate() > 0;
} catch (SQLException e) {
throw new IOException(e);
}
}
@Override
public void close() {
getSQLConfig().close();
}
protected SQLUser constructUser(ResultSet set) throws SQLException {
return set.next() ? new SQLUser(UUID.fromString(set.getString(uuidColumn)), set.getString(usernameColumn),
set.getString(accessTokenColumn), set.getString(serverIDColumn), set.getString(passwordColumn)) : null;
}
public ClientPermissions requestPermissions (String uuid) throws SQLException
{
return new ClientPermissions(isEnabledRoles() ? queryRolesNames(queryRolesByUserUUID,uuid) : new ArrayList<>(),
isEnabledPermissions() ? queryPermissions(queryPermissionsByUUIDSQL,uuid) : new ArrayList<>());
}
private SQLUser queryUser(String sql, String value) throws SQLException {
SQLUser user;
try (Connection c = getSQLConfig().getConnection()) {
PreparedStatement s = c.prepareStatement(sql);
s.setString(1, value);
s.setQueryTimeout(MySQLSourceConfig.TIMEOUT);
try (ResultSet set = s.executeQuery()) {
user = constructUser(set);
}
}
if(user != null) {
user.permissions = requestPermissions(user.uuid.toString());
}
return user;
}
private List<String> queryPermissions(String sql, String value) throws SQLException {
try (Connection c = getSQLConfig().getConnection()) {
PreparedStatement s = c.prepareStatement(sql);
s.setString(1, value);
s.setQueryTimeout(MySQLSourceConfig.TIMEOUT);
ResultSet set = s.executeQuery();
List<String> perms = new ArrayList<>();
while (set.next())
perms.add(set.getString(permissionsPermissionColumn));
return perms;
}
}
protected SQLUserSession createSession(SQLUser user) {
return new SQLUserSession(user);
}
public boolean isEnabledPermissions() {
return permissionsPermissionColumn != null;
}
public boolean isEnabledRoles() {
return rolesNameColumn != null;
}
private List<String> queryRolesNames(String sql, String value) throws SQLException {
try (Connection c = getSQLConfig().getConnection()) {
PreparedStatement s = c.prepareStatement(sql);
s.setString(1, value);
s.setQueryTimeout(MySQLSourceConfig.TIMEOUT);
ResultSet set = s.executeQuery();
List<String> perms = new ArrayList<>();
while (set.next())
perms.add(set.getString(rolesNameColumn));
return perms;
}
}
public static class SQLUser implements User {
protected final UUID uuid;
protected final String username;
protected String accessToken;
protected String serverId;
protected final String password;
protected ClientPermissions permissions;
public SQLUser(UUID uuid, String username, String accessToken, String serverId, String password) {
this.uuid = uuid;
this.username = username;
this.accessToken = accessToken;
this.serverId = serverId;
this.password = password;
}
@Override
public String getUsername() {
return username;
}
@Override
public UUID getUUID() {
return uuid;
}
public String getServerId() {
return serverId;
}
public String getAccessToken() {
return accessToken;
}
@Override
public ClientPermissions getPermissions() {
return permissions;
}
@Override
public String toString() {
return "SQLUser{" +
"uuid=" + uuid +
", username='" + username + '\'' +
", permissions=" + permissions +
'}';
}
}
public static class SQLUserSession implements UserSession {
private final SQLUser user;
private final String id;
public SQLUserSession(SQLUser user) {
this.user = user;
this.id = user.username;
}
@Override
public String getID() {
return id;
}
@Override
public User getUser() {
return user;
}
@Override
public String getMinecraftAccessToken() {
return user.getAccessToken();
}
@Override
public long getExpireIn() {
return 0;
}
}
}

View file

@ -0,0 +1,414 @@
package pro.gravit.launchserver.auth.core;
import com.google.gson.reflect.TypeToken;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import pro.gravit.launcher.base.Launcher;
import pro.gravit.launcher.base.events.RequestEvent;
import pro.gravit.launcher.base.events.request.AuthRequestEvent;
import pro.gravit.launcher.base.events.request.GetAvailabilityAuthRequestEvent;
import pro.gravit.launcher.base.profiles.PlayerProfile;
import pro.gravit.launcher.base.request.auth.AuthRequest;
import pro.gravit.launcher.base.request.auth.details.AuthPasswordDetails;
import pro.gravit.launcher.base.request.auth.password.AuthPlainPassword;
import pro.gravit.launcher.base.request.secure.HardwareReportRequest;
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.Reconfigurable;
import pro.gravit.launchserver.auth.AuthException;
import pro.gravit.launchserver.auth.AuthProviderPair;
import pro.gravit.launchserver.auth.core.interfaces.UserHardware;
import pro.gravit.launchserver.auth.core.interfaces.provider.AuthSupportGetAllUsers;
import pro.gravit.launchserver.auth.core.interfaces.provider.AuthSupportHardware;
import pro.gravit.launchserver.auth.core.interfaces.provider.AuthSupportRegistration;
import pro.gravit.launchserver.auth.core.interfaces.provider.AuthSupportSudo;
import pro.gravit.launchserver.auth.core.openid.OpenIDAuthCoreProvider;
import pro.gravit.launchserver.manangers.AuthManager;
import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.response.auth.AuthResponse;
import pro.gravit.utils.ProviderMap;
import pro.gravit.utils.command.Command;
import pro.gravit.utils.command.SubCommand;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
/*
All-In-One provider
*/
public abstract class AuthCoreProvider implements AutoCloseable, Reconfigurable {
public static final ProviderMap<AuthCoreProvider> providers = new ProviderMap<>("AuthCoreProvider");
private static final Logger logger = LogManager.getLogger();
private static boolean registredProviders = false;
protected transient LaunchServer server;
protected transient AuthProviderPair pair;
public static void registerProviders() {
if (!registredProviders) {
providers.register("reject", RejectAuthCoreProvider.class);
providers.register("mysql", MySQLCoreProvider.class);
providers.register("postgresql", PostgresSQLCoreProvider.class);
providers.register("memory", MemoryAuthCoreProvider.class);
providers.register("merge", MergeAuthCoreProvider.class);
providers.register("openid", OpenIDAuthCoreProvider.class);
providers.register("sql", SQLCoreProvider.class);
registredProviders = true;
}
}
public abstract User getUserByUsername(String username);
public User getUserByLogin(String login) {
return getUserByUsername(login);
}
public abstract User getUserByUUID(UUID uuid);
public abstract UserSession getUserSessionByOAuthAccessToken(String accessToken) throws OAuthAccessTokenExpired;
public abstract AuthManager.AuthReport refreshAccessToken(String refreshToken, AuthResponse.AuthContext context /* may be null */);
public void verifyAuth(AuthResponse.AuthContext context) throws AuthException {
// None
}
public abstract AuthManager.AuthReport authorize(String login, AuthResponse.AuthContext context /* may be null */, AuthRequest.AuthPasswordInterface password /* may be null */, boolean minecraftAccess) throws IOException;
public AuthManager.AuthReport authorize(User user, AuthResponse.AuthContext context /* may be null */, AuthRequest.AuthPasswordInterface password /* may be null */, boolean minecraftAccess) throws IOException {
return authorize(user.getUsername(), context, password, minecraftAccess);
}
public void init(LaunchServer server, AuthProviderPair pair) {
this.server = server;
this.pair = pair;
}
public List<GetAvailabilityAuthRequestEvent.AuthAvailabilityDetails> getDetails(Client client) {
return List.of(new AuthPasswordDetails());
}
@Override
public Map<String, Command> getCommands() {
Map<String, Command> map = defaultCommandsMap();
map.put("auth", new SubCommand("[login] (json/plain password data)", "Test auth") {
@Override
public void invoke(String... args) throws Exception {
verifyArgs(args, 1);
AuthRequest.AuthPasswordInterface password = null;
if (args.length > 1) {
if (args[1].startsWith("{")) {
password = Launcher.gsonManager.gson.fromJson(args[1], AuthRequest.AuthPasswordInterface.class);
} else {
password = new AuthPlainPassword(args[1]);
}
}
var report = authorize(args[0], null, password, false);
if (report.isUsingOAuth()) {
logger.info("OAuth: AccessToken: {} RefreshToken: {} MinecraftAccessToken: {}", report.oauthAccessToken(), report.oauthRefreshToken(), report.minecraftAccessToken());
if (report.session() != null) {
logger.info("UserSession: id {} expire {} user {}", report.session().getID(), report.session().getExpireIn(), report.session().getUser() == null ? "null" : "found");
logger.info(report.session().toString());
}
} else {
logger.info("Basic: MinecraftAccessToken: {}", report.minecraftAccessToken());
}
}
});
map.put("getuserbyusername", new SubCommand("[username]", "get user by username") {
@Override
public void invoke(String... args) throws Exception {
verifyArgs(args, 1);
User user = getUserByUsername(args[0]);
if (user == null) {
logger.info("User {} not found", args[0]);
} else {
logger.info("User {}: {}", args[0], user.toString());
}
}
});
map.put("getuserbyuuid", new SubCommand("[uuid]", "get user by uuid") {
@Override
public void invoke(String... args) throws Exception {
verifyArgs(args, 1);
User user = getUserByUUID(UUID.fromString(args[0]));
if (user == null) {
logger.info("User {} not found", args[0]);
} else {
logger.info("User {}: {}", args[0], user.toString());
}
}
});
{
var instance = isSupport(AuthSupportGetAllUsers.class);
if (instance != null) {
map.put("getallusers", new SubCommand("(limit)", "print all users information") {
@Override
public void invoke(String... args) {
int max = Integer.MAX_VALUE;
if (args.length > 0) max = Integer.parseInt(args[0]);
Iterable<User> users = instance.getAllUsers();
int counter = 0;
for (User u : users) {
logger.info("User {}", u.toString());
counter++;
if (counter == max) break;
}
logger.info("Found {} users", counter);
}
});
}
}
{
var instance = isSupport(AuthSupportHardware.class);
if (instance != null) {
map.put("gethardwarebyid", new SubCommand("[id]", "get hardware by id") {
@Override
public void invoke(String... args) throws Exception {
verifyArgs(args, 1);
UserHardware hardware = instance.getHardwareInfoById(args[0]);
if (hardware == null) {
logger.info("UserHardware {} not found", args[0]);
} else {
logger.info("UserHardware: {}", hardware);
}
}
});
map.put("gethardwarebydata", new SubCommand("[json data]", "fulltext search hardware by json data(slow)") {
@Override
public void invoke(String... args) throws Exception {
verifyArgs(args, 1);
UserHardware hardware = instance.getHardwareInfoByData(Launcher.gsonManager.gson.fromJson(args[0], HardwareReportRequest.HardwareInfo.class));
if (hardware == null) {
logger.info("UserHardware {} not found", args[0]);
} else {
logger.info("UserHardware: {}", hardware);
}
}
});
map.put("findmulti", new SubCommand("[hardware id]", "get all users in one hardware id") {
@Override
public void invoke(String... args) throws Exception {
verifyArgs(args, 1);
UserHardware hardware = instance.getHardwareInfoById(args[0]);
if (hardware == null) {
logger.info("UserHardware {} not found", args[0]);
return;
}
Iterable<User> users = instance.getUsersByHardwareInfo(hardware);
for (User user : users) {
logger.info("User {}", user);
}
}
});
map.put("banhardware", new SubCommand("[hardware id]", "ban hardware by id") {
@Override
public void invoke(String... args) throws Exception {
verifyArgs(args, 1);
UserHardware hardware = instance.getHardwareInfoById(args[0]);
if (hardware == null) {
logger.info("UserHardware {} not found", args[0]);
return;
}
instance.banHardware(hardware);
logger.info("UserHardware {} banned", args[0]);
}
});
map.put("unbanhardware", new SubCommand("[hardware id]", "ban hardware by id") {
@Override
public void invoke(String... args) throws Exception {
verifyArgs(args, 1);
UserHardware hardware = instance.getHardwareInfoById(args[0]);
if (hardware == null) {
logger.info("UserHardware {} not found", args[0]);
return;
}
instance.unbanHardware(hardware);
logger.info("UserHardware {} unbanned", args[0]);
}
});
map.put("comparehardware", new SubCommand("[json data 1] [json data 2]", "compare hardware info") {
@Override
public void invoke(String... args) throws Exception {
verifyArgs(args, 2);
HardwareReportRequest.HardwareInfo hardware1 = Launcher.gsonManager.gson.fromJson(args[0], HardwareReportRequest.HardwareInfo.class);
HardwareReportRequest.HardwareInfo hardware2 = Launcher.gsonManager.gson.fromJson(args[1], HardwareReportRequest.HardwareInfo.class);
AuthSupportHardware.HardwareInfoCompareResult result = instance.compareHardwareInfo(hardware1, hardware2);
if (result == null) {
logger.error("Method compareHardwareInfo return null");
return;
}
logger.info("Compare result: {} Spoof: {} first {} second", result.compareLevel, result.firstSpoofingLevel, result.secondSpoofingLevel);
}
});
}
}
{
var instance = isSupport(AuthSupportRegistration.class);
if (instance != null) {
map.put("register", new SubCommand("[username] [email] [plain or json password] (json args)", "Register new user") {
@Override
public void invoke(String... args) throws Exception {
verifyArgs(args, 2);
Map<String, String> map = null;
String username = args[0];
String email = args[1];
String plainPassword = args[2];
if (args.length > 3) {
Type typeOfMap = new TypeToken<Map<String, String>>() {
}.getType();
map = Launcher.gsonManager.gson.fromJson(args[2], typeOfMap);
}
AuthRequest.AuthPasswordInterface password;
if (plainPassword.startsWith("{")) {
password = Launcher.gsonManager.gson.fromJson(plainPassword, AuthRequest.AuthPasswordInterface.class);
} else {
password = new AuthPlainPassword(plainPassword);
}
User user = instance.registration(username, email, password, map);
logger.info("User '{}' registered", user.toString());
}
});
}
}
{
var instance = isSupport(AuthSupportSudo.class);
if(instance != null) {
map.put("sudo", new SubCommand("[connectUUID] [username/uuid] [isShadow] (CLIENT/API)", "Authorize connectUUID as another user without password") {
@Override
public void invoke(String... args) throws Exception {
verifyArgs(args, 3);
UUID connectUUID = UUID.fromString(args[0]);
String login = args[1];
boolean isShadow = Boolean.parseBoolean(args[2]);
AuthResponse.ConnectTypes type;
if(args.length > 3) {
type = AuthResponse.ConnectTypes.valueOf(args[3]);
} else {
type = AuthResponse.ConnectTypes.CLIENT;
}
User user;
if(login.length() == 36) {
UUID uuid = UUID.fromString(login);
user = getUserByUUID(uuid);
} else {
user = getUserByUsername(login);
}
if(user == null) {
logger.error("User {} not found", login);
return;
}
AtomicBoolean founded = new AtomicBoolean();
server.nettyServerSocketHandler.nettyServer.service.forEachActiveChannels((ch, fh) -> {
var client = fh.getClient();
if(client == null || !connectUUID.equals(fh.getConnectUUID())) {
return;
}
logger.info("Found connectUUID {} with IP {}", fh.getConnectUUID(), fh.context == null ? "null" : fh.context.ip);
var lock = server.config.netty.performance.disableThreadSafeClientObject ? null : client.writeLock();
if(lock != null) {
lock.lock();
}
try {
var report = instance.sudo(user, isShadow);
User user1 = report.session().getUser();
server.authManager.internalAuth(client, type, pair, user1.getUsername(), user1.getUUID(), user1.getPermissions(), true);
client.sessionObject = report.session();
client.coreObject = report.session().getUser();
PlayerProfile playerProfile = server.authManager.getPlayerProfile(client);
AuthRequestEvent request = new AuthRequestEvent(user1.getPermissions(), playerProfile,
report.minecraftAccessToken(), null, null,
new AuthRequestEvent.OAuthRequestEvent(report.oauthAccessToken(), report.oauthRefreshToken(), report.oauthExpire()));
request.requestUUID = RequestEvent.eventUUID;
server.nettyServerSocketHandler.nettyServer.service.sendObject(ch, request);
} catch (Throwable e) {
logger.error("Sudo error", e);
} finally {
if(lock != null) {
lock.unlock();
}
founded.set(true);
}
});
if(!founded.get()) {
logger.error("ConnectUUID {} not found", connectUUID);
}
}
});
}
}
return map;
}
public abstract User checkServer(Client client, String username, String serverID) throws IOException;
public abstract boolean joinServer(Client client, String username, UUID uuid, String accessToken, String serverID) throws IOException;
@SuppressWarnings("unchecked")
public <T> T isSupport(Class<T> clazz) {
if (clazz.isAssignableFrom(getClass())) return (T) this;
return null;
}
@Override
public abstract void close();
public static class PasswordVerifyReport {
public static final PasswordVerifyReport REQUIRED_2FA = new PasswordVerifyReport(-1);
public static final PasswordVerifyReport FAILED = new PasswordVerifyReport(false);
public static final PasswordVerifyReport OK = new PasswordVerifyReport(true);
public final boolean success;
public final boolean needMoreFactors;
public final List<Integer> factors;
public PasswordVerifyReport(boolean success) {
this.success = success;
this.needMoreFactors = false;
this.factors = List.of();
}
public PasswordVerifyReport(AuthManager.AuthReport report) {
this.success = true;
this.needMoreFactors = false;
this.factors = List.of();
}
public PasswordVerifyReport(int nextFactor) {
this.success = false;
this.needMoreFactors = true;
this.factors = List.of(nextFactor);
}
public PasswordVerifyReport(List<Integer> factors) {
this.success = false;
this.needMoreFactors = false;
this.factors = Collections.unmodifiableList(factors);
}
private PasswordVerifyReport(boolean success, boolean needMoreFactors, List<Integer> factors) {
this.success = success;
this.needMoreFactors = needMoreFactors;
this.factors = factors;
}
public boolean isSuccess() {
return success;
}
}
public static class OAuthAccessTokenExpired extends Exception {
public OAuthAccessTokenExpired() {
}
public OAuthAccessTokenExpired(String message) {
super(message);
}
public OAuthAccessTokenExpired(String message, Throwable cause) {
super(message, cause);
}
}
}

View file

@ -0,0 +1,204 @@
package pro.gravit.launchserver.auth.core;
import pro.gravit.launcher.base.ClientPermissions;
import pro.gravit.launcher.base.events.request.GetAvailabilityAuthRequestEvent;
import pro.gravit.launcher.base.request.auth.AuthRequest;
import pro.gravit.launcher.base.request.auth.details.AuthLoginOnlyDetails;
import pro.gravit.launchserver.auth.AuthException;
import pro.gravit.launchserver.auth.core.interfaces.provider.AuthSupportSudo;
import pro.gravit.launchserver.manangers.AuthManager;
import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.response.auth.AuthResponse;
import pro.gravit.utils.helper.SecurityHelper;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
public class MemoryAuthCoreProvider extends AuthCoreProvider implements AuthSupportSudo {
private transient final List<MemoryUser> memory = new ArrayList<>(16);
@Override
public User getUserByUsername(String username) {
synchronized (memory) {
for (MemoryUser u : memory) {
if (u.username.equals(username)) {
return u;
}
}
var result = new MemoryUser(username);
memory.add(result);
return result;
}
}
@Override
public List<GetAvailabilityAuthRequestEvent.AuthAvailabilityDetails> getDetails(Client client) {
return List.of(new AuthLoginOnlyDetails());
}
@Override
public User getUserByUUID(UUID uuid) {
synchronized (memory) {
for (MemoryUser u : memory) {
if (u.uuid.equals(uuid)) {
return u;
}
}
}
return null;
}
@Override
public UserSession getUserSessionByOAuthAccessToken(String accessToken) {
synchronized (memory) {
for (MemoryUser u : memory) {
if (u.accessToken.equals(accessToken)) {
return new MemoryUserSession(u);
}
}
}
return null;
}
@Override
public AuthManager.AuthReport refreshAccessToken(String refreshToken, AuthResponse.AuthContext context) {
return null;
}
@Override
public AuthManager.AuthReport authorize(String login, AuthResponse.AuthContext context, AuthRequest.AuthPasswordInterface password, boolean minecraftAccess) throws IOException {
if (login == null) {
throw AuthException.userNotFound();
}
MemoryUser user = null;
synchronized (memory) {
for (MemoryUser u : memory) {
if (u.username.equals(login)) {
user = u;
break;
}
}
if (user == null) {
user = new MemoryUser(login);
memory.add(user);
}
}
if (!minecraftAccess) {
return AuthManager.AuthReport.ofOAuth(user.accessToken, null, 0, new MemoryUserSession(user));
} else {
return AuthManager.AuthReport.ofOAuthWithMinecraft(user.accessToken, user.accessToken, null, 0, new MemoryUserSession(user));
}
}
@Override
public User checkServer(Client client, String username, String serverID) {
synchronized (memory) {
for (MemoryUser u : memory) {
if (u.username.equals(username)) {
return u;
}
}
var result = new MemoryUser(username);
memory.add(result);
return result;
}
}
@Override
public boolean joinServer(Client client, String username, UUID uuid, String accessToken, String serverID) {
return true;
}
@Override
public void close() {
}
@Override
public AuthManager.AuthReport sudo(User user, boolean shadow) throws IOException {
return authorize(user.getUsername(), null, null, true);
}
public static class MemoryUser implements User {
private final String username;
private final UUID uuid;
private String serverId;
private final String accessToken;
private final ClientPermissions permissions;
public MemoryUser(String username) {
this.username = username;
this.uuid = makeUuidFromUsername(username);
this.accessToken = SecurityHelper.randomStringToken();
this.permissions = new ClientPermissions();
}
private static UUID makeUuidFromUsername(String username) {
return UUID.nameUUIDFromBytes(username.getBytes(StandardCharsets.UTF_8));
}
@Override
public String getUsername() {
return username;
}
@Override
public UUID getUUID() {
return uuid;
}
@Override
public ClientPermissions getPermissions() {
return permissions;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MemoryUser that = (MemoryUser) o;
return uuid.equals(that.uuid);
}
@Override
public int hashCode() {
return Objects.hash(uuid);
}
}
public static class MemoryUserSession implements UserSession {
private final String id;
private final MemoryUser user;
private final long expireIn;
public MemoryUserSession(MemoryUser user) {
this.id = SecurityHelper.randomStringToken();
this.user = user;
this.expireIn = 0;
}
@Override
public String getID() {
return id;
}
@Override
public User getUser() {
return user;
}
@Override
public String getMinecraftAccessToken() {
return "IGNORED";
}
@Override
public long getExpireIn() {
return expireIn;
}
}
}

View file

@ -0,0 +1,91 @@
package pro.gravit.launchserver.auth.core;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import pro.gravit.launcher.base.request.auth.AuthRequest;
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.auth.AuthException;
import pro.gravit.launchserver.auth.AuthProviderPair;
import pro.gravit.launchserver.manangers.AuthManager;
import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.response.auth.AuthResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class MergeAuthCoreProvider extends AuthCoreProvider {
private transient final Logger logger = LogManager.getLogger(MergeAuthCoreProvider.class);
public List<String> list = new ArrayList<>();
private final transient List<AuthCoreProvider> providers = new ArrayList<>();
@Override
public User getUserByUsername(String username) {
for(var core : providers) {
var result = core.getUserByUsername(username);
if(result != null) {
return result;
}
}
return null;
}
@Override
public User getUserByUUID(UUID uuid) {
for(var core : providers) {
var result = core.getUserByUUID(uuid);
if(result != null) {
return result;
}
}
return null;
}
@Override
public UserSession getUserSessionByOAuthAccessToken(String accessToken) throws OAuthAccessTokenExpired {
throw new OAuthAccessTokenExpired(); // Authorization not supported
}
@Override
public AuthManager.AuthReport refreshAccessToken(String refreshToken, AuthResponse.AuthContext context) {
return null;
}
@Override
public AuthManager.AuthReport authorize(String login, AuthResponse.AuthContext context, AuthRequest.AuthPasswordInterface password, boolean minecraftAccess) throws IOException {
throw new AuthException("Authorization not supported");
}
@Override
public User checkServer(Client client, String username, String serverID) throws IOException {
for(var core : providers) {
var result = core.checkServer(client, username, serverID);
if(result != null) {
return result;
}
}
return null;
}
@Override
public boolean joinServer(Client client, String username, UUID uuid, String accessToken, String serverID) {
return false; // Authorization not supported
}
@Override
public void init(LaunchServer server, AuthProviderPair pair1) {
for(var e : list) {
var pair = server.config.auth.get(e);
if(pair != null) {
providers.add(pair.core);
} else {
logger.warn("Provider {} not found", e);
}
}
}
@Override
public void close() {
// Providers closed automatically
}
}

View file

@ -0,0 +1,354 @@
package pro.gravit.launchserver.auth.core;
import pro.gravit.launcher.base.request.secure.HardwareReportRequest;
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.auth.AuthProviderPair;
import pro.gravit.launchserver.auth.MySQLSourceConfig;
import pro.gravit.launchserver.auth.SQLSourceConfig;
import pro.gravit.launchserver.auth.core.interfaces.UserHardware;
import pro.gravit.launchserver.auth.core.interfaces.provider.AuthSupportHardware;
import pro.gravit.launchserver.auth.core.interfaces.session.UserSessionSupportHardware;
import pro.gravit.utils.helper.IOHelper;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.sql.*;
import java.util.Base64;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
public class MySQLCoreProvider extends AbstractSQLCoreProvider implements AuthSupportHardware {
public MySQLSourceConfig mySQLHolder;
public String hardwareIdColumn;
public String tableHWID = "hwids";
public String tableHWIDLog = "hwidLog";
public double criticalCompareLevel = 1.0;
private transient String sqlFindHardwareByPublicKey;
private transient String sqlFindHardwareByData;
private transient String sqlFindHardwareById;
private transient String sqlCreateHardware;
private transient String sqlCreateHWIDLog;
private transient String sqlUpdateHardwarePublicKey;
private transient String sqlUpdateHardwareBanned;
private transient String sqlUpdateUsers;
private transient String sqlUsersByHwidId;
@Override
public SQLSourceConfig getSQLConfig() {
return mySQLHolder;
}
@Override
public void init(LaunchServer server, AuthProviderPair pair) {
super.init(server, pair);
logger.warn("Method 'mysql' deprecated and may be removed in future release. Please use new 'sql' method: https://gravitlauncher.com/auth");
String userInfoCols = makeUserCols();
String hardwareInfoCols = "id, hwDiskId, baseboardSerialNumber, displayId, bitness, totalMemory, logicalProcessors, physicalProcessors, processorMaxFreq, battery, id, graphicCard, banned, publicKey";
if (sqlFindHardwareByPublicKey == null)
sqlFindHardwareByPublicKey = "SELECT %s FROM %s WHERE `publicKey` = ?".formatted(hardwareInfoCols, tableHWID);
if (sqlFindHardwareById == null)
sqlFindHardwareById = "SELECT %s FROM %s WHERE `id` = ?".formatted(hardwareInfoCols, tableHWID);
if (sqlUsersByHwidId == null)
sqlUsersByHwidId = "SELECT %s FROM %s WHERE `%s` = ?".formatted(userInfoCols, table, hardwareIdColumn);
if (sqlFindHardwareByData == null)
sqlFindHardwareByData = "SELECT %s FROM %s".formatted(hardwareInfoCols, tableHWID);
if (sqlCreateHardware == null)
sqlCreateHardware = "INSERT INTO `%s` (`publickey`, `hwDiskId`, `baseboardSerialNumber`, `displayId`, `bitness`, `totalMemory`, `logicalProcessors`, `physicalProcessors`, `processorMaxFreq`, `graphicCard`, `battery`, `banned`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, '0')".formatted(tableHWID);
if (sqlCreateHWIDLog == null)
sqlCreateHWIDLog = "INSERT INTO %s (`hwidId`, `newPublicKey`) VALUES (?, ?)".formatted(tableHWIDLog);
if (sqlUpdateHardwarePublicKey == null)
sqlUpdateHardwarePublicKey = "UPDATE %s SET `publicKey` = ? WHERE `id` = ?".formatted(tableHWID);
sqlUpdateHardwareBanned = "UPDATE %s SET `banned` = ? WHERE `id` = ?".formatted(tableHWID);
sqlUpdateUsers = "UPDATE %s SET `%s` = ? WHERE `%s` = ?".formatted(table, hardwareIdColumn, uuidColumn);
}
@Override
protected String makeUserCols() {
return super.makeUserCols().concat(", ").concat(hardwareIdColumn);
}
@Override
protected MySQLUser constructUser(ResultSet set) throws SQLException {
return set.next() ? new MySQLUser(UUID.fromString(set.getString(uuidColumn)), set.getString(usernameColumn),
set.getString(accessTokenColumn), set.getString(serverIDColumn), set.getString(passwordColumn), set.getLong(hardwareIdColumn)) : null;
}
private MySQLUserHardware fetchHardwareInfo(ResultSet set) throws SQLException, IOException {
HardwareReportRequest.HardwareInfo hardwareInfo = new HardwareReportRequest.HardwareInfo();
hardwareInfo.hwDiskId = set.getString("hwDiskId");
hardwareInfo.baseboardSerialNumber = set.getString("baseboardSerialNumber");
Blob displayId = set.getBlob("displayId");
hardwareInfo.displayId = displayId == null ? null : IOHelper.read(displayId.getBinaryStream());
hardwareInfo.bitness = set.getInt("bitness");
hardwareInfo.totalMemory = set.getLong("totalMemory");
hardwareInfo.logicalProcessors = set.getInt("logicalProcessors");
hardwareInfo.physicalProcessors = set.getInt("physicalProcessors");
hardwareInfo.processorMaxFreq = set.getLong("processorMaxFreq");
hardwareInfo.battery = set.getBoolean("battery");
hardwareInfo.graphicCard = set.getString("graphicCard");
Blob publicKey = set.getBlob("publicKey");
long id = set.getLong("id");
boolean banned = set.getBoolean("banned");
return new MySQLUserHardware(hardwareInfo, publicKey == null ? null : IOHelper.read(publicKey.getBinaryStream()), id, banned);
}
private void setUserHardwareId(Connection connection, UUID uuid, long hwidId) throws SQLException {
PreparedStatement s = connection.prepareStatement(sqlUpdateUsers);
s.setLong(1, hwidId);
s.setString(2, uuid.toString());
s.executeUpdate();
}
@Override
public UserHardware getHardwareInfoByPublicKey(byte[] publicKey) {
try (Connection connection = mySQLHolder.getConnection()) {
PreparedStatement s = connection.prepareStatement(sqlFindHardwareByPublicKey);
s.setBlob(1, new ByteArrayInputStream(publicKey));
try (ResultSet set = s.executeQuery()) {
if (set.next()) {
return fetchHardwareInfo(set);
} else {
return null;
}
}
} catch (SQLException | IOException throwables) {
logger.error("SQL Error", throwables);
return null;
}
}
@Override
public UserHardware getHardwareInfoByData(HardwareReportRequest.HardwareInfo info) {
try (Connection connection = mySQLHolder.getConnection()) {
PreparedStatement s = connection.prepareStatement(sqlFindHardwareByData);
try (ResultSet set = s.executeQuery()) {
while (set.next()) {
MySQLUserHardware hw = fetchHardwareInfo(set);
HardwareInfoCompareResult result = compareHardwareInfo(hw.getHardwareInfo(), info);
if (result.compareLevel > criticalCompareLevel) {
return hw;
}
}
}
} catch (SQLException | IOException throwables) {
logger.error("SQL Error", throwables);
}
return null;
}
@Override
public UserHardware getHardwareInfoById(String id) {
try (Connection connection = mySQLHolder.getConnection()) {
PreparedStatement s = connection.prepareStatement(sqlFindHardwareById);
s.setLong(1, Long.parseLong(id));
try (ResultSet set = s.executeQuery()) {
if (set.next()) {
return fetchHardwareInfo(set);
} else {
return null;
}
}
} catch (SQLException | IOException throwables) {
logger.error("SQL Error", throwables);
return null;
}
}
@Override
public UserHardware createHardwareInfo(HardwareReportRequest.HardwareInfo hardwareInfo, byte[] publicKey) {
try (Connection connection = mySQLHolder.getConnection()) {
PreparedStatement s = connection.prepareStatement(sqlCreateHardware, Statement.RETURN_GENERATED_KEYS);
s.setBlob(1, new ByteArrayInputStream(publicKey));
s.setString(2, hardwareInfo.hwDiskId);
s.setString(3, hardwareInfo.baseboardSerialNumber);
s.setBlob(4, hardwareInfo.displayId == null ? null : new ByteArrayInputStream(hardwareInfo.displayId));
s.setInt(5, hardwareInfo.bitness);
s.setLong(6, hardwareInfo.totalMemory);
s.setInt(7, hardwareInfo.logicalProcessors);
s.setInt(8, hardwareInfo.physicalProcessors);
s.setLong(9, hardwareInfo.processorMaxFreq);
s.setString(10, hardwareInfo.graphicCard);
s.setBoolean(11, hardwareInfo.battery);
s.executeUpdate();
try (ResultSet generatedKeys = s.getGeneratedKeys()) {
if (generatedKeys.next()) {
//writeHwidLog(connection, generatedKeys.getLong(1), publicKey);
long id = generatedKeys.getLong(1);
return new MySQLUserHardware(hardwareInfo, publicKey, id, false);
}
}
return null;
} catch (SQLException throwables) {
logger.error("SQL Error", throwables);
return null;
}
}
@Override
public void connectUserAndHardware(UserSession userSession, UserHardware hardware) {
SQLUserSession mySQLUserSession = (SQLUserSession) userSession;
MySQLUser mySQLUser = (MySQLUser) mySQLUserSession.getUser();
MySQLUserHardware mySQLUserHardware = (MySQLUserHardware) hardware;
if (mySQLUser.hwidId == mySQLUserHardware.id) return;
mySQLUser.hwidId = mySQLUserHardware.id;
try (Connection connection = mySQLHolder.getConnection()) {
setUserHardwareId(connection, mySQLUser.getUUID(), mySQLUserHardware.id);
} catch (SQLException throwables) {
logger.error("SQL Error", throwables);
}
}
@Override
public void addPublicKeyToHardwareInfo(UserHardware hardware, byte[] publicKey) {
MySQLUserHardware mySQLUserHardware = (MySQLUserHardware) hardware;
mySQLUserHardware.publicKey = publicKey;
try (Connection connection = mySQLHolder.getConnection()) {
PreparedStatement s = connection.prepareStatement(sqlUpdateHardwarePublicKey);
s.setBlob(1, new ByteArrayInputStream(publicKey));
s.setLong(2, mySQLUserHardware.id);
s.executeUpdate();
} catch (SQLException e) {
logger.error("SQL error", e);
}
}
@Override
public Iterable<User> getUsersByHardwareInfo(UserHardware hardware) {
List<User> users = new LinkedList<>();
try (Connection c = mySQLHolder.getConnection()) {
PreparedStatement s = c.prepareStatement(sqlUsersByHwidId);
s.setLong(1, Long.parseLong(hardware.getId()));
s.setQueryTimeout(MySQLSourceConfig.TIMEOUT);
try (ResultSet set = s.executeQuery()) {
while (!set.isLast()) {
users.add(constructUser(set));
}
}
} catch (SQLException e) {
logger.error("SQL error", e);
return null;
}
return users;
}
@Override
public void banHardware(UserHardware hardware) {
MySQLUserHardware mySQLUserHardware = (MySQLUserHardware) hardware;
mySQLUserHardware.banned = true;
try (Connection connection = mySQLHolder.getConnection()) {
PreparedStatement s = connection.prepareStatement(sqlUpdateHardwareBanned);
s.setBoolean(1, true);
s.setLong(2, mySQLUserHardware.id);
s.executeUpdate();
} catch (SQLException e) {
logger.error("SQL Error", e);
}
}
@Override
public void unbanHardware(UserHardware hardware) {
MySQLUserHardware mySQLUserHardware = (MySQLUserHardware) hardware;
mySQLUserHardware.banned = false;
try (Connection connection = mySQLHolder.getConnection()) {
PreparedStatement s = connection.prepareStatement(sqlUpdateHardwareBanned);
s.setBoolean(1, false);
s.setLong(2, mySQLUserHardware.id);
s.executeUpdate();
} catch (SQLException e) {
logger.error("SQL error", e);
}
}
@Override
protected SQLUserSession createSession(SQLUser user) {
return new MySQLUserSession(user);
}
public class MySQLUserSession extends SQLUserSession implements UserSessionSupportHardware {
private transient MySQLUser mySQLUser;
protected transient MySQLUserHardware hardware;
public MySQLUserSession(SQLUser user) {
super(user);
mySQLUser = (MySQLUser) user;
}
@Override
public String getHardwareId() {
return mySQLUser.hwidId == 0 ? null : String.valueOf(mySQLUser.hwidId);
}
@Override
public UserHardware getHardware() {
if(hardware == null) {
hardware = (MySQLUserHardware) getHardwareInfoById(String.valueOf(mySQLUser.hwidId));
}
return hardware;
}
}
public static class MySQLUserHardware implements UserHardware {
private final HardwareReportRequest.HardwareInfo hardwareInfo;
private final long id;
private byte[] publicKey;
private boolean banned;
public MySQLUserHardware(HardwareReportRequest.HardwareInfo hardwareInfo, byte[] publicKey, long id, boolean banned) {
this.hardwareInfo = hardwareInfo;
this.publicKey = publicKey;
this.id = id;
this.banned = banned;
}
@Override
public HardwareReportRequest.HardwareInfo getHardwareInfo() {
return hardwareInfo;
}
@Override
public byte[] getPublicKey() {
return publicKey;
}
@Override
public String getId() {
return String.valueOf(id);
}
@Override
public boolean isBanned() {
return banned;
}
@Override
public String toString() {
return "MySQLUserHardware{" +
"hardwareInfo=" + hardwareInfo +
", publicKey=" + (publicKey == null ? null : new String(Base64.getEncoder().encode(publicKey))) +
", id=" + id +
", banned=" + banned +
'}';
}
}
public static class MySQLUser extends SQLUser {
protected long hwidId;
public MySQLUser(UUID uuid, String username, String accessToken, String serverId, String password, long hwidId) {
super(uuid, username, accessToken, serverId, password);
this.hwidId = hwidId;
}
@Override
public String toString() {
return "MySQLUser{" +
"uuid=" + uuid +
", username='" + username + '\'' +
", permissions=" + permissions +
", hwidId=" + hwidId +
'}';
}
}
}

View file

@ -0,0 +1,21 @@
package pro.gravit.launchserver.auth.core;
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.auth.AuthProviderPair;
import pro.gravit.launchserver.auth.PostgreSQLSourceConfig;
import pro.gravit.launchserver.auth.SQLSourceConfig;
public class PostgresSQLCoreProvider extends AbstractSQLCoreProvider {
public PostgreSQLSourceConfig postgresSQLHolder;
@Override
public SQLSourceConfig getSQLConfig() {
return postgresSQLHolder;
}
@Override
public void init(LaunchServer server, AuthProviderPair pair) {
super.init(server, pair);
logger.warn("Method 'postgresql' deprecated and may be removed in future release. Please use new 'sql' method: https://gravitlauncher.com/auth");
}
}

View file

@ -0,0 +1,57 @@
package pro.gravit.launchserver.auth.core;
import pro.gravit.launcher.base.request.auth.AuthRequest;
import pro.gravit.launchserver.auth.AuthException;
import pro.gravit.launchserver.manangers.AuthManager;
import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.response.auth.AuthResponse;
import java.io.IOException;
import java.util.UUID;
public class RejectAuthCoreProvider extends AuthCoreProvider {
@Override
public User getUserByUsername(String username) {
return null;
}
@Override
public User getUserByUUID(UUID uuid) {
return null;
}
@Override
public UserSession getUserSessionByOAuthAccessToken(String accessToken) {
return null;
}
@Override
public AuthManager.AuthReport refreshAccessToken(String refreshToken, AuthResponse.AuthContext context) {
return null;
}
@Override
public void verifyAuth(AuthResponse.AuthContext context) throws AuthException {
throw new AuthException("Please configure AuthCoreProvider");
}
@Override
public AuthManager.AuthReport authorize(String login, AuthResponse.AuthContext context, AuthRequest.AuthPasswordInterface password, boolean minecraftAccess) throws IOException {
throw new AuthException("Please configure AuthCoreProvider");
}
@Override
public User checkServer(Client client, String username, String serverID) {
return null;
}
@Override
public boolean joinServer(Client client, String username, UUID uuid, String accessToken, String serverID) {
return false;
}
@Override
public void close() {
}
}

View file

@ -0,0 +1,391 @@
package pro.gravit.launchserver.auth.core;
import pro.gravit.launcher.base.request.secure.HardwareReportRequest;
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.auth.AuthProviderPair;
import pro.gravit.launchserver.auth.HikariSQLSourceConfig;
import pro.gravit.launchserver.auth.MySQLSourceConfig;
import pro.gravit.launchserver.auth.SQLSourceConfig;
import pro.gravit.launchserver.auth.core.interfaces.UserHardware;
import pro.gravit.launchserver.auth.core.interfaces.provider.AuthSupportExtendedCheckServer;
import pro.gravit.launchserver.auth.core.interfaces.provider.AuthSupportHardware;
import pro.gravit.launchserver.auth.core.interfaces.session.UserSessionSupportHardware;
import pro.gravit.launchserver.socket.Client;
import java.io.IOException;
import java.sql.*;
import java.util.Base64;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
public class SQLCoreProvider extends AbstractSQLCoreProvider implements AuthSupportHardware, AuthSupportExtendedCheckServer {
public HikariSQLSourceConfig holder;
@Override
public void close() {
super.close();
holder.close();
}
@Override
public SQLSourceConfig getSQLConfig() {
return holder;
}
public String hardwareIdColumn;
public String tableHWID = "hwids";
public String tableHWIDLog = "hwidLog";
public double criticalCompareLevel = 1.0;
private transient String sqlFindHardwareByPublicKey;
private transient String sqlFindHardwareByData;
private transient String sqlFindHardwareById;
private transient String sqlCreateHardware;
private transient String sqlCreateHWIDLog;
private transient String sqlUpdateHardwarePublicKey;
private transient String sqlUpdateHardwareBanned;
private transient String sqlUpdateUsers;
private transient String sqlUsersByHwidId;
@Override
public void init(LaunchServer server, AuthProviderPair pair) {
holder.init();
super.init(server, pair);
String userInfoCols = makeUserCols();
String hardwareInfoCols = "id, hwDiskId, baseboardSerialNumber, displayId, bitness, totalMemory, logicalProcessors, physicalProcessors, processorMaxFreq, battery, id, graphicCard, banned, publicKey";
if (sqlFindHardwareByPublicKey == null)
sqlFindHardwareByPublicKey = "SELECT %s FROM %s WHERE publicKey = ?".formatted(hardwareInfoCols, tableHWID);
if (sqlFindHardwareById == null)
sqlFindHardwareById = "SELECT %s FROM %s WHERE id = ?".formatted(hardwareInfoCols, tableHWID);
if (sqlUsersByHwidId == null)
sqlUsersByHwidId = "SELECT %s FROM %s WHERE %s = ?".formatted(userInfoCols, table, hardwareIdColumn);
if (sqlFindHardwareByData == null)
sqlFindHardwareByData = "SELECT %s FROM %s".formatted(hardwareInfoCols, tableHWID);
if (sqlCreateHardware == null)
sqlCreateHardware = "INSERT INTO %s (publickey, hwDiskId, baseboardSerialNumber, displayId, bitness, totalMemory, logicalProcessors, physicalProcessors, processorMaxFreq, graphicCard, battery, banned) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, '0')".formatted(tableHWID);
if (sqlCreateHWIDLog == null)
sqlCreateHWIDLog = "INSERT INTO %s (hwidId, newPublicKey) VALUES (?, ?)".formatted(tableHWIDLog);
if (sqlUpdateHardwarePublicKey == null)
sqlUpdateHardwarePublicKey = "UPDATE %s SET publicKey = ? WHERE id = ?".formatted(tableHWID);
sqlUpdateHardwareBanned = "UPDATE %s SET banned = ? WHERE id = ?".formatted(tableHWID);
sqlUpdateUsers = "UPDATE %s SET %s = ? WHERE %s = ?".formatted(table, hardwareIdColumn, uuidColumn);
}
@Override
protected String makeUserCols() {
return super.makeUserCols().concat(", ").concat(hardwareIdColumn);
}
@Override
protected SQLUser constructUser(ResultSet set) throws SQLException {
return set.next() ? new SQLUser(UUID.fromString(set.getString(uuidColumn)), set.getString(usernameColumn),
set.getString(accessTokenColumn), set.getString(serverIDColumn), set.getString(passwordColumn), set.getLong(hardwareIdColumn)) : null;
}
private SQLUserHardware fetchHardwareInfo(ResultSet set) throws SQLException {
HardwareReportRequest.HardwareInfo hardwareInfo = new HardwareReportRequest.HardwareInfo();
hardwareInfo.hwDiskId = set.getString("hwDiskId");
hardwareInfo.baseboardSerialNumber = set.getString("baseboardSerialNumber");
byte[] displayId = set.getBytes("displayId");
hardwareInfo.displayId = displayId == null ? null : displayId;
hardwareInfo.bitness = set.getInt("bitness");
hardwareInfo.totalMemory = set.getLong("totalMemory");
hardwareInfo.logicalProcessors = set.getInt("logicalProcessors");
hardwareInfo.physicalProcessors = set.getInt("physicalProcessors");
hardwareInfo.processorMaxFreq = set.getLong("processorMaxFreq");
hardwareInfo.battery = set.getBoolean("battery");
hardwareInfo.graphicCard = set.getString("graphicCard");
byte[] publicKey = set.getBytes("publicKey");
long id = set.getLong("id");
boolean banned = set.getBoolean("banned");
return new SQLUserHardware(hardwareInfo, publicKey, id, banned);
}
private void setUserHardwareId(Connection connection, UUID uuid, long hwidId) throws SQLException {
PreparedStatement s = connection.prepareStatement(sqlUpdateUsers);
s.setLong(1, hwidId);
s.setString(2, uuid.toString());
s.executeUpdate();
}
@Override
public UserHardware getHardwareInfoByPublicKey(byte[] publicKey) {
try (Connection connection = holder.getConnection()) {
connection.setAutoCommit(false);
PreparedStatement s = connection.prepareStatement(sqlFindHardwareByPublicKey);
s.setBytes(1, publicKey);
try (ResultSet set = s.executeQuery()) {
if (set.next()) {
connection.commit();
return fetchHardwareInfo(set);
} else {
connection.commit();
return null;
}
}
} catch (SQLException throwables) {
logger.error("SQL Error", throwables);
return null;
}
}
@Override
public UserHardware getHardwareInfoByData(HardwareReportRequest.HardwareInfo info) {
try (Connection connection = holder.getConnection()) {
connection.setAutoCommit(false);
PreparedStatement s = connection.prepareStatement(sqlFindHardwareByData);
try (ResultSet set = s.executeQuery()) {
while (set.next()) {
SQLUserHardware hw = fetchHardwareInfo(set);
AuthSupportHardware.HardwareInfoCompareResult result = compareHardwareInfo(hw.getHardwareInfo(), info);
if (result.compareLevel > criticalCompareLevel) {
connection.commit();
return hw;
} else {
connection.commit();
}
}
}
} catch (SQLException throwables) {
logger.error("SQL Error", throwables);
}
return null;
}
@Override
public UserHardware getHardwareInfoById(String id) {
try (Connection connection = holder.getConnection()) {
connection.setAutoCommit(false);
PreparedStatement s = connection.prepareStatement(sqlFindHardwareById);
s.setLong(1, Long.parseLong(id));
try (ResultSet set = s.executeQuery()) {
if (set.next()) {
connection.commit();
return fetchHardwareInfo(set);
} else {
connection.commit();
return null;
}
}
} catch (SQLException throwables) {
logger.error("SQL Error", throwables);
return null;
}
}
@Override
public UserHardware createHardwareInfo(HardwareReportRequest.HardwareInfo hardwareInfo, byte[] publicKey) {
try (Connection connection = holder.getConnection()) {
connection.setAutoCommit(false);
PreparedStatement s = connection.prepareStatement(sqlCreateHardware, Statement.RETURN_GENERATED_KEYS);
s.setBytes(1, publicKey);
s.setString(2, hardwareInfo.hwDiskId);
s.setString(3, hardwareInfo.baseboardSerialNumber);
s.setBytes(4, hardwareInfo.displayId == null ? null : hardwareInfo.displayId);
s.setInt(5, hardwareInfo.bitness);
s.setLong(6, hardwareInfo.totalMemory);
s.setInt(7, hardwareInfo.logicalProcessors);
s.setInt(8, hardwareInfo.physicalProcessors);
s.setLong(9, hardwareInfo.processorMaxFreq);
s.setString(10, hardwareInfo.graphicCard);
s.setBoolean(11, hardwareInfo.battery);
s.executeUpdate();
try (ResultSet generatedKeys = s.getGeneratedKeys()) {
if (generatedKeys.next()) {
//writeHwidLog(connection, generatedKeys.getLong(1), publicKey);
long id = generatedKeys.getLong(1);
connection.commit();
return new SQLUserHardware(hardwareInfo, publicKey, id, false);
}
}
connection.commit();
return null;
} catch (SQLException throwables) {
logger.error("SQL Error", throwables);
return null;
}
}
@Override
public void connectUserAndHardware(UserSession userSession, UserHardware hardware) {
AbstractSQLCoreProvider.SQLUserSession SQLUserSession = (AbstractSQLCoreProvider.SQLUserSession) userSession;
SQLUser SQLUser = (SQLUser) SQLUserSession.getUser();
SQLUserHardware SQLUserHardware = (SQLUserHardware) hardware;
if (SQLUser.hwidId == SQLUserHardware.id) return;
SQLUser.hwidId = SQLUserHardware.id;
try (Connection connection = holder.getConnection()) {
setUserHardwareId(connection, SQLUser.getUUID(), SQLUserHardware.id);
} catch (SQLException throwables) {
logger.error("SQL Error", throwables);
}
}
@Override
public void addPublicKeyToHardwareInfo(UserHardware hardware, byte[] publicKey) {
SQLUserHardware SQLUserHardware = (SQLUserHardware) hardware;
SQLUserHardware.publicKey = publicKey;
try (Connection connection = holder.getConnection()) {
connection.setAutoCommit(false);
PreparedStatement s = connection.prepareStatement(sqlUpdateHardwarePublicKey);
s.setBytes(1, publicKey);
s.setLong(2, SQLUserHardware.id);
s.executeUpdate();
connection.commit();
} catch (SQLException e) {
logger.error("SQL error", e);
}
}
@Override
public Iterable<User> getUsersByHardwareInfo(UserHardware hardware) {
List<User> users = new LinkedList<>();
try (Connection c = holder.getConnection()) {
c.setAutoCommit(false);
PreparedStatement s = c.prepareStatement(sqlUsersByHwidId);
s.setLong(1, Long.parseLong(hardware.getId()));
s.setQueryTimeout(MySQLSourceConfig.TIMEOUT);
try (ResultSet set = s.executeQuery()) {
while (!set.isLast()) {
users.add(constructUser(set));
}
}
c.commit();
} catch (SQLException e) {
logger.error("SQL error", e);
return null;
}
return users;
}
@Override
public void banHardware(UserHardware hardware) {
SQLUserHardware SQLUserHardware = (SQLUserHardware) hardware;
SQLUserHardware.banned = true;
try (Connection connection = holder.getConnection()) {
PreparedStatement s = connection.prepareStatement(sqlUpdateHardwareBanned);
s.setBoolean(1, true);
s.setLong(2, SQLUserHardware.id);
s.executeUpdate();
} catch (SQLException e) {
logger.error("SQL Error", e);
}
}
@Override
public void unbanHardware(UserHardware hardware) {
SQLUserHardware SQLUserHardware = (SQLUserHardware) hardware;
SQLUserHardware.banned = false;
try (Connection connection = holder.getConnection()) {
PreparedStatement s = connection.prepareStatement(sqlUpdateHardwareBanned);
s.setBoolean(1, false);
s.setLong(2, SQLUserHardware.id);
s.executeUpdate();
} catch (SQLException e) {
logger.error("SQL error", e);
}
}
@Override
protected AbstractSQLCoreProvider.SQLUserSession createSession(AbstractSQLCoreProvider.SQLUser user) {
return new SQLUserSession(user);
}
@Override
public UserSession extendedCheckServer(Client client, String username, String serverID) {
AbstractSQLCoreProvider.SQLUser user = (AbstractSQLCoreProvider.SQLUser) getUserByUsername(username);
if (user == null) {
return null;
}
if (user.getUsername().equals(username) && user.getServerId().equals(serverID)) {
return createSession(user);
}
return null;
}
public class SQLUserSession extends AbstractSQLCoreProvider.SQLUserSession implements UserSessionSupportHardware {
private transient SQLUser SQLUser;
protected transient SQLUserHardware hardware;
public SQLUserSession(AbstractSQLCoreProvider.SQLUser user) {
super(user);
SQLUser = (SQLUser) user;
}
@Override
public String getHardwareId() {
return SQLUser.hwidId == 0 ? null : String.valueOf(SQLUser.hwidId);
}
@Override
public UserHardware getHardware() {
if(hardware == null) {
hardware = (SQLUserHardware) getHardwareInfoById(String.valueOf(SQLUser.hwidId));
}
return hardware;
}
}
public static class SQLUserHardware implements UserHardware {
private final HardwareReportRequest.HardwareInfo hardwareInfo;
private final long id;
private byte[] publicKey;
private boolean banned;
public SQLUserHardware(HardwareReportRequest.HardwareInfo hardwareInfo, byte[] publicKey, long id, boolean banned) {
this.hardwareInfo = hardwareInfo;
this.publicKey = publicKey;
this.id = id;
this.banned = banned;
}
@Override
public HardwareReportRequest.HardwareInfo getHardwareInfo() {
return hardwareInfo;
}
@Override
public byte[] getPublicKey() {
return publicKey;
}
@Override
public String getId() {
return String.valueOf(id);
}
@Override
public boolean isBanned() {
return banned;
}
@Override
public String toString() {
return "SQLUserHardware{" +
"hardwareInfo=" + hardwareInfo +
", publicKey=" + (publicKey == null ? null : new String(Base64.getEncoder().encode(publicKey))) +
", id=" + id +
", banned=" + banned +
'}';
}
}
public static class SQLUser extends AbstractSQLCoreProvider.SQLUser {
protected long hwidId;
public SQLUser(UUID uuid, String username, String accessToken, String serverId, String password, long hwidId) {
super(uuid, username, accessToken, serverId, password);
this.hwidId = hwidId;
}
@Override
public String toString() {
return "SQLUser{" +
"uuid=" + uuid +
", username='" + username + '\'' +
", permissions=" + permissions +
", hwidId=" + hwidId +
'}';
}
}
}

View file

@ -0,0 +1,17 @@
package pro.gravit.launchserver.auth.core;
import pro.gravit.launcher.base.ClientPermissions;
import java.util.UUID;
public interface User {
String getUsername();
UUID getUUID();
ClientPermissions getPermissions();
default boolean isBanned() {
return false;
}
}

View file

@ -0,0 +1,11 @@
package pro.gravit.launchserver.auth.core;
public interface UserSession {
String getID();
User getUser();
String getMinecraftAccessToken();
long getExpireIn();
}

View file

@ -0,0 +1,13 @@
package pro.gravit.launchserver.auth.core.interfaces;
import pro.gravit.launcher.base.request.secure.HardwareReportRequest;
public interface UserHardware {
HardwareReportRequest.HardwareInfo getHardwareInfo();
byte[] getPublicKey();
String getId();
boolean isBanned();
}

View file

@ -0,0 +1,4 @@
package pro.gravit.launchserver.auth.core.interfaces.provider;
public interface AuthSupport {
}

View file

@ -0,0 +1,22 @@
package pro.gravit.launchserver.auth.core.interfaces.provider;
import pro.gravit.launcher.base.events.request.AssetUploadInfoRequestEvent;
import pro.gravit.launcher.base.events.request.AuthRequestEvent;
import pro.gravit.launcher.base.events.request.GetAssetUploadUrlRequestEvent;
import pro.gravit.launchserver.auth.Feature;
import pro.gravit.launchserver.auth.core.User;
import java.util.Set;
@Feature(GetAssetUploadUrlRequestEvent.FEATURE_NAME)
public interface AuthSupportAssetUpload extends AuthSupport {
String getAssetUploadUrl(String name, User user);
default AuthRequestEvent.OAuthRequestEvent getAssetUploadToken(String name, User user) {
return null;
}
default AssetUploadInfoRequestEvent getAssetUploadInfo(User user) {
return new AssetUploadInfoRequestEvent(Set.of("SKIN", "CAPE"), AssetUploadInfoRequestEvent.SlimSupportConf.USER);
}
}

View file

@ -0,0 +1,10 @@
package pro.gravit.launchserver.auth.core.interfaces.provider;
import pro.gravit.launchserver.auth.core.User;
import pro.gravit.launchserver.auth.core.UserSession;
public interface AuthSupportExit extends AuthSupport {
void deleteSession(UserSession session);
void exitUser(User user);
}

View file

@ -0,0 +1,10 @@
package pro.gravit.launchserver.auth.core.interfaces.provider;
import pro.gravit.launchserver.auth.core.UserSession;
import pro.gravit.launchserver.socket.Client;
import java.io.IOException;
public interface AuthSupportExtendedCheckServer {
UserSession extendedCheckServer(Client client, String username, String serverID);
}

View file

@ -0,0 +1,9 @@
package pro.gravit.launchserver.auth.core.interfaces.provider;
import pro.gravit.launchserver.auth.Feature;
import pro.gravit.launchserver.auth.core.User;
@Feature("users")
public interface AuthSupportGetAllUsers extends AuthSupport {
Iterable<User> getAllUsers();
}

View file

@ -1,48 +1,40 @@
package pro.gravit.launchserver.auth.protect.hwid; package pro.gravit.launchserver.auth.core.interfaces.provider;
import pro.gravit.launcher.request.secure.HardwareReportRequest; import pro.gravit.launcher.base.request.secure.HardwareReportRequest;
import pro.gravit.launchserver.LaunchServer; import pro.gravit.launchserver.auth.core.User;
import pro.gravit.launchserver.auth.core.UserSession;
import pro.gravit.launchserver.auth.core.interfaces.UserHardware;
import pro.gravit.launchserver.helper.DamerauHelper; import pro.gravit.launchserver.helper.DamerauHelper;
import pro.gravit.launchserver.socket.Client;
import pro.gravit.utils.ProviderMap;
import pro.gravit.utils.helper.LogHelper;
import pro.gravit.utils.helper.SecurityHelper;
import java.util.Arrays; import java.util.Arrays;
public abstract class HWIDProvider { public interface AuthSupportHardware extends AuthSupport {
public static final ProviderMap<HWIDProvider> providers = new ProviderMap<>("HWIDProvider"); UserHardware getHardwareInfoByPublicKey(byte[] publicKey);
private static boolean registredProv = false;
public static void registerProviders() { UserHardware getHardwareInfoByData(HardwareReportRequest.HardwareInfo info);
if (!registredProv) {
providers.register("memory", MemoryHWIDProvider.class);
providers.register("mysql", MysqlHWIDProvider.class);
providers.register("json", JsonHWIDProvider.class);
registredProv = true;
}
}
public abstract HardwareReportRequest.HardwareInfo findHardwareInfoByPublicKey(byte[] publicKey, Client client) throws HWIDException; UserHardware getHardwareInfoById(String id);
public abstract void createHardwareInfo(HardwareReportRequest.HardwareInfo hardwareInfo, byte[] publicKey, Client client) throws HWIDException; UserHardware createHardwareInfo(HardwareReportRequest.HardwareInfo info, byte[] publicKey);
public abstract boolean addPublicKeyToHardwareInfo(HardwareReportRequest.HardwareInfo hardwareInfo, byte[] publicKey, Client client) throws HWIDException; void connectUserAndHardware(UserSession userSession, UserHardware hardware);
public void normalizeHardwareInfo(HardwareReportRequest.HardwareInfo hardwareInfo) { void addPublicKeyToHardwareInfo(UserHardware hardware, byte[] publicKey);
Iterable<User> getUsersByHardwareInfo(UserHardware hardware);
void banHardware(UserHardware hardware);
void unbanHardware(UserHardware hardware);
default void normalizeHardwareInfo(HardwareReportRequest.HardwareInfo hardwareInfo) {
if (hardwareInfo.baseboardSerialNumber != null) if (hardwareInfo.baseboardSerialNumber != null)
hardwareInfo.baseboardSerialNumber = hardwareInfo.baseboardSerialNumber.trim(); hardwareInfo.baseboardSerialNumber = hardwareInfo.baseboardSerialNumber.trim();
if (hardwareInfo.hwDiskId != null) hardwareInfo.hwDiskId = hardwareInfo.hwDiskId.trim(); if (hardwareInfo.hwDiskId != null) hardwareInfo.hwDiskId = hardwareInfo.hwDiskId.trim();
} }
public static class HardwareInfoCompareResult {
public double firstSpoofingLevel = 0.0;
public double secondSpoofingLevel = 0.0;
public double compareLevel;
}
//Required normalize HardwareInfo //Required normalize HardwareInfo
public HardwareInfoCompareResult compareHardwareInfo(HardwareReportRequest.HardwareInfo first, HardwareReportRequest.HardwareInfo second) { default HardwareInfoCompareResult compareHardwareInfo(HardwareReportRequest.HardwareInfo first, HardwareReportRequest.HardwareInfo second) {
HardwareInfoCompareResult result = new HardwareInfoCompareResult(); HardwareInfoCompareResult result = new HardwareInfoCompareResult();
if (first.hwDiskId == null || first.hwDiskId.isEmpty()) result.firstSpoofingLevel += 0.9; if (first.hwDiskId == null || first.hwDiskId.isEmpty()) result.firstSpoofingLevel += 0.9;
if (first.displayId == null || first.displayId.length < 4) result.firstSpoofingLevel += 0.3; if (first.displayId == null || first.displayId.length < 4) result.firstSpoofingLevel += 0.3;
@ -98,17 +90,9 @@ public HardwareInfoCompareResult compareHardwareInfo(HardwareReportRequest.Hardw
return result; return result;
} }
protected void printHardwareInfo(LogHelper.Level logLevel, HardwareReportRequest.HardwareInfo info) { class HardwareInfoCompareResult {
LogHelper.log(logLevel, String.format("[HardwareInfo] Processor: logical %d | physical %d | freq %d | bitness %d", info.logicalProcessors, info.physicalProcessors, info.processorMaxFreq, info.bitness), false); public double firstSpoofingLevel = 0.0;
LogHelper.log(logLevel, String.format("[HardwareInfo] Memory max: %d | battery %s", info.totalMemory, info.battery ? "true" : "false"), false); public double secondSpoofingLevel = 0.0;
LogHelper.log(logLevel, String.format("[HardwareInfo] HWDiskID %s | baseboardSerialNumber %s | displayId hash: %s", info.hwDiskId, info.baseboardSerialNumber, SecurityHelper.toHex(SecurityHelper.digest(SecurityHelper.DigestAlgorithm.MD5, info.displayId))), false); public double compareLevel;
}
public void init(LaunchServer server) {
}
public void close() {
} }
} }

View file

@ -0,0 +1,12 @@
package pro.gravit.launchserver.auth.core.interfaces.provider;
import pro.gravit.launcher.base.request.auth.AuthRequest;
import pro.gravit.launchserver.auth.Feature;
import pro.gravit.launchserver.auth.core.User;
import java.util.Map;
@Feature("registration")
public interface AuthSupportRegistration extends AuthSupport {
User registration(String login, String email, AuthRequest.AuthPasswordInterface password, Map<String, String> properties);
}

View file

@ -0,0 +1,10 @@
package pro.gravit.launchserver.auth.core.interfaces.provider;
import pro.gravit.launchserver.auth.core.User;
import pro.gravit.launchserver.manangers.AuthManager;
import java.io.IOException;
public interface AuthSupportSudo {
AuthManager.AuthReport sudo(User user, boolean shadow) throws IOException;
}

View file

@ -0,0 +1,8 @@
package pro.gravit.launchserver.auth.core.interfaces.session;
import pro.gravit.launchserver.auth.core.interfaces.UserHardware;
public interface UserSessionSupportHardware {
String getHardwareId();
UserHardware getHardware();
}

View file

@ -0,0 +1,13 @@
package pro.gravit.launchserver.auth.core.interfaces.session;
import java.security.PrivateKey;
import java.security.PublicKey;
public interface UserSessionSupportKeys {
ClientProfileKeys getClientProfileKeys();
record ClientProfileKeys(PublicKey publicKey, PrivateKey privateKey, byte[] signature /* V2 */, long expiresAt,
long refreshedAfter) {
}
}

View file

@ -0,0 +1,7 @@
package pro.gravit.launchserver.auth.core.interfaces.session;
import java.util.Map;
public interface UserSessionSupportProperties {
Map<String, String> getProperties();
}

View file

@ -0,0 +1,25 @@
package pro.gravit.launchserver.auth.core.interfaces.user;
import java.util.Map;
public interface UserSupportAdditionalData {
String getProperty(String name);
default String getPropertyUnprivileged(String name) {
return getProperty(name);
}
default String getPropertyUnprivilegedSelf(String name) {
return getProperty(name);
}
Map<String, String> getPropertiesMap();
default Map<String, String> getPropertiesMapUnprivileged() {
return getPropertiesMap();
}
default Map<String, String> getPropertiesMapUnprivilegedSelf() {
return getPropertiesMapUnprivileged();
}
}

View file

@ -0,0 +1,7 @@
package pro.gravit.launchserver.auth.core.interfaces.user;
import java.util.Map;
public interface UserSupportProperties {
Map<String, String> getProperties();
}

View file

@ -0,0 +1,34 @@
package pro.gravit.launchserver.auth.core.interfaces.user;
import pro.gravit.launcher.base.profiles.ClientProfile;
import pro.gravit.launcher.base.profiles.Texture;
import java.util.HashMap;
import java.util.Map;
public interface UserSupportTextures {
Texture getSkinTexture();
Texture getCloakTexture();
default Texture getSkinTexture(ClientProfile profile) {
return getSkinTexture();
}
default Texture getCloakTexture(ClientProfile profile) {
return getCloakTexture();
}
default Map<String, Texture> getUserAssets() {
var skin = getSkinTexture();
var cape = getCloakTexture();
Map<String, Texture> map = new HashMap<>();
if (skin != null) {
map.put("SKIN", skin);
}
if (cape != null) {
map.put("CAPE", cape);
}
return map;
}
}

View file

@ -0,0 +1,14 @@
package pro.gravit.launchserver.auth.core.openid;
import com.google.gson.annotations.SerializedName;
public record AccessTokenResponse(@SerializedName("access_token") String accessToken,
@SerializedName("expires_in") Long expiresIn,
@SerializedName("refresh_expires_in") Long refreshExpiresIn,
@SerializedName("refresh_token") String refreshToken,
@SerializedName("token_type") String tokenType,
@SerializedName("id_token") String idToken,
@SerializedName("not-before-policy") Integer notBeforePolicy,
@SerializedName("session_state") String sessionState,
@SerializedName("scope") String scope) {
}

View file

@ -0,0 +1,178 @@
package pro.gravit.launchserver.auth.core.openid;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
import pro.gravit.launcher.base.ClientPermissions;
import pro.gravit.launcher.base.events.request.GetAvailabilityAuthRequestEvent;
import pro.gravit.launcher.base.request.auth.AuthRequest;
import pro.gravit.launcher.base.request.auth.password.AuthCodePassword;
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.auth.AuthException;
import pro.gravit.launchserver.auth.AuthProviderPair;
import pro.gravit.launchserver.auth.HikariSQLSourceConfig;
import pro.gravit.launchserver.auth.core.AuthCoreProvider;
import pro.gravit.launchserver.auth.core.User;
import pro.gravit.launchserver.auth.core.UserSession;
import pro.gravit.launchserver.manangers.AuthManager;
import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.response.auth.AuthResponse;
import pro.gravit.utils.helper.LogHelper;
import java.io.IOException;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
public class OpenIDAuthCoreProvider extends AuthCoreProvider {
private transient SQLUserStore sqlUserStore;
private transient SQLServerSessionStore sqlSessionStore;
private transient OpenIDAuthenticator openIDAuthenticator;
private OpenIDConfig openIDConfig;
private HikariSQLSourceConfig sqlSourceConfig;
@Override
public List<GetAvailabilityAuthRequestEvent.AuthAvailabilityDetails> getDetails(Client client) {
return openIDAuthenticator.getDetails();
}
@Override
public User getUserByUsername(String username) {
return sqlUserStore.getByUsername(username);
}
@Override
public User getUserByUUID(UUID uuid) {
return sqlUserStore.getUserByUUID(uuid);
}
@Override
public UserSession getUserSessionByOAuthAccessToken(String accessToken) throws OAuthAccessTokenExpired {
return openIDAuthenticator.getUserSessionByOAuthAccessToken(accessToken);
}
@Override
public AuthManager.AuthReport refreshAccessToken(String oldRefreshToken, AuthResponse.AuthContext context) {
var tokens = openIDAuthenticator.refreshAccessToken(oldRefreshToken);
var accessToken = tokens.accessToken();
var refreshToken = tokens.refreshToken();
long expiresIn = TimeUnit.SECONDS.toMillis(tokens.accessTokenExpiresIn());
UserSession session;
try {
session = openIDAuthenticator.getUserSessionByOAuthAccessToken(accessToken);
} catch (OAuthAccessTokenExpired e) {
throw new RuntimeException("invalid token", e);
}
return AuthManager.AuthReport.ofOAuth(accessToken, refreshToken,
expiresIn, session);
}
@Override
public AuthManager.AuthReport authorize(String login, AuthResponse.AuthContext context, AuthRequest.AuthPasswordInterface password, boolean minecraftAccess) throws IOException {
if (password == null) {
throw AuthException.wrongPassword();
}
var authCodePassword = (AuthCodePassword) password;
var tokens = openIDAuthenticator.authorize(authCodePassword);
var accessToken = tokens.accessToken();
var refreshToken = tokens.refreshToken();
var user = openIDAuthenticator.createUserFromToken(accessToken);
long expiresIn = TimeUnit.SECONDS.toMillis(tokens.accessTokenExpiresIn());
sqlUserStore.createOrUpdateUser(user);
UserSession session;
try {
session = openIDAuthenticator.getUserSessionByOAuthAccessToken(accessToken);
} catch (OAuthAccessTokenExpired e) {
throw new AuthException("invalid token", e);
}
if (minecraftAccess) {
var minecraftToken = generateMinecraftToken(user);
return AuthManager.AuthReport.ofOAuthWithMinecraft(minecraftToken, accessToken, refreshToken,
expiresIn, session);
} else {
return AuthManager.AuthReport.ofOAuth(accessToken, refreshToken,
expiresIn, session);
}
}
private String generateMinecraftToken(User user) {
return Jwts.builder()
.issuer("LaunchServer")
.subject(user.getUUID().toString())
.claim("preferred_username", user.getUsername())
.expiration(Date.from(Instant.now().plus(24, ChronoUnit.HOURS)))
.signWith(server.keyAgreementManager.ecdsaPrivateKey)
.compact();
}
private User createUserFromMinecraftToken(String accessToken) throws AuthException {
try {
var parser = Jwts.parser()
.requireIssuer("LaunchServer")
.verifyWith(server.keyAgreementManager.ecdsaPublicKey)
.build();
var claims = parser.parseSignedClaims(accessToken);
var username = claims.getPayload().get("preferred_username", String.class);
var uuid = UUID.fromString(claims.getPayload().getSubject());
return new UserEntity(username, uuid, new ClientPermissions());
} catch (JwtException e) {
throw new AuthException("Bad minecraft token", e);
}
}
@Override
public void init(LaunchServer server, AuthProviderPair pair) {
super.init(server, pair);
this.sqlSourceConfig.init();
this.sqlUserStore = new SQLUserStore(sqlSourceConfig);
this.sqlUserStore.init();
this.sqlSessionStore = new SQLServerSessionStore(sqlSourceConfig);
this.sqlSessionStore.init();
this.openIDAuthenticator = new OpenIDAuthenticator(openIDConfig);
}
@Override
public User checkServer(Client client, String username, String serverID) {
var savedServerId = sqlSessionStore.getServerIdByUsername(username);
if (!serverID.equals(savedServerId)) {
return null;
}
return sqlUserStore.getByUsername(username);
}
@Override
public boolean joinServer(Client client, String username, UUID uuid, String accessToken, String serverID) {
User user;
try {
user = createUserFromMinecraftToken(accessToken);
} catch (AuthException e) {
LogHelper.error(e);
return false;
}
if (!user.getUUID().equals(uuid)) {
return false;
}
sqlUserStore.createOrUpdateUser(user);
return sqlSessionStore.joinServer(user.getUUID(), user.getUsername(), serverID);
}
@Override
public void close() {
sqlSourceConfig.close();
}
}

View file

@ -0,0 +1,232 @@
package pro.gravit.launchserver.auth.core.openid;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Jwk;
import io.jsonwebtoken.security.JwkSet;
import io.jsonwebtoken.security.Jwks;
import pro.gravit.launcher.base.ClientPermissions;
import pro.gravit.launcher.base.Launcher;
import pro.gravit.launcher.base.events.request.GetAvailabilityAuthRequestEvent;
import pro.gravit.launcher.base.request.auth.details.AuthWebViewDetails;
import pro.gravit.launcher.base.request.auth.password.AuthCodePassword;
import pro.gravit.launchserver.auth.AuthException;
import pro.gravit.launchserver.auth.core.AuthCoreProvider;
import pro.gravit.launchserver.auth.core.User;
import pro.gravit.launchserver.auth.core.UserSession;
import pro.gravit.utils.helper.CommonHelper;
import pro.gravit.utils.helper.QueryHelper;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.security.Key;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.stream.Collectors;
public class OpenIDAuthenticator {
private static final HttpClient CLIENT = HttpClient.newBuilder().build();
private final OpenIDConfig openIDConfig;
private final JwtParser jwtParser;
public OpenIDAuthenticator(OpenIDConfig openIDConfig) {
this.openIDConfig = openIDConfig;
var keyLocator = loadKeyLocator(openIDConfig);
this.jwtParser = Jwts.parser()
.keyLocator(keyLocator)
.requireIssuer(openIDConfig.issuer())
.require("azp", openIDConfig.clientId())
.build();
}
public List<GetAvailabilityAuthRequestEvent.AuthAvailabilityDetails> getDetails() {
var state = UUID.randomUUID().toString();
var uri = QueryBuilder.get(openIDConfig.authorizationEndpoint())
.addQuery("response_type", "code")
.addQuery("client_id", openIDConfig.clientId())
.addQuery("redirect_uri", openIDConfig.redirectUri())
.addQuery("scope", openIDConfig.scopes())
.addQuery("state", state)
.toUriString();
return List.of(new AuthWebViewDetails(uri, openIDConfig.redirectUri()));
}
public TokenResponse refreshAccessToken(String oldRefreshToken) {
var postBody = QueryBuilder.post()
.addQuery("grant_type", "refresh_token")
.addQuery("refresh_token", oldRefreshToken)
.addQuery("client_id", openIDConfig.clientId())
.addQuery("client_secret", openIDConfig.clientSecret())
.toString();
var accessTokenResponse = requestToken(postBody);
var accessToken = accessTokenResponse.accessToken();
var refreshToken = accessTokenResponse.refreshToken();
try {
readAndVerifyToken(accessToken);
} catch (AuthException e) {
throw new RuntimeException(e);
}
var accessTokenExpiresIn = Objects.requireNonNullElse(accessTokenResponse.expiresIn(), 0L);
var refreshTokenExpiresIn = Objects.requireNonNullElse(accessTokenResponse.refreshExpiresIn(), 0L);
return new TokenResponse(accessToken, accessTokenExpiresIn,
refreshToken, refreshTokenExpiresIn);
}
public UserSession getUserSessionByOAuthAccessToken(String accessToken) throws AuthCoreProvider.OAuthAccessTokenExpired {
Jws<Claims> token;
try {
token = readAndVerifyToken(accessToken);
} catch (AuthException e) {
throw new AuthCoreProvider.OAuthAccessTokenExpired("Can't read token", e);
}
var user = createUserFromToken(token);
long expiresIn = 0;
var expDate = token.getPayload().getExpiration();
if (expDate != null) {
expiresIn = expDate.toInstant().toEpochMilli();
}
return new OpenIDUserSession(user, accessToken, expiresIn);
}
public TokenResponse authorize(AuthCodePassword authCode) throws IOException {
var uri = URI.create(authCode.uri);
var queries = QueryHelper.splitUriQuery(uri);
String code = CommonHelper.multimapFirstOrNullValue("code", queries);
String error = CommonHelper.multimapFirstOrNullValue("error", queries);
String errorDescription = CommonHelper.multimapFirstOrNullValue("error_description", queries);
if (error != null && !error.isBlank()) {
throw new AuthException("Auth error. Error: %s, description: %s".formatted(error, errorDescription));
}
var postBody = QueryBuilder.post()
.addQuery("grant_type", "authorization_code")
.addQuery("code", code)
.addQuery("redirect_uri", openIDConfig.redirectUri())
.addQuery("client_id", openIDConfig.clientId())
.addQuery("client_secret", openIDConfig.clientSecret())
.toString();
var accessTokenResponse = requestToken(postBody);
var accessToken = accessTokenResponse.accessToken();
var refreshToken = accessTokenResponse.refreshToken();
readAndVerifyToken(accessToken);
var accessTokenExpiresIn = Objects.requireNonNullElse(accessTokenResponse.expiresIn(), 0L);
var refreshTokenExpiresIn = Objects.requireNonNullElse(accessTokenResponse.refreshExpiresIn(), 0L);
return new TokenResponse(accessToken, accessTokenExpiresIn,
refreshToken, refreshTokenExpiresIn);
}
public User createUserFromToken(String accessToken) throws AuthException {
return createUserFromToken(readAndVerifyToken(accessToken));
}
private Jws<Claims> readAndVerifyToken(String accessToken) throws AuthException {
if (accessToken == null) {
throw new AuthException("Token is null");
}
try {
return jwtParser.parseSignedClaims(accessToken);
} catch (JwtException e) {
throw new AuthException("Bad token", e);
}
}
private User createUserFromToken(Jws<Claims> token) {
var username = token.getPayload().get(openIDConfig.extractorConfig().usernameClaim(), String.class);
var uuidStr = token.getPayload().get(openIDConfig.extractorConfig().uuidClaim(), String.class);
var uuid = UUID.fromString(uuidStr);
return new UserEntity(username, uuid, new ClientPermissions());
}
private AccessTokenResponse requestToken(String postBody) {
var request = HttpRequest.newBuilder()
.uri(openIDConfig.tokenUri())
.header("Content-Type", "application/x-www-form-urlencoded")
.header("Accept", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(postBody))
.build();
HttpResponse<String> resp;
try {
resp = CLIENT.send(request, HttpResponse.BodyHandlers.ofString());
} catch (IOException | InterruptedException e) {
throw new RuntimeException(e);
}
return Launcher.gsonManager.gson.fromJson(resp.body(), AccessTokenResponse.class);
}
private static KeyLocator loadKeyLocator(OpenIDConfig openIDConfig) {
var request = HttpRequest.newBuilder(openIDConfig.jwksUri()).GET().build();
HttpResponse<String> response;
try {
response = CLIENT.send(request, HttpResponse.BodyHandlers.ofString());
} catch (IOException | InterruptedException e) {
throw new RuntimeException(e);
}
var jwks = Jwks.setParser().build().parse(response.body());
return new KeyLocator(jwks);
}
private static class KeyLocator extends LocatorAdapter<Key> {
private final Map<String, Key> keys;
public KeyLocator(JwkSet jwks) {
this.keys = jwks.getKeys().stream().collect(
Collectors.toMap(jwk -> String.valueOf(jwk.get("kid")), Jwk::toKey));
}
@Override
protected Key locate(JweHeader header) {
return super.locate(header);
}
@Override
protected Key locate(JwsHeader header) {
return keys.get(header.getKeyId());
}
@Override
protected Key doLocate(Header header) {
return super.doLocate(header);
}
}
record OpenIDUserSession(User user, String token, long expiresIn) implements UserSession {
@Override
public String getID() {
return user.getUsername();
}
@Override
public User getUser() {
return user;
}
@Override
public String getMinecraftAccessToken() {
return token;
}
@Override
public long getExpireIn() {
return expiresIn;
}
}
}

View file

@ -0,0 +1,10 @@
package pro.gravit.launchserver.auth.core.openid;
import java.net.URI;
public record OpenIDConfig(URI tokenUri, String authorizationEndpoint, String clientId, String clientSecret,
String redirectUri, URI jwksUri, String scopes, String issuer,
ClaimExtractorConfig extractorConfig) {
public record ClaimExtractorConfig(String usernameClaim, String uuidClaim) {}
}

View file

@ -0,0 +1,59 @@
package pro.gravit.launchserver.auth.core.openid;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
/**
* @author Xakep_SDK
*/
public class QueryBuilder {
private final String uri;
private final StringBuilder query = new StringBuilder();
public QueryBuilder(String uri) {
this.uri = uri;
}
public static QueryBuilder get(String uri) {
Objects.requireNonNull(uri, "uri");
if (uri.endsWith("/")) {
uri = uri.substring(0, uri.length() - 1);
}
return new QueryBuilder(uri);
}
public static QueryBuilder post() {
return new QueryBuilder(null);
}
public QueryBuilder addQuery(String key, String value) {
if (!query.isEmpty()) {
query.append('&');
}
query.append(URLEncoder.encode(key, StandardCharsets.UTF_8))
.append('=')
.append(URLEncoder.encode(value, StandardCharsets.UTF_8));
return this;
}
public String toUriString() {
if (uri != null) {
if (query. isEmpty()) {
return uri;
}
return uri + '?' + query;
}
return toQueryString();
}
public String toQueryString() {
return query.toString();
}
@Override
public String toString() {
return toUriString();
}
}

View file

@ -0,0 +1,97 @@
package pro.gravit.launchserver.auth.core.openid;
import pro.gravit.launchserver.auth.SQLSourceConfig;
import pro.gravit.utils.helper.LogHelper;
import java.sql.SQLException;
import java.util.UUID;
public class SQLServerSessionStore implements ServerSessionStore {
private static final String CREATE_TABLE = """
create table if not exists `gravit_server_session` (
id int auto_increment,
uuid varchar(36),
username varchar(255),
server_id varchar(41),
primary key (id),
unique (uuid),
unique (username)
);
""";
private static final String DELETE_SERVER_ID = """
delete from `gravit_server_session` where uuid = ?
""";
private static final String INSERT_SERVER_ID = """
insert into `gravit_server_session` (uuid, username, server_id) values (?, ?, ?)
""";
private static final String SELECT_SERVER_ID_BY_USERNAME = """
select server_id from `gravit_server_session` where username = ?
""";
private final SQLSourceConfig sqlSourceConfig;
public SQLServerSessionStore(SQLSourceConfig sqlSourceConfig) {
this.sqlSourceConfig = sqlSourceConfig;
}
@Override
public boolean joinServer(UUID uuid, String username, String serverId) {
try (var connection = sqlSourceConfig.getConnection()) {
connection.setAutoCommit(false);
var savepoint = connection.setSavepoint();
try (var deleteServerIdStmt = connection.prepareStatement(DELETE_SERVER_ID);
var insertServerIdStmt = connection.prepareStatement(INSERT_SERVER_ID)) {
deleteServerIdStmt.setString(1, uuid.toString());
deleteServerIdStmt.execute();
insertServerIdStmt.setString(1, uuid.toString());
insertServerIdStmt.setString(2, username);
insertServerIdStmt.setString(3, serverId);
insertServerIdStmt.execute();
connection.commit();
return true;
} catch (Exception e) {
connection.rollback(savepoint);
throw e;
}
} catch (SQLException e) {
LogHelper.debug("Can't join server. Username: %s".formatted(username));
LogHelper.error(e);
}
return false;
}
@Override
public String getServerIdByUsername(String username) {
try (var connection = sqlSourceConfig.getConnection();
var selectServerId = connection.prepareStatement(SELECT_SERVER_ID_BY_USERNAME)) {
selectServerId.setString(1, username);
try (var rs = selectServerId.executeQuery()) {
if (!rs.next()) {
return null;
}
return rs.getString("server_id");
}
} catch (SQLException e) {
LogHelper.debug("Can't find server id by username. Username: %s".formatted(username));
LogHelper.error(e);
}
return null;
}
public void init() {
try (var connection = sqlSourceConfig.getConnection()) {
connection.setAutoCommit(false);
var savepoint = connection.setSavepoint();
try (var createTableStmt = connection.prepareStatement(CREATE_TABLE)) {
createTableStmt.execute();
connection.commit();
} catch (Exception e) {
connection.rollback(savepoint);
throw e;
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}

View file

@ -0,0 +1,124 @@
package pro.gravit.launchserver.auth.core.openid;
import pro.gravit.launcher.base.ClientPermissions;
import pro.gravit.launchserver.auth.HikariSQLSourceConfig;
import pro.gravit.launchserver.auth.core.User;
import pro.gravit.utils.helper.LogHelper;
import java.sql.SQLException;
import java.util.UUID;
public class SQLUserStore implements UserStore {
private static final String CREATE_USER_TABLE = """
create table if not exists `gravit_user` (
id int auto_increment,
uuid varchar(36),
username varchar(255),
primary key (id),
unique (uuid),
unique (username)
)
""";
private static final String INSERT_USER = """
insert into `gravit_user` (uuid, username) values (?, ?)
""";
private static final String DELETE_USER_BY_NAME = """
delete `gravit_user` where username = ?
""";
private static final String SELECT_USER_BY_NAME = """
select uuid, username from `gravit_user` where username = ?
""";
private static final String SELECT_USER_BY_UUID = """
select uuid, username from `gravit_user` where uuid = ?
""";
private final HikariSQLSourceConfig sqlSourceConfig;
public SQLUserStore(HikariSQLSourceConfig sqlSourceConfig) {
this.sqlSourceConfig = sqlSourceConfig;
}
@Override
public User getByUsername(String username) {
try (var connection = sqlSourceConfig.getConnection();
var selectUserStmt = connection.prepareStatement(SELECT_USER_BY_NAME)) {
selectUserStmt.setString(1, username);
try (var rs = selectUserStmt.executeQuery()) {
if (!rs.next()) {
LogHelper.debug("User not found, username: %s".formatted(username));
return null;
}
return new UserEntity(rs.getString("username"),
UUID.fromString(rs.getString("uuid")),
new ClientPermissions());
}
} catch (SQLException e) {
LogHelper.error(e);
}
return null;
}
@Override
public User getUserByUUID(UUID uuid) {
try (var connection = sqlSourceConfig.getConnection();
var selectUserStmt = connection.prepareStatement(SELECT_USER_BY_UUID)) {
selectUserStmt.setString(1, uuid.toString());
try (var rs = selectUserStmt.executeQuery()) {
if (!rs.next()) {
LogHelper.debug("User not found, UUID: %s".formatted(uuid));
return null;
}
return new UserEntity(rs.getString("username"),
UUID.fromString(rs.getString("uuid")),
new ClientPermissions());
}
} catch (SQLException e) {
LogHelper.error(e);
}
return null;
}
@Override
public void createOrUpdateUser(User user) {
try (var connection = sqlSourceConfig.getConnection()) {
connection.setAutoCommit(false);
var savepoint = connection.setSavepoint();
try (var deleteUserStmt = connection.prepareStatement(DELETE_USER_BY_NAME);
var insertUserStmt = connection.prepareStatement(INSERT_USER)) {
deleteUserStmt.setString(1, user.getUsername());
deleteUserStmt.execute();
insertUserStmt.setString(1, user.getUUID().toString());
insertUserStmt.setString(2, user.getUsername());
insertUserStmt.execute();
connection.commit();
LogHelper.debug("User saved. UUID: %s, username: %s".formatted(user.getUUID(), user.getUsername()));
} catch (Exception e) {
connection.rollback(savepoint);
throw e;
}
} catch (SQLException e) {
LogHelper.debug("Failed to save user");
LogHelper.error(e);
throw new RuntimeException("Failed to save user", e);
}
}
public void init() {
try (var connection = sqlSourceConfig.getConnection()) {
connection.setAutoCommit(false);
var savepoint = connection.setSavepoint();
try (var createUserTableStmt = connection.prepareStatement(CREATE_USER_TABLE)) {
createUserTableStmt.execute();
connection.commit();
} catch (Exception e) {
connection.rollback(savepoint);
throw e;
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}

View file

@ -0,0 +1,8 @@
package pro.gravit.launchserver.auth.core.openid;
import java.util.UUID;
public interface ServerSessionStore {
boolean joinServer(UUID uuid, String username, String serverId);
String getServerIdByUsername(String username);
}

View file

@ -0,0 +1,5 @@
package pro.gravit.launchserver.auth.core.openid;
public record TokenResponse(String accessToken, long accessTokenExpiresIn,
String refreshToken, long refreshTokenExpiresIn) {
}

View file

@ -0,0 +1,23 @@
package pro.gravit.launchserver.auth.core.openid;
import pro.gravit.launcher.base.ClientPermissions;
import pro.gravit.launchserver.auth.core.User;
import java.util.UUID;
record UserEntity(String username, UUID uuid, ClientPermissions permissions) implements User {
@Override
public String getUsername() {
return username;
}
@Override
public UUID getUUID() {
return uuid;
}
@Override
public ClientPermissions getPermissions() {
return permissions;
}
}

View file

@ -0,0 +1,13 @@
package pro.gravit.launchserver.auth.core.openid;
import pro.gravit.launchserver.auth.core.User;
import java.util.UUID;
public interface UserStore {
User getByUsername(String username);
User getUserByUUID(UUID uuid);
void createOrUpdateUser(User user);
}

View file

@ -1,77 +0,0 @@
package pro.gravit.launchserver.auth.handler;
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.auth.AuthException;
import pro.gravit.launchserver.auth.provider.AuthProviderResult;
import pro.gravit.utils.ProviderMap;
import java.io.IOException;
import java.util.UUID;
public abstract class AuthHandler implements AutoCloseable {
public static final ProviderMap<AuthHandler> providers = new ProviderMap<>("AuthHandler");
private static boolean registredHandl = false;
protected transient LaunchServer srv;
public static UUID authError(String message) throws AuthException {
throw new AuthException(message);
}
public static void registerHandlers() {
if (!registredHandl) {
providers.register("null", NullAuthHandler.class);
providers.register("json", JsonAuthHandler.class);
providers.register("memory", MemoryAuthHandler.class);
providers.register("mysql", MySQLAuthHandler.class);
providers.register("postgresql", PostgreSQLAuthHandler.class);
providers.register("request", RequestAuthHandler.class);
providers.register("hibernate", HibernateAuthHandler.class);
registredHandl = true;
}
}
/**
* Returns the UUID associated with the account
*
* @param authResult {@link pro.gravit.launchserver.auth.provider.AuthProvider} result
* @return User UUID
* @throws IOException Internal Script Error
*/
public abstract UUID auth(AuthProviderResult authResult) throws IOException;
/**
* Validates serverID
*
* @param username user name
* @param serverID serverID to check
* @return user UUID
* @throws IOException Internal Script Error
*/
public abstract UUID checkServer(String username, String serverID) throws IOException;
@Override
public abstract void close() throws IOException;
/**
* Checks assessToken for validity and saves serverID if successful
*
* @param username user name
* @param accessToken assessToken to check
* @param serverID serverID to save
* @return true - allow, false - deny
* @throws IOException Internal Script Error
*/
public abstract boolean joinServer(String username, String accessToken, String serverID) throws IOException;
public abstract UUID usernameToUUID(String username) throws IOException;
public abstract String uuidToUsername(UUID uuid) throws IOException;
public void init(LaunchServer srv) {
this.srv = srv;
}
}

View file

@ -1,200 +0,0 @@
package pro.gravit.launchserver.auth.handler;
import pro.gravit.launcher.Launcher;
import pro.gravit.launcher.NeedGarbageCollection;
import pro.gravit.launchserver.Reconfigurable;
import pro.gravit.launchserver.auth.provider.AuthProviderResult;
import pro.gravit.utils.command.Command;
import pro.gravit.utils.command.SubCommand;
import pro.gravit.utils.helper.*;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
public abstract class CachedAuthHandler extends AuthHandler implements NeedGarbageCollection, Reconfigurable {
private transient final Map<UUID, Entry> entryCache = new HashMap<>(1024);
private transient final Map<String, UUID> usernamesCache = new HashMap<>(1024);
@Override
public Map<String, Command> getCommands() {
Map<String, Command> commands = new HashMap<>();
commands.put("clear", new SubCommand() {
@Override
public void invoke(String... args) {
long entryCacheSize = entryCache.size();
long usernamesCacheSize = usernamesCache.size();
entryCache.clear();
usernamesCache.clear();
LogHelper.info("Cleared cache: %d Entry %d Usernames", entryCacheSize, usernamesCacheSize);
}
});
commands.put("load", new SubCommand() {
@Override
public void invoke(String... args) throws Exception {
verifyArgs(args, 2);
LogHelper.info("CachedAuthHandler read from %s", args[0]);
int size_entry;
int size_username;
try (Reader reader = IOHelper.newReader(Paths.get(args[1]))) {
EntryAndUsername entryAndUsername = Launcher.gsonManager.configGson.fromJson(reader, EntryAndUsername.class);
size_entry = entryAndUsername.entryCache.size();
size_username = entryAndUsername.usernameCache.size();
loadEntryCache(entryAndUsername.entryCache);
loadUsernameCache(entryAndUsername.usernameCache);
}
LogHelper.subInfo("Readed %d entryCache %d usernameCache", size_entry, size_username);
}
});
commands.put("unload", new SubCommand() {
@Override
public void invoke(String... args) throws Exception {
verifyArgs(args, 2);
LogHelper.info("CachedAuthHandler write to %s", args[1]);
Map<UUID, CachedAuthHandler.Entry> entryCache = getEntryCache();
Map<String, UUID> usernamesCache = getUsernamesCache();
EntryAndUsername serializable = new EntryAndUsername();
serializable.entryCache = entryCache;
serializable.usernameCache = usernamesCache;
try (Writer writer = IOHelper.newWriter(Paths.get(args[1]))) {
Launcher.gsonManager.configGson.toJson(serializable, writer);
}
LogHelper.subInfo("Write %d entryCache, %d usernameCache", entryCache.size(), usernamesCache.size());
}
});
return commands;
}
protected void addEntry(Entry entry) {
Entry previous = entryCache.put(entry.uuid, entry);
if (previous != null)
usernamesCache.remove(CommonHelper.low(previous.username));
usernamesCache.put(CommonHelper.low(entry.username), entry.uuid);
}
@Override
public final synchronized UUID auth(AuthProviderResult result) throws IOException {
Entry entry = getEntry(result.username);
if (entry == null || !updateAuth(entry.uuid, entry.username, result.accessToken))
return authError(String.format("UUID is null for username '%s'", result.username));
// Update cached access token (and username case)
entry.username = result.username;
entry.accessToken = result.accessToken;
entry.serverID = null;
return entry.uuid;
}
@Override
public synchronized UUID checkServer(String username, String serverID) throws IOException {
Entry entry = getEntry(username);
return entry != null && username.equals(entry.username) &&
serverID.equals(entry.serverID) ? entry.uuid : null;
}
protected abstract Entry fetchEntry(String username) throws IOException;
protected abstract Entry fetchEntry(UUID uuid) throws IOException;
private Entry getEntry(String username) throws IOException {
UUID uuid = usernamesCache.get(CommonHelper.low(username));
if (uuid != null)
return getEntry(uuid);
// Fetch entry by username
Entry entry = fetchEntry(username);
if (entry != null)
addEntry(entry);
// Return what we got
return entry;
}
private Entry getEntry(UUID uuid) throws IOException {
Entry entry = entryCache.get(uuid);
if (entry == null) {
entry = fetchEntry(uuid);
if (entry != null)
addEntry(entry);
}
return entry;
}
@Override
public synchronized boolean joinServer(String username, String accessToken, String serverID) throws IOException {
Entry entry = getEntry(username);
if (entry == null || !username.equals(entry.username) || !accessToken.equals(entry.accessToken) ||
!updateServerID(entry.uuid, serverID))
return false; // Account doesn't exist or invalid access token
// Update cached server ID
entry.serverID = serverID;
return true;
}
public synchronized void garbageCollection() {
entryCache.clear();
usernamesCache.clear();
}
public Map<UUID, Entry> getEntryCache() {
return entryCache;
}
public Map<String, UUID> getUsernamesCache() {
return usernamesCache;
}
public void loadEntryCache(Map<UUID, Entry> map) {
entryCache.putAll(map);
}
public void loadUsernameCache(Map<String, UUID> map) {
usernamesCache.putAll(map);
}
protected abstract boolean updateAuth(UUID uuid, String username, String accessToken) throws IOException;
protected abstract boolean updateServerID(UUID uuid, String serverID) throws IOException;
@Override
public final synchronized UUID usernameToUUID(String username) throws IOException {
Entry entry = getEntry(username);
return entry == null ? null : entry.uuid;
}
@Override
public final synchronized String uuidToUsername(UUID uuid) throws IOException {
Entry entry = getEntry(uuid);
return entry == null ? null : entry.username;
}
public static final class Entry {
public final UUID uuid;
private String username;
private String accessToken;
private String serverID;
public Entry(UUID uuid, String username, String accessToken, String serverID) {
this.uuid = Objects.requireNonNull(uuid, "uuid");
this.username = Objects.requireNonNull(username, "username");
this.accessToken = accessToken == null ? null : SecurityHelper.verifyToken(accessToken);
this.serverID = serverID == null ? null : VerifyHelper.verifyServerID(serverID);
}
}
protected static class EntryAndUsername {
public Map<UUID, CachedAuthHandler.Entry> entryCache;
public Map<String, UUID> usernameCache;
}
}

View file

@ -1,42 +0,0 @@
package pro.gravit.launchserver.auth.handler;
import pro.gravit.launchserver.dao.User;
import java.util.UUID;
public class HibernateAuthHandler extends CachedAuthHandler {
@Override
protected Entry fetchEntry(String username) {
User user = srv.config.dao.userDAO.findByUsername(username);
if (user == null) return null;
return new Entry(user.getUuid(), user.getUsername(), user.getAccessToken(), user.getServerID());
}
@Override
protected Entry fetchEntry(UUID uuid) {
User user = srv.config.dao.userDAO.findByUUID(uuid);
if (user == null) return null;
return new Entry(user.getUuid(), user.getUsername(), user.getAccessToken(), user.getServerID());
}
@Override
protected boolean updateAuth(UUID uuid, String username, String accessToken) {
User user = srv.config.dao.userDAO.findByUUID(uuid);
user.setAccessToken(accessToken);
srv.config.dao.userDAO.update(user);
return true;
}
@Override
protected boolean updateServerID(UUID uuid, String serverID) {
User user = srv.config.dao.userDAO.findByUUID(uuid);
user.setServerID(serverID);
srv.config.dao.userDAO.update(user);
return true;
}
@Override
public void close() {
}
}

View file

@ -1,90 +0,0 @@
package pro.gravit.launchserver.auth.handler;
import pro.gravit.launcher.HTTPRequest;
import pro.gravit.launcher.Launcher;
import java.io.IOException;
import java.net.URL;
import java.util.UUID;
public class JsonAuthHandler extends CachedAuthHandler {
public URL getUrl;
public URL updateAuthUrl;
public URL updateServerIdUrl;
public String apiKey;
@Override
protected Entry fetchEntry(String username) throws IOException {
return Launcher.gsonManager.gson.fromJson(HTTPRequest.jsonRequest(Launcher.gsonManager.gson.toJsonTree(new EntryRequestByUsername(username, apiKey)), getUrl), Entry.class);
}
@Override
protected Entry fetchEntry(UUID uuid) throws IOException {
return Launcher.gsonManager.gson.fromJson(HTTPRequest.jsonRequest(Launcher.gsonManager.gson.toJsonTree(new EntryRequestByUUID(uuid, apiKey)), getUrl), Entry.class);
}
@Override
protected boolean updateAuth(UUID uuid, String username, String accessToken) throws IOException {
return Launcher.gsonManager.gson.fromJson(HTTPRequest.jsonRequest(Launcher.gsonManager.gson.toJsonTree(new UpdateAuthRequest(uuid, username, accessToken, apiKey)), updateAuthUrl), SuccessResponse.class).success;
}
@Override
protected boolean updateServerID(UUID uuid, String serverID) throws IOException {
return Launcher.gsonManager.gson.fromJson(HTTPRequest.jsonRequest(Launcher.gsonManager.gson.toJsonTree(new UpdateServerIDRequest(uuid, serverID, apiKey)), updateServerIdUrl), SuccessResponse.class).success;
}
@Override
public void close() {
}
public static class EntryRequestByUsername {
public final String username;
public final String apiKey;
public EntryRequestByUsername(String username, String apiKey) {
this.username = username;
this.apiKey = apiKey;
}
}
public static class EntryRequestByUUID {
public final UUID uuid;
public final String apiKey;
public EntryRequestByUUID(UUID uuid, String apiKey) {
this.uuid = uuid;
this.apiKey = apiKey;
}
}
public static class UpdateAuthRequest {
public final UUID uuid;
public final String username;
public final String accessToken;
public final String apiKey;
public UpdateAuthRequest(UUID uuid, String username, String accessToken, String apiKey) {
this.uuid = uuid;
this.username = username;
this.accessToken = accessToken;
this.apiKey = apiKey;
}
}
public static class UpdateServerIDRequest {
public final UUID uuid;
public final String serverID;
public final String apiKey;
public UpdateServerIDRequest(UUID uuid, String serverID, String apiKey) {
this.uuid = uuid;
this.serverID = serverID;
this.apiKey = apiKey;
}
}
public static class SuccessResponse {
public boolean success;
}
}

View file

@ -1,54 +0,0 @@
package pro.gravit.launchserver.auth.handler;
import pro.gravit.utils.helper.IOHelper;
import pro.gravit.utils.helper.VerifyHelper;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.UUID;
public final class MemoryAuthHandler extends CachedAuthHandler {
private static String toUsername(UUID uuid) {
byte[] bytes = ByteBuffer.allocate(16).
putLong(uuid.getMostSignificantBits()).
putLong(uuid.getLeastSignificantBits()).array();
// Find username end
int length = 0;
while (length < bytes.length && bytes[length] != 0)
length++;
// Decode and verify
return VerifyHelper.verifyUsername(new String(bytes, 0, length, IOHelper.ASCII_CHARSET));
}
private static UUID toUUID(String username) {
ByteBuffer buffer = ByteBuffer.wrap(Arrays.copyOf(IOHelper.encodeASCII(username), 16));
return new UUID(buffer.getLong(), buffer.getLong()); // MOST, LEAST
}
@Override
public void close() {
// Do nothing
}
@Override
protected Entry fetchEntry(String username) {
return new Entry(toUUID(username), username, null, null);
}
@Override
protected Entry fetchEntry(UUID uuid) {
return new Entry(uuid, toUsername(uuid), null, null);
}
@Override
protected boolean updateAuth(UUID uuid, String username, String accessToken) {
return true; // Do nothing
}
@Override
protected boolean updateServerID(UUID uuid, String serverID) {
return true; // Do nothing
}
}

View file

@ -1,108 +0,0 @@
package pro.gravit.launchserver.auth.handler;
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.auth.MySQLSourceConfig;
import pro.gravit.utils.helper.LogHelper;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.UUID;
public final class MySQLAuthHandler extends CachedAuthHandler {
private MySQLSourceConfig mySQLHolder;
private String uuidColumn;
private String usernameColumn;
private String accessTokenColumn;
private String serverIDColumn;
private String table;
// Prepared SQL queries
private transient String queryByUUIDSQL;
private transient String queryByUsernameSQL;
private transient String updateAuthSQL;
private transient String updateServerIDSQL;
@Override
public void init(LaunchServer srv) {
super.init(srv);
//Verify
if (mySQLHolder == null) LogHelper.error("[Verify][AuthHandler] mySQLHolder cannot be null");
if (uuidColumn == null) LogHelper.error("[Verify][AuthHandler] uuidColumn cannot be null");
if (usernameColumn == null) LogHelper.error("[Verify][AuthHandler] usernameColumn cannot be null");
if (accessTokenColumn == null) LogHelper.error("[Verify][AuthHandler] accessTokenColumn cannot be null");
if (serverIDColumn == null) LogHelper.error("[Verify][AuthHandler] serverIDColumn cannot be null");
if (table == null) LogHelper.error("[Verify][AuthHandler] table cannot be null");
// Prepare SQL queries
queryByUUIDSQL = String.format("SELECT %s, %s, %s, %s FROM %s WHERE %s=? LIMIT 1",
uuidColumn, usernameColumn, accessTokenColumn, serverIDColumn, table, uuidColumn);
queryByUsernameSQL = String.format("SELECT %s, %s, %s, %s FROM %s WHERE %s=? LIMIT 1",
uuidColumn, usernameColumn, accessTokenColumn, serverIDColumn, table, usernameColumn);
updateAuthSQL = String.format("UPDATE %s SET %s=?, %s=?, %s=NULL WHERE %s=? LIMIT 1",
table, usernameColumn, accessTokenColumn, serverIDColumn, uuidColumn);
updateServerIDSQL = String.format("UPDATE %s SET %s=? WHERE %s=? LIMIT 1",
table, serverIDColumn, uuidColumn);
}
@Override
public void close() {
mySQLHolder.close();
}
private Entry constructEntry(ResultSet set) throws SQLException {
return set.next() ? new Entry(UUID.fromString(set.getString(uuidColumn)), set.getString(usernameColumn),
set.getString(accessTokenColumn), set.getString(serverIDColumn)) : null;
}
@Override
protected Entry fetchEntry(String username) throws IOException {
return query(queryByUsernameSQL, username);
}
@Override
protected Entry fetchEntry(UUID uuid) throws IOException {
return query(queryByUUIDSQL, uuid.toString());
}
private Entry query(String sql, String value) throws IOException {
try (Connection c = mySQLHolder.getConnection()) {
PreparedStatement s = c.prepareStatement(sql);
s.setString(1, value);
s.setQueryTimeout(MySQLSourceConfig.TIMEOUT);
try (ResultSet set = s.executeQuery()) {
return constructEntry(set);
}
} catch (SQLException e) {
throw new IOException(e);
}
}
@Override
protected boolean updateAuth(UUID uuid, String username, String accessToken) throws IOException {
try (Connection c = mySQLHolder.getConnection()) {
PreparedStatement s = c.prepareStatement(updateAuthSQL);
s.setString(1, username); // Username case
s.setString(2, accessToken);
s.setString(3, uuid.toString());
s.setQueryTimeout(MySQLSourceConfig.TIMEOUT);
return s.executeUpdate() > 0;
} catch (SQLException e) {
throw new IOException(e);
}
}
@Override
protected boolean updateServerID(UUID uuid, String serverID) throws IOException {
try (Connection c = mySQLHolder.getConnection()) {
PreparedStatement s = c.prepareStatement(updateServerIDSQL);
s.setString(1, serverID);
s.setString(2, uuid.toString());
s.setQueryTimeout(MySQLSourceConfig.TIMEOUT);
return s.executeUpdate() > 0;
} catch (SQLException e) {
throw new IOException(e);
}
}
}

View file

@ -1,53 +0,0 @@
package pro.gravit.launchserver.auth.handler;
import pro.gravit.launchserver.auth.provider.AuthProviderResult;
import pro.gravit.utils.helper.VerifyHelper;
import java.io.IOException;
import java.util.Objects;
import java.util.UUID;
public final class NullAuthHandler extends AuthHandler {
private volatile AuthHandler handler;
@Override
public UUID auth(AuthProviderResult authResult) throws IOException {
return getHandler().auth(authResult);
}
@Override
public UUID checkServer(String username, String serverID) throws IOException {
return getHandler().checkServer(username, serverID);
}
@Override
public void close() throws IOException {
AuthHandler handler = this.handler;
if (handler != null)
handler.close();
}
private AuthHandler getHandler() {
return VerifyHelper.verify(handler, Objects::nonNull, "Backend auth handler wasn't set");
}
@Override
public boolean joinServer(String username, String accessToken, String serverID) throws IOException {
return getHandler().joinServer(username, accessToken, serverID);
}
public void setBackend(AuthHandler handler) {
this.handler = handler;
}
@Override
public UUID usernameToUUID(String username) throws IOException {
return getHandler().usernameToUUID(username);
}
@Override
public String uuidToUsername(UUID uuid) throws IOException {
return getHandler().uuidToUsername(uuid);
}
}

View file

@ -1,118 +0,0 @@
package pro.gravit.launchserver.auth.handler;
import org.postgresql.util.PGobject;
import pro.gravit.launchserver.auth.PostgreSQLSourceConfig;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.UUID;
public final class PostgreSQLAuthHandler extends CachedAuthHandler {
private PostgreSQLSourceConfig postgreSQLHolder;
private String uuidColumn;
private String usernameColumn;
private String accessTokenColumn;
private String serverIDColumn;
private String queryByUUIDSQL;
private String queryByUsernameSQL;
private String updateAuthSQL;
private String updateServerIDSQL;
@Override
public void close() {
postgreSQLHolder.close();
}
private Entry constructEntry(ResultSet set) throws SQLException {
return set.next() ? new Entry(UUID.fromString(set.getString(uuidColumn)),
set.getString(usernameColumn), set.getString(accessTokenColumn), set.getString(serverIDColumn)) : null;
}
@Override
protected Entry fetchEntry(String username) throws IOException {
return query(queryByUsernameSQL, username);
}
@Override
protected Entry fetchEntry(UUID uuid) throws IOException {
return query(queryByUUIDSQL, uuid);
}
@Override
protected boolean updateAuth(UUID uuid, String username, String accessToken) throws IOException {
try (Connection c = postgreSQLHolder.getConnection();
PreparedStatement s = c.prepareStatement(updateAuthSQL)) {
s.setString(1, username); // Username case
s.setString(2, accessToken);
PGobject uuidObject = new PGobject();
uuidObject.setType("uuid");
uuidObject.setValue(uuid.toString());
s.setObject(3, uuidObject);
// Execute update
s.setQueryTimeout(PostgreSQLSourceConfig.TIMEOUT);
return s.executeUpdate() > 0;
} catch (SQLException e) {
throw new IOException(e);
}
}
@Override
protected boolean updateServerID(UUID uuid, String serverID) throws IOException {
try (Connection c = postgreSQLHolder.getConnection();
PreparedStatement s = c.prepareStatement(updateServerIDSQL)) {
s.setString(1, serverID);
PGobject uuidObject = new PGobject();
uuidObject.setType("uuid");
uuidObject.setValue(uuid.toString());
s.setObject(2, uuidObject);
// Execute update
s.setQueryTimeout(PostgreSQLSourceConfig.TIMEOUT);
return s.executeUpdate() > 0;
} catch (SQLException e) {
throw new IOException(e);
}
}
private Entry query(String sql, String value) throws IOException {
try (Connection c = postgreSQLHolder.getConnection();
PreparedStatement s = c.prepareStatement(sql)) {
s.setString(1, value);
// Execute query
s.setQueryTimeout(PostgreSQLSourceConfig.TIMEOUT);
try (ResultSet set = s.executeQuery()) {
return constructEntry(set);
}
} catch (SQLException e) {
throw new IOException(e);
}
}
private Entry query(String sql, UUID value) throws IOException {
try (Connection c = postgreSQLHolder.getConnection();
PreparedStatement s = c.prepareStatement(sql)) {
PGobject uuidObject = new PGobject();
uuidObject.setType("uuid");
uuidObject.setValue(value.toString());
s.setObject(1, uuidObject);
// Execute query
s.setQueryTimeout(PostgreSQLSourceConfig.TIMEOUT);
try (ResultSet set = s.executeQuery()) {
return constructEntry(set);
}
} catch (SQLException e) {
throw new IOException(e);
}
}
}

View file

@ -1,89 +0,0 @@
package pro.gravit.launchserver.auth.handler;
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.utils.helper.CommonHelper;
import pro.gravit.utils.helper.IOHelper;
import pro.gravit.utils.helper.LogHelper;
import java.io.IOException;
import java.net.URL;
import java.util.UUID;
public final class RequestAuthHandler extends CachedAuthHandler {
private final String splitSymbol = ":";
private final String goodResponse = "OK";
private String usernameFetch;
private String uuidFetch;
private String updateAuth;
private String updateServerID;
@Override
public void init(LaunchServer srv) {
super.init(srv);
if (usernameFetch == null)
LogHelper.error("[Verify][AuthHandler] usernameFetch cannot be null");
if (uuidFetch == null)
LogHelper.error("[Verify][AuthHandler] uuidFetch cannot be null");
if (updateAuth == null)
LogHelper.error("[Verify][AuthHandler] updateAuth cannot be null");
if (updateServerID == null)
LogHelper.error("[Verify][AuthHandler] updateServerID cannot be null");
}
@Override
protected Entry fetchEntry(UUID uuid) throws IOException {
String response = IOHelper.request(new URL(CommonHelper.replace(uuidFetch, "uuid", IOHelper.urlEncode(uuid.toString()))));
String[] parts = response.split(splitSymbol);
String username = parts[0];
String accessToken = parts[1];
String serverID = parts[2];
if (LogHelper.isDebugEnabled()) {
LogHelper.debug("[AuthHandler] Got username: " + username);
LogHelper.debug("[AuthHandler] Got accessToken: " + accessToken);
LogHelper.debug("[AuthHandler] Got serverID: " + serverID);
LogHelper.debug("[AuthHandler] Got UUID: " + uuid);
}
return new Entry(uuid, username, accessToken, serverID);
}
@Override
protected Entry fetchEntry(String username) throws IOException {
String response = IOHelper.request(new URL(CommonHelper.replace(usernameFetch, "user", IOHelper.urlEncode(username))));
String[] parts = response.split(splitSymbol);
UUID uuid = UUID.fromString(parts[0]);
String accessToken = parts[1];
String serverID = parts[2];
if (LogHelper.isDebugEnabled()) {
LogHelper.debug("[AuthHandler] Got username: " + username);
LogHelper.debug("[AuthHandler] Got accessToken: " + accessToken);
LogHelper.debug("[AuthHandler] Got serverID: " + serverID);
LogHelper.debug("[AuthHandler] Got UUID: " + uuid);
}
return new Entry(uuid, username, accessToken, serverID);
}
@Override
protected boolean updateAuth(UUID uuid, String username, String accessToken) throws IOException {
String response = IOHelper.request(new URL(CommonHelper.replace(updateAuth, "user", IOHelper.urlEncode(username), "uuid", IOHelper.urlEncode(uuid.toString()), "token", IOHelper.urlEncode(accessToken))));
if (LogHelper.isDebugEnabled()) {
LogHelper.debug("[AuthHandler] Set accessToken: " + accessToken);
LogHelper.debug("[AuthHandler] Set UUID: " + uuid);
LogHelper.debug("[AuthHandler] For this username: " + username);
}
return goodResponse.equals(response);
}
@Override
protected boolean updateServerID(UUID uuid, String serverID) throws IOException {
String response = IOHelper.request(new URL(CommonHelper.replace(updateAuth, "serverid", IOHelper.urlEncode(serverID), "uuid", IOHelper.urlEncode(uuid.toString()))));
if (LogHelper.isDebugEnabled()) {
LogHelper.debug("[AuthHandler] Set serverID: " + serverID);
LogHelper.debug("[AuthHandler] For this UUID: " + uuid);
}
return goodResponse.equals(response);
}
@Override
public void close() {
}
}

View file

@ -0,0 +1,31 @@
package pro.gravit.launchserver.auth.mix;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.auth.core.AuthCoreProvider;
import pro.gravit.utils.ProviderMap;
public abstract class MixProvider implements AutoCloseable{
public static final ProviderMap<MixProvider> providers = new ProviderMap<>("MixProvider");
private static final Logger logger = LogManager.getLogger();
private static boolean registredProviders = false;
public static void registerProviders() {
if (!registredProviders) {
providers.register("uploadAsset", UploadAssetMixProvider.class);
registredProviders = true;
}
}
public abstract void init(LaunchServer server, AuthCoreProvider core);
@SuppressWarnings("unchecked")
public <T> T isSupport(Class<T> clazz) {
if (clazz.isAssignableFrom(getClass())) return (T) this;
return null;
}
@Override
public abstract void close();
}

View file

@ -0,0 +1,34 @@
package pro.gravit.launchserver.auth.mix;
import pro.gravit.launcher.base.events.request.AssetUploadInfoRequestEvent;
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.auth.core.AuthCoreProvider;
import pro.gravit.launchserver.auth.core.User;
import pro.gravit.launchserver.auth.core.interfaces.provider.AuthSupportAssetUpload;
import java.util.Map;
public class UploadAssetMixProvider extends MixProvider implements AuthSupportAssetUpload {
public Map<String, String> urls;
public AssetUploadInfoRequestEvent.SlimSupportConf slimSupportConf;
@Override
public String getAssetUploadUrl(String name, User user) {
return urls.get(name);
}
@Override
public AssetUploadInfoRequestEvent getAssetUploadInfo(User user) {
return new AssetUploadInfoRequestEvent(urls.keySet(), slimSupportConf);
}
@Override
public void init(LaunchServer server, AuthCoreProvider core) {
}
@Override
public void close() {
}
}

View file

@ -0,0 +1,13 @@
package pro.gravit.launchserver.auth.password;
public class AcceptPasswordVerifier extends PasswordVerifier {
@Override
public boolean check(String encryptedPassword, String password) {
return true;
}
@Override
public String encrypt(String password) {
return "";
}
}

View file

@ -0,0 +1,18 @@
package pro.gravit.launchserver.auth.password;
import org.bouncycastle.crypto.generators.OpenBSDBCrypt;
import pro.gravit.utils.helper.SecurityHelper;
public class BCryptPasswordVerifier extends PasswordVerifier {
public int cost = 10;
@Override
public boolean check(String encryptedPassword, String password) {
return OpenBSDBCrypt.checkPassword(encryptedPassword, password.toCharArray());
}
@Override
public String encrypt(String password) {
return OpenBSDBCrypt.generate(password.toCharArray(), SecurityHelper.randomBytes(16), cost);
}
}

View file

@ -0,0 +1,41 @@
package pro.gravit.launchserver.auth.password;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import pro.gravit.utils.helper.IOHelper;
import pro.gravit.utils.helper.SecurityHelper;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
public class DigestPasswordVerifier extends PasswordVerifier {
private transient final Logger logger = LogManager.getLogger();
public String algo;
private byte[] digest(String text) throws NoSuchAlgorithmException {
MessageDigest digest = MessageDigest.getInstance(algo);
return digest.digest(IOHelper.encode(text));
}
@Override
public boolean check(String encryptedPassword, String password) {
try {
byte[] bytes = SecurityHelper.fromHex(encryptedPassword);
return Arrays.equals(bytes, digest(password));
} catch (NoSuchAlgorithmException e) {
logger.error("Digest algorithm {} not supported", algo);
return false;
}
}
@Override
public String encrypt(String password) {
try {
return SecurityHelper.toHex(digest(password));
} catch (NoSuchAlgorithmException e) {
logger.error("Digest algorithm {} not supported", algo);
return null;
}
}
}

View file

@ -0,0 +1,43 @@
package pro.gravit.launchserver.auth.password;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import pro.gravit.utils.helper.IOHelper;
import pro.gravit.utils.helper.SecurityHelper;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
public class DoubleDigestPasswordVerifier extends PasswordVerifier {
private transient final Logger logger = LogManager.getLogger();
public String algo;
public boolean toHexMode;
private byte[] digest(String text) throws NoSuchAlgorithmException {
MessageDigest digest = MessageDigest.getInstance(algo);
byte[] firstDigest = digest.digest(IOHelper.encode(text));
return toHexMode ? digest.digest(IOHelper.encode(SecurityHelper.toHex(firstDigest))) : digest.digest(firstDigest);
}
@Override
public boolean check(String encryptedPassword, String password) {
try {
byte[] bytes = SecurityHelper.fromHex(encryptedPassword);
return Arrays.equals(bytes, digest(password));
} catch (NoSuchAlgorithmException e) {
logger.error("Digest algorithm {} not supported", algo);
return false;
}
}
@Override
public String encrypt(String password) {
try {
return SecurityHelper.toHex(digest(password));
} catch (NoSuchAlgorithmException e) {
logger.error("Digest algorithm {} not supported", algo);
return null;
}
}
}

View file

@ -0,0 +1,81 @@
package pro.gravit.launchserver.auth.password;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import pro.gravit.launcher.base.Launcher;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
public class JsonPasswordVerifier extends PasswordVerifier {
private static final Logger logger = LogManager.getLogger();
private transient final HttpClient client = HttpClient.newBuilder().build();
public String url;
public String bearerToken;
public static <T, R> R jsonRequest(T request, String url, String bearerToken, Class<R> clazz, HttpClient client) {
HttpRequest.BodyPublisher publisher;
if (request != null) {
publisher = HttpRequest.BodyPublishers.ofString(Launcher.gsonManager.gson.toJson(request));
} else {
publisher = HttpRequest.BodyPublishers.noBody();
}
try {
HttpRequest.Builder request1 = HttpRequest.newBuilder()
.method("POST", publisher)
.uri(new URI(url))
.header("Content-Type", "application/json; charset=UTF-8")
.header("Accept", "application/json")
.timeout(Duration.ofMillis(10000));
if (bearerToken != null) {
request1.header("Authorization", "Bearer ".concat(bearerToken));
}
HttpResponse<InputStream> response = client.send(request1.build(), HttpResponse.BodyHandlers.ofInputStream());
int statusCode = response.statusCode();
if (200 > statusCode || statusCode > 300) {
if (statusCode >= 500) {
logger.error("JsonCoreProvider: {} return {}", url, statusCode);
} else if (statusCode >= 300 && statusCode <= 400) {
logger.error("JsonCoreProvider: {} return {}, try redirect to {}. Redirects not supported!", url, statusCode, response.headers().firstValue("Location").orElse("Unknown"));
} else if (statusCode == 403 || statusCode == 401) {
logger.error("JsonCoreProvider: {} return {}. Please set 'bearerToken'!", url, statusCode);
}
return null;
}
try (Reader reader = new InputStreamReader(response.body())) {
return Launcher.gsonManager.gson.fromJson(reader, clazz);
}
} catch (Exception e) {
return null;
}
}
@Override
public boolean check(String encryptedPassword, String password) {
JsonPasswordResponse response = jsonRequest(new JsonPasswordRequest(encryptedPassword, password), url, bearerToken, JsonPasswordResponse.class, client);
if (response != null) {
return response.success;
}
return false;
}
public static class JsonPasswordRequest {
public String encryptedPassword;
public String password;
public JsonPasswordRequest(String encryptedPassword, String password) {
this.encryptedPassword = encryptedPassword;
this.password = password;
}
}
public static class JsonPasswordResponse {
public boolean success;
}
}

View file

@ -0,0 +1,27 @@
package pro.gravit.launchserver.auth.password;
import pro.gravit.utils.ProviderMap;
public abstract class PasswordVerifier {
public static final ProviderMap<PasswordVerifier> providers = new ProviderMap<>("PasswordVerifier");
private static boolean registeredProviders = false;
public static void registerProviders() {
if (!registeredProviders) {
providers.register("plain", PlainPasswordVerifier.class);
providers.register("digest", DigestPasswordVerifier.class);
providers.register("doubleDigest", DoubleDigestPasswordVerifier.class);
providers.register("json", JsonPasswordVerifier.class);
providers.register("bcrypt", BCryptPasswordVerifier.class);
providers.register("accept", AcceptPasswordVerifier.class);
providers.register("reject", RejectPasswordVerifier.class);
registeredProviders = true;
}
}
public abstract boolean check(String encryptedPassword, String password);
public String encrypt(String password) {
throw new UnsupportedOperationException();
}
}

View file

@ -0,0 +1,13 @@
package pro.gravit.launchserver.auth.password;
public class PlainPasswordVerifier extends PasswordVerifier {
@Override
public boolean check(String encryptedPassword, String password) {
return encryptedPassword.equals(password);
}
@Override
public String encrypt(String password) {
return super.encrypt(password);
}
}

View file

@ -0,0 +1,8 @@
package pro.gravit.launchserver.auth.password;
public class RejectPasswordVerifier extends PasswordVerifier {
@Override
public boolean check(String encryptedPassword, String password) {
return false;
}
}

View file

@ -0,0 +1,108 @@
package pro.gravit.launchserver.auth.profiles;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import pro.gravit.launcher.base.Launcher;
import pro.gravit.launcher.base.profiles.ClientProfile;
import pro.gravit.utils.helper.IOHelper;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.*;
public class LocalProfileProvider extends ProfileProvider {
public String profilesDir = "profiles";
private transient volatile Map<Path, ClientProfile> profilesMap;
private transient volatile Set<ClientProfile> profilesList; // Cache
@Override
public void sync() throws IOException {
Path profilesDirPath = Path.of(profilesDir);
if (!IOHelper.isDir(profilesDirPath))
Files.createDirectory(profilesDirPath);
Map<Path, ClientProfile> newProfiles = new HashMap<>();
IOHelper.walk(profilesDirPath, new ProfilesFileVisitor(newProfiles), false);
Set<ClientProfile> newProfilesList = new HashSet<>(newProfiles.values());
profilesMap = newProfiles;
profilesList = newProfilesList;
}
@Override
public Set<ClientProfile> getProfiles() {
return profilesList;
}
@Override
public void addProfile(ClientProfile profile) throws IOException {
Path profilesDirPath = Path.of(profilesDir);
ClientProfile oldProfile;
Path target = null;
for(var e : profilesMap.entrySet()) {
if(e.getValue().getUUID().equals(profile.getUUID())) {
target = e.getKey();
}
}
if(target == null) {
target = profilesDirPath.resolve(profile.getTitle()+".json");
oldProfile = profilesMap.get(target);
if(oldProfile != null && !oldProfile.getUUID().equals(profile.getUUID())) {
throw new FileAlreadyExistsException(target.toString());
}
}
try (BufferedWriter writer = IOHelper.newWriter(target)) {
Launcher.gsonManager.configGson.toJson(profile, writer);
}
addProfile(target, profile);
}
@Override
public void deleteProfile(ClientProfile profile) throws IOException {
for(var e : profilesMap.entrySet()) {
if(e.getValue().getUUID().equals(profile.getUUID())) {
Files.deleteIfExists(e.getKey());
profilesMap.remove(e.getKey());
profilesList.remove(e.getValue());
break;
}
}
}
private void addProfile(Path path, ClientProfile profile) {
for(var e : profilesMap.entrySet()) {
if(e.getValue().getUUID().equals(profile.getUUID())) {
profilesMap.remove(e.getKey());
profilesList.remove(e.getValue());
break;
}
}
profilesMap.put(path, profile);
profilesList.add(profile);
}
private static final class ProfilesFileVisitor extends SimpleFileVisitor<Path> {
private final Map<Path, ClientProfile> result;
private final Logger logger = LogManager.getLogger();
private ProfilesFileVisitor(Map<Path, ClientProfile> result) {
this.result = result;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
logger.info("Syncing '{}' profile", IOHelper.getFileName(file));
// Read profile
ClientProfile profile;
try (BufferedReader reader = IOHelper.newReader(file)) {
profile = Launcher.gsonManager.gson.fromJson(reader, ClientProfile.class);
}
profile.verify();
// Add SIGNED profile to result list
result.put(file.toAbsolutePath(), profile);
return super.visitFile(file, attrs);
}
}
}

View file

@ -0,0 +1,76 @@
package pro.gravit.launchserver.auth.profiles;
import pro.gravit.launcher.base.profiles.ClientProfile;
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.auth.protect.interfaces.ProfilesProtectHandler;
import pro.gravit.launchserver.socket.Client;
import pro.gravit.utils.ProviderMap;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.UUID;
public abstract class ProfileProvider {
public static final ProviderMap<ProfileProvider> providers = new ProviderMap<>("ProfileProvider");
private static boolean registredProviders = false;
protected transient LaunchServer server;
public static void registerProviders() {
if (!registredProviders) {
providers.register("local", LocalProfileProvider.class);
registredProviders = true;
}
}
public void init(LaunchServer server) {
this.server = server;
}
public abstract void sync() throws IOException;
public abstract Set<ClientProfile> getProfiles();
public abstract void addProfile(ClientProfile profile) throws IOException;
public abstract void deleteProfile(ClientProfile profile) throws IOException;
public void close() {
}
public ClientProfile getProfile(UUID uuid) {
for(var e : getProfiles()) {
if(e.getUUID().equals(uuid)) {
return e;
}
}
return null;
}
public ClientProfile getProfile(String title) {
for(var e : getProfiles()) {
if(e.getTitle().equals(title)) {
return e;
}
}
return null;
}
public List<ClientProfile> getProfiles(Client client) {
List<ClientProfile> profileList;
Set<ClientProfile> serverProfiles = getProfiles();
if (server.config.protectHandler instanceof ProfilesProtectHandler protectHandler) {
profileList = new ArrayList<>(4);
for (ClientProfile profile : serverProfiles) {
if (protectHandler.canGetProfile(profile, client)) {
profileList.add(profile);
}
}
} else {
profileList = List.copyOf(serverProfiles);
}
return profileList;
}
}

View file

@ -1,39 +1,34 @@
package pro.gravit.launchserver.auth.protect; package pro.gravit.launchserver.auth.protect;
import pro.gravit.launcher.events.request.GetSecureLevelInfoRequestEvent; import io.jsonwebtoken.JwtParser;
import pro.gravit.launcher.events.request.HardwareReportRequestEvent; import io.jsonwebtoken.Jwts;
import pro.gravit.launcher.events.request.VerifySecureLevelKeyRequestEvent; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import pro.gravit.launcher.base.events.request.GetSecureLevelInfoRequestEvent;
import pro.gravit.launcher.base.events.request.HardwareReportRequestEvent;
import pro.gravit.launcher.base.events.request.VerifySecureLevelKeyRequestEvent;
import pro.gravit.launchserver.LaunchServer; import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.Reconfigurable; import pro.gravit.launchserver.auth.AuthProviderPair;
import pro.gravit.launchserver.auth.protect.hwid.HWIDException; import pro.gravit.launchserver.auth.core.interfaces.UserHardware;
import pro.gravit.launchserver.auth.protect.hwid.HWIDProvider; import pro.gravit.launchserver.auth.core.interfaces.provider.AuthSupportHardware;
import pro.gravit.launchserver.auth.protect.interfaces.HardwareProtectHandler; import pro.gravit.launchserver.auth.protect.interfaces.HardwareProtectHandler;
import pro.gravit.launchserver.auth.protect.interfaces.JoinServerProtectHandler; import pro.gravit.launchserver.auth.protect.interfaces.JoinServerProtectHandler;
import pro.gravit.launchserver.auth.protect.interfaces.SecureProtectHandler; import pro.gravit.launchserver.auth.protect.interfaces.SecureProtectHandler;
import pro.gravit.launchserver.socket.Client; import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.response.auth.AuthResponse; import pro.gravit.launchserver.socket.response.auth.RestoreResponse;
import pro.gravit.launchserver.socket.response.secure.HardwareReportResponse; import pro.gravit.launchserver.socket.response.secure.HardwareReportResponse;
import pro.gravit.utils.command.Command;
import pro.gravit.utils.helper.LogHelper;
import java.util.HashMap; import java.util.Base64;
import java.util.Map; import java.util.Date;
import java.util.UUID;
public class AdvancedProtectHandler extends StdProtectHandler implements SecureProtectHandler, HardwareProtectHandler, JoinServerProtectHandler, Reconfigurable { import static java.util.concurrent.TimeUnit.SECONDS;
public class AdvancedProtectHandler extends StdProtectHandler implements SecureProtectHandler, HardwareProtectHandler, JoinServerProtectHandler {
private transient final Logger logger = LogManager.getLogger();
public boolean enableHardwareFeature; public boolean enableHardwareFeature;
public HWIDProvider provider;
private transient LaunchServer server; private transient LaunchServer server;
@Override
public boolean allowGetAccessToken(AuthResponse.AuthContext context) {
return (context.authType == AuthResponse.ConnectTypes.CLIENT) && context.client.checkSign;
}
@Override
public void checkLaunchServerLicense() {
}
@Override @Override
public GetSecureLevelInfoRequestEvent onGetSecureLevelInfo(GetSecureLevelInfoRequestEvent event) { public GetSecureLevelInfoRequestEvent onGetSecureLevelInfo(GetSecureLevelInfoRequestEvent event) {
return event; return event;
@ -50,66 +45,145 @@ public void onHardwareReport(HardwareReportResponse response, Client client) {
response.sendResult(new HardwareReportRequestEvent()); response.sendResult(new HardwareReportRequestEvent());
return; return;
} }
try {
if (!client.isAuth || client.trustLevel == null || client.trustLevel.publicKey == null) { if (!client.isAuth || client.trustLevel == null || client.trustLevel.publicKey == null) {
response.sendError("Access denied"); response.sendError("Access denied");
return; return;
} }
provider.normalizeHardwareInfo(response.hardware); if(client.trustLevel.hardwareInfo != null) {
LogHelper.debug("[HardwareInfo] HardwareInfo received"); response.sendResult(new HardwareReportRequestEvent(createHardwareToken(client.username, client.trustLevel.hardwareInfo), SECONDS.toMillis(server.config.netty.security.hardwareTokenExpire)));
boolean needCreate = !provider.addPublicKeyToHardwareInfo(response.hardware, client.trustLevel.publicKey, client); return;
LogHelper.debug("[HardwareInfo] HardwareInfo needCreate: %s", needCreate ? "true" : "false"); }
if (needCreate) logger.debug("HardwareInfo received");
provider.createHardwareInfo(response.hardware, client.trustLevel.publicKey, client); {
client.trustLevel.hardwareInfo = response.hardware; var authSupportHardware = client.auth.isSupport(AuthSupportHardware.class);
} catch (HWIDException e) { if (authSupportHardware != null) {
throw new SecurityException(e.getMessage()); UserHardware hardware = authSupportHardware.getHardwareInfoByData(response.hardware);
if (hardware == null) {
hardware = authSupportHardware.createHardwareInfo(response.hardware, client.trustLevel.publicKey);
} else {
authSupportHardware.addPublicKeyToHardwareInfo(hardware, client.trustLevel.publicKey);
}
authSupportHardware.connectUserAndHardware(client.sessionObject, hardware);
if (hardware.isBanned()) {
throw new SecurityException("Your hardware banned");
}
client.trustLevel.hardwareInfo = hardware;
response.sendResult(new HardwareReportRequestEvent(createHardwareToken(client.username, hardware), SECONDS.toMillis(server.config.netty.security.hardwareTokenExpire)));
} else {
logger.error("AuthCoreProvider not supported hardware");
response.sendError("AuthCoreProvider not supported hardware");
}
} }
response.sendResult(new HardwareReportRequestEvent());
} }
@Override @Override
public VerifySecureLevelKeyRequestEvent onSuccessVerify(Client client) { public VerifySecureLevelKeyRequestEvent onSuccessVerify(Client client) {
if (enableHardwareFeature) { if (enableHardwareFeature) {
if (provider == null) { var authSupportHardware = client.auth.isSupport(AuthSupportHardware.class);
LogHelper.warning("HWIDProvider null. HardwareInfo not checked!"); if (authSupportHardware != null) {
UserHardware hardware = authSupportHardware.getHardwareInfoByPublicKey(client.trustLevel.publicKey);
if (hardware == null) //HWID not found?
return new VerifySecureLevelKeyRequestEvent(true, false, createPublicKeyToken(client.username, client.trustLevel.publicKey), SECONDS.toMillis(server.config.netty.security.publicKeyTokenExpire));
if (hardware.isBanned()) {
throw new SecurityException("Your hardware banned");
}
client.trustLevel.hardwareInfo = hardware;
authSupportHardware.connectUserAndHardware(client.sessionObject, hardware);
return new VerifySecureLevelKeyRequestEvent(false, false, createPublicKeyToken(client.username, client.trustLevel.publicKey), SECONDS.toMillis(server.config.netty.security.publicKeyTokenExpire));
} else { } else {
try { logger.warn("AuthCoreProvider not supported hardware. HardwareInfo not checked!");
client.trustLevel.hardwareInfo = provider.findHardwareInfoByPublicKey(client.trustLevel.publicKey, client);
if (client.trustLevel.hardwareInfo == null) //HWID not found?
return new VerifySecureLevelKeyRequestEvent(true);
} catch (HWIDException e) {
throw new SecurityException(e.getMessage()); //Show banned message
} }
} }
return new VerifySecureLevelKeyRequestEvent(false); return new VerifySecureLevelKeyRequestEvent(false, false, createPublicKeyToken(client.username, client.trustLevel.publicKey), SECONDS.toMillis(server.config.netty.security.publicKeyTokenExpire));
}
return new VerifySecureLevelKeyRequestEvent();
} }
@Override @Override
public Map<String, Command> getCommands() { public boolean onJoinServer(String serverID, String username, UUID uuid, Client client) {
Map<String, Command> commands = new HashMap<>();
if (provider instanceof Reconfigurable) {
commands.putAll(((Reconfigurable) provider).getCommands());
}
return commands;
}
@Override
public boolean onJoinServer(String serverID, String username, Client client) {
return !enableHardwareFeature || (client.trustLevel != null && client.trustLevel.hardwareInfo != null); return !enableHardwareFeature || (client.trustLevel != null && client.trustLevel.hardwareInfo != null);
} }
@Override @Override
public void init(LaunchServer server) { public void init(LaunchServer server) {
if (provider != null) this.server = server;
provider.init(server); }
public String createHardwareToken(String username, UserHardware hardware) {
return Jwts.builder()
.setIssuer("LaunchServer")
.setSubject(username)
.setExpiration(new Date(System.currentTimeMillis() + SECONDS.toMillis(server.config.netty.security.hardwareTokenExpire)))
.claim("hardware", hardware.getId())
.signWith(server.keyAgreementManager.ecdsaPrivateKey)
.compact();
}
public String createPublicKeyToken(String username, byte[] publicKey) {
return Jwts.builder()
.setIssuer("LaunchServer")
.setSubject(username)
.setExpiration(new Date(System.currentTimeMillis() + SECONDS.toMillis(server.config.netty.security.publicKeyTokenExpire)))
.claim("publicKey", Base64.getEncoder().encodeToString(publicKey))
.signWith(server.keyAgreementManager.ecdsaPrivateKey)
.compact();
}
public static class HardwareInfoTokenVerifier implements RestoreResponse.ExtendedTokenProvider {
private transient final Logger logger = LogManager.getLogger();
private final JwtParser parser;
public HardwareInfoTokenVerifier(LaunchServer server) {
this.parser = Jwts.parser()
.requireIssuer("LaunchServer")
.verifyWith(server.keyAgreementManager.ecdsaPublicKey)
.build();
} }
@Override @Override
public void close() { public boolean accept(Client client, AuthProviderPair pair, String extendedToken) {
if (provider != null) try {
provider.close(); var parse = parser.parseClaimsJws(extendedToken);
String hardwareInfoId = parse.getBody().get("hardware", String.class);
if (hardwareInfoId == null) return false;
if (client.auth == null) return false;
var hardwareSupport = client.auth.core.isSupport(AuthSupportHardware.class);
if (hardwareSupport == null) return false;
UserHardware hardware = hardwareSupport.getHardwareInfoById(hardwareInfoId);
if (client.trustLevel == null) client.trustLevel = new Client.TrustLevel();
client.trustLevel.hardwareInfo = hardware;
return true;
} catch (Throwable e) {
logger.error("Hardware JWT error", e);
}
return false;
}
}
public static class PublicKeyTokenVerifier implements RestoreResponse.ExtendedTokenProvider {
private transient final Logger logger = LogManager.getLogger();
private final JwtParser parser;
public PublicKeyTokenVerifier(LaunchServer server) {
this.parser = Jwts.parser()
.requireIssuer("LaunchServer")
.verifyWith(server.keyAgreementManager.ecdsaPublicKey)
.build();
}
@Override
public boolean accept(Client client, AuthProviderPair pair, String extendedToken) {
try {
var parse = parser.parseClaimsJws(extendedToken);
String publicKey = parse.getBody().get("publicKey", String.class);
if (publicKey == null) return false;
if (client.trustLevel == null) client.trustLevel = new Client.TrustLevel();
client.trustLevel.publicKey = Base64.getDecoder().decode(publicKey);
return true;
} catch (Throwable e) {
logger.error("Public Key JWT error", e);
}
return false;
}
} }
} }

View file

@ -1,5 +1,6 @@
package pro.gravit.launchserver.auth.protect; package pro.gravit.launchserver.auth.protect;
import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.response.auth.AuthResponse; import pro.gravit.launchserver.socket.response.auth.AuthResponse;
public class NoProtectHandler extends ProtectHandler { public class NoProtectHandler extends ProtectHandler {
@ -10,7 +11,7 @@ public boolean allowGetAccessToken(AuthResponse.AuthContext context) {
} }
@Override @Override
public void checkLaunchServerLicense() { public boolean allowJoinServer(Client client) {
// None return true;
} }
} }

View file

@ -1,6 +1,7 @@
package pro.gravit.launchserver.auth.protect; package pro.gravit.launchserver.auth.protect;
import pro.gravit.launchserver.LaunchServer; import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.response.auth.AuthResponse; import pro.gravit.launchserver.socket.response.auth.AuthResponse;
import pro.gravit.utils.ProviderMap; import pro.gravit.utils.ProviderMap;
@ -19,8 +20,9 @@ public static void registerHandlers() {
} }
public abstract boolean allowGetAccessToken(AuthResponse.AuthContext context); public abstract boolean allowGetAccessToken(AuthResponse.AuthContext context);
public boolean allowJoinServer(Client client) {
public abstract void checkLaunchServerLicense(); //Выдает SecurityException при ошибке проверки лицензии return client.isAuth && client.type == AuthResponse.ConnectTypes.CLIENT;
}
public void init(LaunchServer server) { public void init(LaunchServer server) {

View file

@ -1,16 +1,17 @@
package pro.gravit.launchserver.auth.protect; package pro.gravit.launchserver.auth.protect;
import pro.gravit.launcher.profiles.ClientProfile; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import pro.gravit.launcher.base.profiles.ClientProfile;
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.auth.protect.interfaces.ProfilesProtectHandler; import pro.gravit.launchserver.auth.protect.interfaces.ProfilesProtectHandler;
import pro.gravit.launchserver.socket.Client; import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.response.auth.AuthResponse; import pro.gravit.launchserver.socket.response.auth.AuthResponse;
import java.util.ArrayList; import java.util.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class StdProtectHandler extends ProtectHandler implements ProfilesProtectHandler { public class StdProtectHandler extends ProtectHandler implements ProfilesProtectHandler {
private transient final Logger logger = LogManager.getLogger();
public Map<String, List<String>> profileWhitelist = new HashMap<>(); public Map<String, List<String>> profileWhitelist = new HashMap<>();
public List<String> allowUpdates = new ArrayList<>(); public List<String> allowUpdates = new ArrayList<>();
@ -20,18 +21,20 @@ public boolean allowGetAccessToken(AuthResponse.AuthContext context) {
} }
@Override @Override
public void checkLaunchServerLicense() { public void init(LaunchServer server) {
if (profileWhitelist != null && !profileWhitelist.isEmpty()) {
logger.warn("profileWhitelist deprecated. Please use permission 'launchserver.profile.PROFILE_UUID.show' and 'launchserver.profile.PROFILE_UUID.enter'");
}
} }
@Override @Override
public boolean canGetProfile(ClientProfile profile, Client client) { public boolean canGetProfile(ClientProfile profile, Client client) {
return canChangeProfile(profile, client); return (client.isAuth && !profile.isLimited()) || isWhitelisted("launchserver.profile.%s.show", profile, client);
} }
@Override @Override
public boolean canChangeProfile(ClientProfile profile, Client client) { public boolean canChangeProfile(ClientProfile profile, Client client) {
return client.isAuth && client.username != null && isWhitelisted(profile.getTitle(), client.username); return (client.isAuth && !profile.isLimited()) || isWhitelisted("launchserver.profile.%s.enter", profile, client);
} }
@Override @Override
@ -39,9 +42,18 @@ public boolean canGetUpdates(String updatesDirName, Client client) {
return client.profile != null && (client.profile.getDir().equals(updatesDirName) || client.profile.getAssetDir().equals(updatesDirName) || allowUpdates.contains(updatesDirName)); return client.profile != null && (client.profile.getDir().equals(updatesDirName) || client.profile.getAssetDir().equals(updatesDirName) || allowUpdates.contains(updatesDirName));
} }
public boolean isWhitelisted(String profileTitle, String username) { private boolean isWhitelisted(String property, ClientProfile profile, Client client) {
List<String> allowedUsername = profileWhitelist.get(profileTitle); if (client.permissions != null) {
if (allowedUsername == null) return true; String permByUUID = property.formatted(profile.getUUID());
return allowedUsername.contains(username); if (client.permissions.hasPerm(permByUUID)) {
return true;
}
String permByTitle = property.formatted(profile.getTitle().toLowerCase(Locale.ROOT));
if (client.permissions.hasPerm(permByTitle)) {
return true;
}
}
List<String> allowedUsername = profileWhitelist.get(profile.getTitle());
return allowedUsername != null && allowedUsername.contains(client.username);
} }
} }

View file

@ -1,22 +0,0 @@
package pro.gravit.launchserver.auth.protect.hwid;
public class HWIDException extends Exception {
public HWIDException() {
}
public HWIDException(String message) {
super(message);
}
public HWIDException(String message, Throwable cause) {
super(message, cause);
}
public HWIDException(Throwable cause) {
super(cause);
}
public HWIDException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}

View file

@ -1,101 +0,0 @@
package pro.gravit.launchserver.auth.protect.hwid;
import pro.gravit.launcher.HTTPRequest;
import pro.gravit.launcher.Launcher;
import pro.gravit.launcher.request.secure.HardwareReportRequest;
import pro.gravit.launchserver.socket.Client;
import java.net.URL;
public class JsonHWIDProvider extends HWIDProvider {
public URL findHardwareInfoByPublicKeyRequest;
public URL createHardwareInfoRequest;
public URL addPublicKeyToHardwareInfoRequest;
public String apiKey;
public static class RequestFind {
public byte[] publicKey;
public Client client;
public String apiKey;
}
public static class ResultFind {
public String error;
public HardwareReportRequest.HardwareInfo info;
}
@Override
public HardwareReportRequest.HardwareInfo findHardwareInfoByPublicKey(byte[] publicKey, Client client) throws HWIDException {
try {
RequestFind req = new RequestFind();
req.publicKey = publicKey;
req.client = client;
req.apiKey = apiKey;
ResultFind r = Launcher.gsonManager.gson.fromJson(HTTPRequest.jsonRequest(Launcher.gsonManager.gson.toJsonTree(req), findHardwareInfoByPublicKeyRequest), ResultFind.class);
if (r.error != null) throw new HWIDException(r.error);
return r.info;
} catch (HWIDException t) {
throw t;
} catch (Throwable t) {
throw new HWIDException(t);
}
}
public static class RequestCreate {
public byte[] publicKey;
public Client client;
public HardwareReportRequest.HardwareInfo hardwareInfo;
public String apiKey;
}
public static class ResultCreate {
public String error;
}
@Override
public void createHardwareInfo(HardwareReportRequest.HardwareInfo hardwareInfo, byte[] publicKey, Client client) throws HWIDException {
try {
RequestCreate req = new RequestCreate();
req.publicKey = publicKey;
req.client = client;
req.hardwareInfo = hardwareInfo;
req.apiKey = apiKey;
ResultCreate r = Launcher.gsonManager.gson.fromJson(HTTPRequest.jsonRequest(Launcher.gsonManager.gson.toJsonTree(req), createHardwareInfoRequest), ResultCreate.class);
if (r.error != null) throw new HWIDException(r.error);
} catch (HWIDException t) {
throw t;
} catch (Throwable t) {
throw new HWIDException(t);
}
}
public static class RequestAddKey {
public byte[] publicKey;
public Client client;
public HardwareReportRequest.HardwareInfo hardwareInfo;
public String apiKey;
}
public static class ResultAddKey {
public String error;
public boolean success;
}
@Override
public boolean addPublicKeyToHardwareInfo(HardwareReportRequest.HardwareInfo hardwareInfo, byte[] publicKey, Client client) throws HWIDException {
try {
RequestAddKey req = new RequestAddKey();
req.publicKey = publicKey;
req.client = client;
req.hardwareInfo = hardwareInfo;
req.apiKey = apiKey;
ResultAddKey r = Launcher.gsonManager.gson.fromJson(HTTPRequest.jsonRequest(Launcher.gsonManager.gson.toJsonTree(req), addPublicKeyToHardwareInfoRequest), ResultAddKey.class);
if (r.error != null) throw new HWIDException(r.error);
return r.success;
} catch (HWIDException t) {
throw t;
} catch (Throwable t) {
throw new HWIDException(t);
}
}
}

View file

@ -1,99 +0,0 @@
package pro.gravit.launchserver.auth.protect.hwid;
import pro.gravit.launcher.request.secure.HardwareReportRequest;
import pro.gravit.launchserver.Reconfigurable;
import pro.gravit.launchserver.socket.Client;
import pro.gravit.utils.command.Command;
import pro.gravit.utils.command.SubCommand;
import pro.gravit.utils.helper.LogHelper;
import pro.gravit.utils.helper.SecurityHelper;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
public class MemoryHWIDProvider extends HWIDProvider implements Reconfigurable {
public double warningSpoofingLevel = -1.0;
public double criticalCompareLevel = 1.0;
@Override
public Map<String, Command> getCommands() {
Map<String, Command> commands = new HashMap<>();
commands.put("hardwarelist", new SubCommand() {
@Override
public void invoke(String... args) throws Exception {
for (MemoryHWIDEntity e : db) {
printHardwareInfo(LogHelper.Level.INFO, e.hardware);
LogHelper.info("ID %d banned %s", e.id, e.banned ? "true" : "false");
LogHelper.info("PublicKey Hash: %s", SecurityHelper.toHex(SecurityHelper.digest(SecurityHelper.DigestAlgorithm.SHA1, e.publicKey)));
}
}
});
commands.put("hardwareban", new SubCommand() {
@Override
public void invoke(String... args) throws Exception {
verifyArgs(args, 1);
long id = Long.parseLong(args[0]);
for (MemoryHWIDEntity e : db) {
if (e.id == id) {
e.banned = true;
LogHelper.info("HardwareID %d banned", e.id);
}
}
}
});
return commands;
}
static class MemoryHWIDEntity {
public HardwareReportRequest.HardwareInfo hardware;
public byte[] publicKey;
public boolean banned;
public long id;
public MemoryHWIDEntity(HardwareReportRequest.HardwareInfo hardware, byte[] publicKey) {
this.hardware = hardware;
this.publicKey = publicKey;
this.id = SecurityHelper.newRandom().nextLong();
}
}
public Set<MemoryHWIDEntity> db = ConcurrentHashMap.newKeySet();
@Override
public HardwareReportRequest.HardwareInfo findHardwareInfoByPublicKey(byte[] publicKey, Client client) throws HWIDException {
for (MemoryHWIDEntity e : db) {
if (Arrays.equals(e.publicKey, publicKey)) {
if (e.banned) throw new HWIDException("You HWID banned");
return e.hardware;
}
}
return null;
}
@Override
public void createHardwareInfo(HardwareReportRequest.HardwareInfo hardwareInfo, byte[] publicKey, Client client) throws HWIDException {
db.add(new MemoryHWIDEntity(hardwareInfo, publicKey));
}
@Override
public boolean addPublicKeyToHardwareInfo(HardwareReportRequest.HardwareInfo hardwareInfo, byte[] publicKey, Client client) throws HWIDException {
boolean isAlreadyWarning = false;
for (MemoryHWIDEntity e : db) {
HardwareInfoCompareResult result = compareHardwareInfo(e.hardware, hardwareInfo);
if (warningSpoofingLevel > 0 && result.firstSpoofingLevel > warningSpoofingLevel && !isAlreadyWarning) {
LogHelper.warning("HardwareInfo spoofing level too high: %d", result.firstSpoofingLevel);
isAlreadyWarning = true;
}
if (result.compareLevel > criticalCompareLevel) {
LogHelper.debug("HardwareInfo publicKey change: compareLevel %d", result.compareLevel);
if (e.banned) throw new HWIDException("You HWID banned");
e.publicKey = publicKey;
return true;
}
}
return false;
}
}

View file

@ -1,164 +0,0 @@
package pro.gravit.launchserver.auth.protect.hwid;
import pro.gravit.launcher.request.secure.HardwareReportRequest;
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.auth.MySQLSourceConfig;
import pro.gravit.launchserver.socket.Client;
import pro.gravit.utils.helper.IOHelper;
import pro.gravit.utils.helper.LogHelper;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.sql.*;
public class MysqlHWIDProvider extends HWIDProvider {
public MySQLSourceConfig mySQLHolder;
public double warningSpoofingLevel = -1.0;
public double criticalCompareLevel = 1.0;
public String tableHWID = "hwids";
public String tableHWIDLog = "hwidLog";
public String tableUsers;
public String usersNameColumn;
public String usersHWIDColumn;
private String sqlFindByPublicKey;
private String sqlFindByHardware;
private String sqlCreateHardware;
private String sqlCreateHWIDLog;
private String sqlUpdateHardware;
private String sqlUpdateUsers;
@Override
public void init(LaunchServer server) {
sqlFindByPublicKey = String.format("SELECT hwDiskId, baseboardSerialNumber, displayId, bitness, totalMemory, logicalProcessors, physicalProcessors, processorMaxFreq, battery, id, banned FROM %s WHERE `publicKey` = ?", tableHWID);
sqlFindByHardware = String.format("SELECT hwDiskId, baseboardSerialNumber, displayId, bitness, totalMemory, logicalProcessors, physicalProcessors, processorMaxFreq, battery, id, banned FROM %s", tableHWID);
sqlCreateHardware = String.format("INSERT INTO `%s` (`publickey`, `hwDiskId`, `baseboardSerialNumber`, `displayId`, `bitness`, `totalMemory`, `logicalProcessors`, `physicalProcessors`, `processorMaxFreq`, `battery`, `banned`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, '0')", tableHWID);
sqlCreateHWIDLog = String.format("INSERT INTO %s (`hwidId`, `newPublicKey`) VALUES (?, ?)", tableHWIDLog);
sqlUpdateHardware = String.format("UPDATE %s SET `publicKey` = ? WHERE `id` = ?", tableHWID);
if (tableUsers != null && usersHWIDColumn != null && usersNameColumn != null) {
sqlUpdateUsers = String.format("UPDATE %s SET `%s` = ? WHERE `%s` = ?", tableUsers, usersHWIDColumn, usersNameColumn);
} else {
LogHelper.warning("[MysqlHWIDProvider] Link to users table not configured");
}
}
@Override
public HardwareReportRequest.HardwareInfo findHardwareInfoByPublicKey(byte[] publicKey, Client client) throws HWIDException {
try (Connection connection = mySQLHolder.getConnection()) {
PreparedStatement s = connection.prepareStatement(sqlFindByPublicKey);
s.setBlob(1, new ByteArrayInputStream(publicKey));
ResultSet set = s.executeQuery();
if (set.next()) {
if (set.getBoolean(11)) //isBanned
{
throw new SecurityException("You HWID banned");
}
long id = set.getLong(10);
setUserHardwareId(connection, client.username, id);
return fetchHardwareInfo(set);
} else {
return null;
}
} catch (SQLException | IOException throwables) {
LogHelper.error(throwables);
throw new SecurityException("SQL error. Please try again later");
}
}
private HardwareReportRequest.HardwareInfo fetchHardwareInfo(ResultSet set) throws SQLException, IOException {
HardwareReportRequest.HardwareInfo hardwareInfo = new HardwareReportRequest.HardwareInfo();
hardwareInfo.hwDiskId = set.getString(1);
hardwareInfo.baseboardSerialNumber = set.getString(2);
Blob displayId = set.getBlob(3);
hardwareInfo.displayId = displayId == null ? null : IOHelper.read(displayId.getBinaryStream());
hardwareInfo.bitness = set.getInt(4);
hardwareInfo.totalMemory = set.getLong(5);
hardwareInfo.logicalProcessors = set.getInt(6);
hardwareInfo.physicalProcessors = set.getInt(7);
hardwareInfo.processorMaxFreq = set.getLong(8);
hardwareInfo.battery = set.getBoolean(9);
return hardwareInfo;
}
@Override
public void createHardwareInfo(HardwareReportRequest.HardwareInfo hardwareInfo, byte[] publicKey, Client client) throws HWIDException {
try (Connection connection = mySQLHolder.getConnection()) {
PreparedStatement s = connection.prepareStatement(sqlCreateHardware, Statement.RETURN_GENERATED_KEYS);
s.setBlob(1, new ByteArrayInputStream(publicKey));
s.setString(2, hardwareInfo.hwDiskId);
s.setString(3, hardwareInfo.baseboardSerialNumber);
s.setBlob(4, hardwareInfo.displayId == null ? null : new ByteArrayInputStream(hardwareInfo.displayId));
s.setInt(5, hardwareInfo.bitness);
s.setLong(6, hardwareInfo.totalMemory);
s.setInt(7, hardwareInfo.logicalProcessors);
s.setInt(8, hardwareInfo.physicalProcessors);
s.setLong(9, hardwareInfo.processorMaxFreq);
s.setBoolean(10, hardwareInfo.battery);
s.executeUpdate();
try (ResultSet generatedKeys = s.getGeneratedKeys()) {
if (generatedKeys.next()) {
writeHwidLog(connection, generatedKeys.getLong(1), publicKey);
setUserHardwareId(connection, client.username, generatedKeys.getLong(1));
}
}
} catch (SQLException throwables) {
LogHelper.error(throwables);
throw new SecurityException("SQL error. Please try again later");
}
}
@Override
public boolean addPublicKeyToHardwareInfo(HardwareReportRequest.HardwareInfo hardwareInfo, byte[] publicKey, Client client) throws HWIDException {
try (Connection connection = mySQLHolder.getConnection()) {
PreparedStatement s = connection.prepareStatement(sqlFindByHardware);
ResultSet set = s.executeQuery();
while (set.next()) {
HardwareReportRequest.HardwareInfo hw = fetchHardwareInfo(set);
long id = set.getLong(10);
HardwareInfoCompareResult result = compareHardwareInfo(hw, hardwareInfo);
if (result.compareLevel > criticalCompareLevel) {
if (set.getBoolean(11)) //isBanned
{
throw new SecurityException("You HWID banned");
}
writeHwidLog(connection, id, publicKey);
changePublicKey(connection, id, publicKey);
setUserHardwareId(connection, client.username, id);
return true;
}
}
} catch (SQLException | IOException throwables) {
LogHelper.error(throwables);
throw new SecurityException("SQL error. Please try again later");
}
return false;
}
private void changePublicKey(Connection connection, long id, byte[] publicKey) throws SQLException {
PreparedStatement s = connection.prepareStatement(sqlUpdateHardware);
s.setBlob(1, new ByteArrayInputStream(publicKey));
s.setLong(2, id);
s.executeUpdate();
}
private void writeHwidLog(Connection connection, long hwidId, byte[] newPublicKey) throws SQLException {
PreparedStatement s = connection.prepareStatement(sqlCreateHWIDLog);
s.setLong(1, hwidId);
s.setBlob(2, new ByteArrayInputStream(newPublicKey));
s.executeUpdate();
}
private void setUserHardwareId(Connection connection, String username, long hwidId) throws SQLException {
if (sqlUpdateUsers == null || username == null) return;
PreparedStatement s = connection.prepareStatement(sqlUpdateUsers);
s.setLong(1, hwidId);
s.setString(2, username);
s.executeUpdate();
}
@Override
public void close() {
mySQLHolder.close();
}
}

View file

@ -2,8 +2,10 @@
import pro.gravit.launchserver.socket.Client; import pro.gravit.launchserver.socket.Client;
import java.util.UUID;
public interface JoinServerProtectHandler { public interface JoinServerProtectHandler {
default boolean onJoinServer(String serverID, String username, Client client) { default boolean onJoinServer(String serverID, String username, UUID uuid, Client client) {
return true; return true;
} }
} }

View file

@ -1,6 +1,6 @@
package pro.gravit.launchserver.auth.protect.interfaces; package pro.gravit.launchserver.auth.protect.interfaces;
import pro.gravit.launcher.profiles.ClientProfile; import pro.gravit.launcher.base.profiles.ClientProfile;
import pro.gravit.launchserver.socket.Client; import pro.gravit.launchserver.socket.Client;
public interface ProfilesProtectHandler { public interface ProfilesProtectHandler {

View file

@ -1,8 +1,8 @@
package pro.gravit.launchserver.auth.protect.interfaces; package pro.gravit.launchserver.auth.protect.interfaces;
import pro.gravit.launcher.events.request.GetSecureLevelInfoRequestEvent; import pro.gravit.launcher.base.events.request.GetSecureLevelInfoRequestEvent;
import pro.gravit.launcher.events.request.SecurityReportRequestEvent; import pro.gravit.launcher.base.events.request.SecurityReportRequestEvent;
import pro.gravit.launcher.events.request.VerifySecureLevelKeyRequestEvent; import pro.gravit.launcher.base.events.request.VerifySecureLevelKeyRequestEvent;
import pro.gravit.launchserver.socket.Client; import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.response.secure.SecurityReportResponse; import pro.gravit.launchserver.socket.response.secure.SecurityReportResponse;
import pro.gravit.utils.helper.SecurityHelper; import pro.gravit.utils.helper.SecurityHelper;
@ -19,7 +19,7 @@ default byte[] generateSecureLevelKey() {
default void verifySecureLevelKey(byte[] publicKey, byte[] data, byte[] signature) throws InvalidKeySpecException, SignatureException { default void verifySecureLevelKey(byte[] publicKey, byte[] data, byte[] signature) throws InvalidKeySpecException, SignatureException {
if (publicKey == null || signature == null) throw new InvalidKeySpecException(); if (publicKey == null || signature == null) throw new InvalidKeySpecException();
ECPublicKey pubKey = SecurityHelper.toPublicECKey(publicKey); ECPublicKey pubKey = SecurityHelper.toPublicECDSAKey(publicKey);
Signature sign = SecurityHelper.newECVerifySignature(pubKey); Signature sign = SecurityHelper.newECVerifySignature(pubKey);
sign.update(data); sign.update(data);
sign.verify(signature); sign.verify(signature);

View file

@ -1,18 +0,0 @@
package pro.gravit.launchserver.auth.provider;
import pro.gravit.launcher.ClientPermissions;
import pro.gravit.launcher.request.auth.AuthRequest;
import pro.gravit.utils.helper.SecurityHelper;
public final class AcceptAuthProvider extends AuthProvider {
@Override
public AuthProviderResult auth(String login, AuthRequest.AuthPasswordInterface password, String ip) {
return new AuthProviderResult(login, SecurityHelper.randomStringToken(), ClientPermissions.DEFAULT); // Same as login
}
@Override
public void close() {
// Do nothing
}
}

View file

@ -1,63 +0,0 @@
package pro.gravit.launchserver.auth.provider;
import pro.gravit.launcher.events.request.GetAvailabilityAuthRequestEvent;
import pro.gravit.launcher.request.auth.AuthRequest;
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.auth.AuthException;
import pro.gravit.utils.ProviderMap;
import java.io.IOException;
public abstract class AuthProvider implements AutoCloseable {
public static final ProviderMap<AuthProvider> providers = new ProviderMap<>("AuthProvider");
private static boolean registredProv = false;
protected transient LaunchServer srv = null;
public static AuthProviderResult authError(String message) throws AuthException {
throw new AuthException(message);
}
public static void registerProviders() {
if (!registredProv) {
providers.register("null", NullAuthProvider.class);
providers.register("accept", AcceptAuthProvider.class);
providers.register("reject", RejectAuthProvider.class);
providers.register("mysql", MySQLAuthProvider.class);
providers.register("postgresql", PostgreSQLAuthProvider.class);
providers.register("request", RequestAuthProvider.class);
providers.register("json", JsonAuthProvider.class);
providers.register("hibernate", HibernateAuthProvider.class);
registredProv = true;
}
}
public GetAvailabilityAuthRequestEvent.AuthAvailability.AuthType getFirstAuthType() {
return GetAvailabilityAuthRequestEvent.AuthAvailability.AuthType.PASSWORD;
}
public GetAvailabilityAuthRequestEvent.AuthAvailability.AuthType getSecondAuthType() {
return GetAvailabilityAuthRequestEvent.AuthAvailability.AuthType.NONE;
}
/**
* Verifies the username and password
*
* @param login user login
* @param password user password
* @param ip user ip
* @return player privileges, effective username and authorization token
* @throws Exception Throws an exception {@link AuthException} {@link pro.gravit.utils.HookException} if the verification script returned a meaningful error
* In other cases, throwing an exception indicates a serious error
*/
public abstract AuthProviderResult auth(String login, AuthRequest.AuthPasswordInterface password, String ip) throws Exception;
public void preAuth(String login, AuthRequest.AuthPasswordInterface password, String ip) {
}
@Override
public abstract void close() throws IOException;
public void init(LaunchServer srv) {
this.srv = srv;
}
}

View file

@ -1,21 +0,0 @@
package pro.gravit.launchserver.auth.provider;
import pro.gravit.launcher.ClientPermissions;
import pro.gravit.launchserver.dao.User;
public class AuthProviderDAOResult extends AuthProviderResult {
public User daoObject;
public AuthProviderDAOResult(String username, String accessToken) {
super(username, accessToken);
}
public AuthProviderDAOResult(String username, String accessToken, ClientPermissions permissions) {
super(username, accessToken, permissions);
}
public AuthProviderDAOResult(String username, String accessToken, ClientPermissions permissions, User daoObject) {
super(username, accessToken, permissions);
this.daoObject = daoObject;
}
}

View file

@ -1,22 +0,0 @@
package pro.gravit.launchserver.auth.provider;
import pro.gravit.launcher.ClientPermissions;
public class AuthProviderResult {
public final String username;
public final String accessToken;
public final ClientPermissions permissions;
public AuthProviderResult(String username, String accessToken) {
this.username = username;
this.accessToken = accessToken;
permissions = ClientPermissions.DEFAULT;
}
public AuthProviderResult(String username, String accessToken, ClientPermissions permissions) {
this.username = username;
this.accessToken = accessToken;
this.permissions = permissions;
}
}

View file

@ -1,26 +0,0 @@
package pro.gravit.launchserver.auth.provider;
import pro.gravit.launchserver.auth.AuthException;
import pro.gravit.utils.helper.SecurityHelper;
import pro.gravit.utils.helper.SecurityHelper.DigestAlgorithm;
public abstract class DigestAuthProvider extends AuthProvider {
private DigestAlgorithm digest;
protected final void verifyDigest(String validDigest, String password) throws AuthException {
boolean valid;
if (digest == DigestAlgorithm.PLAIN)
valid = password.equals(validDigest);
else if (validDigest == null)
valid = false;
else {
byte[] actualDigest = SecurityHelper.digest(digest, password);
valid = SecurityHelper.toHex(actualDigest).equals(validDigest);
}
// Verify is valid
if (!valid)
authError("Incorrect username or password");
}
}

View file

@ -1,36 +0,0 @@
package pro.gravit.launchserver.auth.provider;
import pro.gravit.launcher.request.auth.AuthRequest;
import pro.gravit.launcher.request.auth.password.AuthPlainPassword;
import pro.gravit.launchserver.auth.AuthException;
import pro.gravit.launchserver.dao.User;
import pro.gravit.launchserver.manangers.hook.AuthHookManager;
import pro.gravit.utils.helper.SecurityHelper;
public class HibernateAuthProvider extends AuthProvider {
public boolean autoReg;
@Override
public AuthProviderResult auth(String login, AuthRequest.AuthPasswordInterface password, String ip) throws Exception {
if (!(password instanceof AuthPlainPassword)) throw new AuthException("This password type not supported");
User user = srv.config.dao.userDAO.findByUsername(login);
if (user == null && autoReg) {
AuthHookManager.RegContext context = new AuthHookManager.RegContext(login, ((AuthPlainPassword) password).password, ip, false);
if (srv.authHookManager.registraion.hook(context)) {
//user = srv.config.dao.userService.registerNewUser(login, ((AuthPlainPassword) password).password); //TODO: FIX
} else {
throw new AuthException("Registration canceled. Try again later");
}
}
if (user == null || !user.verifyPassword(((AuthPlainPassword) password).password)) {
if (user == null) throw new AuthException("Username incorrect");
else throw new AuthException("Username or password incorrect");
}
return new AuthProviderDAOResult(user.getUsername(), SecurityHelper.randomStringToken(), user.getPermissions(), user);
}
@Override
public void close() {
}
}

View file

@ -1,63 +0,0 @@
package pro.gravit.launchserver.auth.provider;
import com.google.gson.JsonElement;
import pro.gravit.launcher.ClientPermissions;
import pro.gravit.launcher.HTTPRequest;
import pro.gravit.launcher.Launcher;
import pro.gravit.launcher.request.auth.AuthRequest;
import pro.gravit.launcher.request.auth.password.AuthPlainPassword;
import pro.gravit.launchserver.auth.AuthException;
import pro.gravit.utils.helper.SecurityHelper;
import java.io.IOException;
import java.net.URL;
import java.util.Objects;
public final class JsonAuthProvider extends AuthProvider {
public URL url;
public String apiKey;
@Override
public AuthProviderResult auth(String login, AuthRequest.AuthPasswordInterface password, String ip) throws IOException {
if (!(password instanceof AuthPlainPassword)) throw new AuthException("This password type not supported");
JsonElement content = HTTPRequest.jsonRequest(Launcher.gsonManager.gson.toJsonTree(new authRequest(login, ((AuthPlainPassword) password).password, ip, apiKey)), url);
if (!content.isJsonObject())
return authError("Authentication server response is malformed");
authResult result = Launcher.gsonManager.gson.fromJson(content, authResult.class);
if (result.username != null)
return new AuthProviderResult(result.username, SecurityHelper.randomStringToken(), new ClientPermissions(result.permissions, result.flags));
else return authError(Objects.requireNonNullElse(result.error, "Authentication server response is malformed"));
}
@Override
public void close() {
// pass
}
public static class authResult {
String username;
String error;
long permissions;
long flags;
}
public static class authRequest {
final String username;
final String password;
final String ip;
String apiKey;
public authRequest(String username, String password, String ip) {
this.username = username;
this.password = password;
this.ip = ip;
}
public authRequest(String username, String password, String ip, String apiKey) {
this.username = username;
this.password = password;
this.ip = ip;
this.apiKey = apiKey;
}
}
}

View file

@ -1,56 +0,0 @@
package pro.gravit.launchserver.auth.provider;
import pro.gravit.launcher.ClientPermissions;
import pro.gravit.launcher.request.auth.AuthRequest;
import pro.gravit.launcher.request.auth.password.AuthPlainPassword;
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.auth.AuthException;
import pro.gravit.launchserver.auth.MySQLSourceConfig;
import pro.gravit.utils.helper.CommonHelper;
import pro.gravit.utils.helper.LogHelper;
import pro.gravit.utils.helper.SecurityHelper;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public final class MySQLAuthProvider extends AuthProvider {
private MySQLSourceConfig mySQLHolder;
private String query;
private String message;
private String[] queryParams;
private boolean flagsEnabled;
@Override
public void init(LaunchServer srv) {
super.init(srv);
if (query == null) LogHelper.error("[Verify][AuthProvider] query cannot be null");
if (message == null) LogHelper.error("[Verify][AuthProvider] message cannot be null");
if (mySQLHolder == null) LogHelper.error("[Verify][AuthProvider] mySQLHolder cannot be null");
}
@Override
public AuthProviderResult auth(String login, AuthRequest.AuthPasswordInterface password, String ip) throws SQLException, AuthException {
if (!(password instanceof AuthPlainPassword)) throw new AuthException("This password type not supported");
try (Connection c = mySQLHolder.getConnection()) {
PreparedStatement s = c.prepareStatement(query);
String[] replaceParams = {"login", login, "password", ((AuthPlainPassword) password).password, "ip", ip};
for (int i = 0; i < queryParams.length; i++)
s.setString(i + 1, CommonHelper.replace(queryParams[i], replaceParams));
// Execute SQL query
s.setQueryTimeout(MySQLSourceConfig.TIMEOUT);
try (ResultSet set = s.executeQuery()) {
return set.next() ? new AuthProviderResult(set.getString(1), SecurityHelper.randomStringToken(), new ClientPermissions(
set.getLong(2), flagsEnabled ? set.getLong(3) : 0)) : authError(message);
}
}
}
@Override
public void close() {
mySQLHolder.close();
}
}

View file

@ -1,32 +0,0 @@
package pro.gravit.launchserver.auth.provider;
import pro.gravit.launcher.request.auth.AuthRequest;
import pro.gravit.utils.helper.VerifyHelper;
import java.io.IOException;
import java.util.Objects;
public final class NullAuthProvider extends AuthProvider {
private volatile AuthProvider provider;
@Override
public AuthProviderResult auth(String login, AuthRequest.AuthPasswordInterface password, String ip) throws Exception {
return getProvider().auth(login, password, ip);
}
@Override
public void close() throws IOException {
AuthProvider provider = this.provider;
if (provider != null)
provider.close();
}
private AuthProvider getProvider() {
return VerifyHelper.verify(provider, Objects::nonNull, "Backend auth provider wasn't set");
}
public void setBackend(AuthProvider provider) {
this.provider = provider;
}
}

View file

@ -1,46 +0,0 @@
package pro.gravit.launchserver.auth.provider;
import pro.gravit.launcher.ClientPermissions;
import pro.gravit.launcher.request.auth.AuthRequest;
import pro.gravit.launcher.request.auth.password.AuthPlainPassword;
import pro.gravit.launchserver.auth.AuthException;
import pro.gravit.launchserver.auth.PostgreSQLSourceConfig;
import pro.gravit.utils.helper.CommonHelper;
import pro.gravit.utils.helper.SecurityHelper;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public final class PostgreSQLAuthProvider extends AuthProvider {
private PostgreSQLSourceConfig postgreSQLHolder;
private String query;
private String message;
private String[] queryParams;
private boolean flagsEnabled;
@Override
public AuthProviderResult auth(String login, AuthRequest.AuthPasswordInterface password, String ip) throws SQLException, AuthException {
if (!(password instanceof AuthPlainPassword)) throw new AuthException("This password type not supported");
try (Connection c = postgreSQLHolder.getConnection(); PreparedStatement s = c.prepareStatement(query)) {
String[] replaceParams = {"login", login, "password", ((AuthPlainPassword) password).password, "ip", ip};
for (int i = 0; i < queryParams.length; i++) {
s.setString(i + 1, CommonHelper.replace(queryParams[i], replaceParams));
}
// Execute SQL query
s.setQueryTimeout(PostgreSQLSourceConfig.TIMEOUT);
try (ResultSet set = s.executeQuery()) {
return set.next() ? new AuthProviderResult(set.getString(1), SecurityHelper.randomStringToken(), new ClientPermissions(
set.getLong(2), flagsEnabled ? set.getLong(3) : 0)) : authError(message);
}
}
}
@Override
public void close() {
postgreSQLHolder.close();
}
}

Some files were not shown because too many files have changed in this diff Show more