Compare commits

..

1194 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
Gravit
90f360c565
Merge branch 'release/5.1.8' into master 2020-09-29 01:35:23 +07:00
Gravit
a555f1b1d9
[FIX] 5.1.8-stable 2020-09-29 01:32:19 +07:00
Gravit
fe00a62971
[FIX] SecurityCheckCommand fix 2020-09-29 01:08:05 +07:00
Gravit
f9066d1782
[ANY] 5.1.8-dev to 5.1.8-beta 2020-09-27 01:22:00 +07:00
Gravit
326012aad8
[FIX] optionalClientArgs 2020-09-27 00:52:43 +07:00
Gravit
ef3fbaef18
[ANY] IDEA Reformat 2020-09-27 00:49:45 +07:00
Gravit
78c0e0d54d
[FEATURE] Command SecurityCheck 2020-09-26 22:09:25 +07:00
Gravit
438133bce7
[FIX] Потенциальные NPE 2020-09-26 20:13:52 +07:00
Gravit
32d7ca932a
[FIX] Multi-Release 2020-09-26 17:06:51 +07:00
Gravit
f9521b41a2
[FIX] Утечка памяти из за пингов(!) 2020-09-26 17:06:09 +07:00
Gravit
d7519688ee
[FIX][CRITICAL] Замена SimpleDateFormat на thread-safe DateTimeFormatter 2020-09-26 04:01:36 +07:00
Gravit
92df57e491
[FEATURE] Улучшение обработки ошибок websocket 2020-09-26 02:57:42 +07:00
Gravit
584e07328c
[FEATURE][EXPERIMENTAL] NettyThreadFactory 2020-09-26 02:47:19 +07:00
Gravit
3a3aafe5fa
[FEATURE] Улучшение RequestAuthProvider 2020-09-26 02:25:18 +07:00
Gravit
a4355d1d05
[FEATURE] Использование Java 11 по умолчанию 2020-09-26 01:53:44 +07:00
Gravit
9766cd69e5
[ANY] IDEA Reformat 2020-09-25 22:49:43 +07:00
Gravit
8ce5b4179c
[FIX] IDEA Inspect and bug fixes 2020-09-25 22:44:39 +07:00
Gravit
9efcd7af26
[FIX] secp256k1 #414 2020-09-25 17:19:57 +07:00
Gravit
5646813885
[FIX] Ошибка при сериализации инициализированного DaoProvider 2020-09-24 22:07:46 +07:00
Gravit
186a749150
[FIX] Оптимизация 2020-09-17 19:48:11 +07:00
Gravit
5413ba0dd7
[FEATURE] Simple Severlet (велосипедостроение) 2020-09-17 19:40:53 +07:00
Gravit
211b440b8b
[FIX] Server-side pings 2020-09-15 18:10:07 +07:00
Dbarkovski
ddab96f7ea
Update ClientProfile.java (#413)
Добавлена поддержка 1.16.3
2020-09-14 15:06:54 +07:00
Gravit
5835676b7c
[FEATURE][EXPERIMENTAL] Конструктор копирования у OptionalView 2020-09-12 16:49:54 +07:00
Gravit
8b8ef665ef
[FEATURE][EXPERIMENTAL] Потенциальная проблема с зависимостями в опциональных модах 2020-09-12 16:34:09 +07:00
Gravit
72a8325a15
[FEATURE][EXPERIMENTAL] Конвертация опциональных модов в новый формат 2020-09-12 16:12:39 +07:00
Gravit
2271b91653
[FIX][EXPERIMENTAL] Некорректная работа findRecursive при отсутствии конечного HashedEntry 2020-09-12 15:57:03 +07:00
Gravit
b2a3e5672a
[FEATURE][EXPERIMENTAL] Новый API опциональных модов 2020-09-12 13:41:58 +07:00
Gravit
383551229d
[FEATURE] Поддержка HikariCP в HibernateDAO 2020-09-08 21:26:04 +07:00
Gravit
996d126611
[FIX] Забытые конструкторы для API 2020-08-29 14:37:56 +07:00
Gravit
57cdd64142
[FIX] AdvancedProtectHandler сериализация 2020-08-28 19:44:41 +07:00
Gravit
4d17f34ba6
[FIX] ExitResponse 2020-08-27 23:02:40 +07:00
Gravit
886c085572
[FEATURE] Новые типы пароля для поддержки 2FA 2020-08-27 22:50:18 +07:00
Gravit
1362d71788
[FIX] Исправление безопасности 2020-08-27 21:31:11 +07:00
Gravit
4917f19b81
[FEATURE] Улучшение API 2020-08-27 21:25:14 +07:00
Gravit
2e238f3dc2
[FIX] LaunchServer instance в ProtectHandler 2020-08-27 20:39:20 +07:00
Zaxar163
d5bab20610
[ANY] Обновлены модули. 2020-08-24 13:01:51 +03:00
Zaxar163
9a748f0ea3
Merge branch 'dev' of github.com:GravitLauncher/Launcher into dev 2020-08-24 12:15:01 +03:00
Zaxar163
ea299c4fea
[FIX] Неправильного вывода IP командой clients. Closes #411. 2020-08-24 12:05:24 +03:00
Zaxar163
d98c6c39c5
[FEATURE] JsonHWIDProvider + [FIX] Больше запросы JsonAuthHandler не pretty-print. Closes #410 2020-08-24 12:01:42 +03:00
Gravit
17bce5dff0
[FIX] Username case 2020-08-22 23:22:25 +07:00
Dbarkovski
e3da81bc85
Update ClientProfile.java (#409) 2020-08-14 17:16:34 +07:00
Dbarkovski
46ce073a1f
Update ClientProfile.java (#407)
Добавлена поддержка версии 1.16.2
2020-08-14 16:11:20 +07:00
Gravit
15a0de33ce
[ANY] Обновлены модули 2020-08-12 13:39:16 +07:00
Gravit
e304bd7285
[FEATURE][EXPERIMENTAL] session теперь UUID для совместимости с JS 2020-08-12 13:39:16 +07:00
Gravit
2c8c640130
[FIX] Несколько фиксов NPE 2020-08-12 13:39:15 +07:00
Alexey Zakharov
aa87ff05eb
Обновлена ссылка на Discord (#406)
* Update README.md

* Сокращена ссылка
2020-08-10 13:45:02 +07:00
Gravit
11e50e065c
[ANY] Предупреждение если не сконфигурирована связь с таблицей users 2020-08-02 05:25:39 +07:00
Gravit
1fbad1cfa0
[FEATURE] DirWatcher для params.javaHDir 2020-08-02 05:16:18 +07:00
Gravit
c969b16391
[FIX] Предупреждение об отсутствии JavaFX теперь ошибка 2020-07-24 23:30:08 +07:00
Gravit
48a2a8346a
[FEATURE] Добавление параметра bits к ClientProcess API 2020-07-24 20:39:23 +07:00
Gravit
84617f2fbc
[ANY] 5.1.8-dev 2020-07-22 21:31:24 +07:00
Gravit
db2dffb51c
[ANY] Обновлены модули 2020-07-22 21:28:26 +07:00
Gravit
d4389b9f1f
[FEATURE] Properties в объекте клиента 2020-07-22 21:28:14 +07:00
Gravit
57b0809e56
Merge tag 'v5.1.7' into dev
5.1.7 Stable
2020-07-15 10:58:48 +07:00
Gravit
4b894c3ae1
Merge branch 'release/5.1.7' 2020-07-15 10:58:38 +07:00
Gravit
90ec3efe3d
[ANY] 5.1.7 stable 2020-07-15 10:58:24 +07:00
Gravit
cd890a73ee
[FIX] Альтернативное зеркало и фиксы профилей 2020-07-15 10:54:10 +07:00
Gravit
b92e9a1d29
[FEATURE] SaveProfilesCommand поддержка ServerProfile 2020-07-03 07:38:48 +07:00
Gravit
4bea6bb16b
[FEATURE] PingServerRequest 2020-07-03 07:38:48 +07:00
Andrew Molchanov
6c76e0758d
[FIX] Замена 1.16 на 1.16.1 (#400) 2020-06-30 19:44:18 +07:00
Gravit
3ad7002da7
[FEATURE][EXPERIMENTAL] PingServerReportRequest 2020-06-24 11:06:18 +07:00
Andrew Molchanov
4502f978fb
[ANY] Поддержка 1.16 (#397) 2020-06-23 19:07:43 +03:00
Gravit
07ba1255ce
[FEATURE] defaultClass в сериализации по type 2020-06-22 11:46:22 +07:00
Zaxar163
dd11ba8179
[ANY] Обновлены модули. 2020-06-20 11:58:34 +03:00
Zaxar163
6500b764e9
[ANY] Обновлены модули. 2020-06-18 19:46:40 +03:00
Gravit
926e1534af
[FEATURE] Вывод отладки в поиске Java 2020-06-17 15:54:23 +07:00
Will0376
3e6384cad9
[FEATURE] SyncUPCommand (#396)
* Create SyncAll.java

Выплёвываю как есть. Регистрация где - я хз. Дальше сами.

* Update CommandHandler.java

* Update and rename SyncAll.java to SyncUP.java

* Update CommandHandler.java

* Update and rename SyncUP.java to SyncUPCommand.java

* Update CommandHandler.java
2020-06-17 15:46:08 +07:00
Gravit
09a6afdf57
[FEATURE] Улучшение поиска подходящей Java 2020-06-17 15:32:30 +07:00
Gravit
a65837d81d
[FIX] NPE in MysqlHWIDProvider 2020-06-15 13:23:07 +07:00
Gravit
5b77e4d8fd
[FIX] Поиск подходящей Java 2020-06-15 13:20:34 +07:00
Gravit
12f73aabcb
[ANY] Обновлены модули 2020-06-15 13:18:27 +07:00
Gravit
36786c688d
[FEATURE][EXPERIMENTAL] Поиск подходящей джавы для Windows 2020-06-08 15:45:49 +07:00
Gravit
e7fd749c2f
[FIX] Обращение к AuthHandler'у 2020-06-07 12:52:36 +07:00
Gravit
eca8cc2cbc
Merge branch 'dev' of github.com:GravitLauncher/Launcher into dev 2020-06-05 08:27:18 +07:00
Gravit
aabd18e089
[FIX] Открытие доступа к UUID и URL скинов и плащей 2020-06-05 08:25:12 +07:00
ijo42
2043d301a1
[FIX] Работа конфигов на русифицированных ОС (#394) 2020-06-04 21:18:12 +02:00
Gravit
dba9d70b37
[FIX] LauncherModuleLoader 2020-06-04 13:28:33 +07:00
Gravit
81abc3fdc2
[FEATURE] Добавлен запрос ServerStatus. Удален execCommand и logListen 2020-05-30 04:06:15 +07:00
Gravit
39f7d4bbda
[ANY] 5.1.7-dev 2020-05-30 00:13:55 +07:00
Gravit
c85e5be4da
Merge tag 'v5.1.6' into dev
v5.1.6-stable
2020-05-30 00:12:53 +07:00
Gravit
ea7d50a8bc
Merge branch 'release/5.1.6' 2020-05-30 00:12:41 +07:00
Gravit
6511207a6e
[ANY] 5.1.6-stable 2020-05-29 23:53:03 +07:00
Gravit
2a9a4256f9
[FIX] NPE in MysqlHWIDProvider 2020-05-29 23:46:12 +07:00
Zaxar163
98431cc796
[ANY] Исправлена ссылка на дискорд канал. 2020-05-28 20:00:44 +03:00
Gravit
898c6a33a1
[FEATURE] Запись последнего HWID в users 2020-05-28 22:50:54 +07:00
Gravit
c353de9267
[FEATURE][EXPERIMENTAL] AuthProviderDAOResult 2020-05-26 04:42:31 +07:00
Gravit
bdecd57470
[FEATURE][EXPERIMENTAL] Переработка DAO/Hibernate 2020-05-26 04:38:47 +07:00
Gravit
c7688afb32
[FIX] NPE в AdvancedProtectHandler 2020-05-26 00:09:01 +07:00
Gravit
72a8c03296
[FEATURE] MysqlHWIDProvider 2020-05-24 03:17:00 +07:00
Gravit
90519f0985
[ANY] J,yjdktys vjlekb 2020-05-24 00:40:12 +07:00
Gravit
73fcf4aae4 [FEATURE] Try recursive LauncherModule configuration 2020-05-20 21:30:33 +07:00
Gravit
fb00adb129
[FIX] HWID banned users 2020-05-18 19:57:12 +07:00
Gravit
3baffcafb5
[FEATURE] HWIDProvider 2020-05-18 19:33:52 +07:00
Gravit
a180673b26
[ANY] 5.1.6 dev 2020-05-08 10:37:08 +07:00
Gravit
d2b82214fb
Merge tag 'v5.1.5' into dev
5.1.5 Stable
2020-05-08 10:32:17 +07:00
Gravit
98f1e30e61
Merge branch 'release/5.1.5' 2020-05-08 10:32:05 +07:00
Gravit
3332faac57
[ANY] 5.1.5 Stable 2020-05-08 10:31:50 +07:00
Gravit
b9d29008ff
[FIX] Compile fix 2020-05-05 10:56:00 +07:00
Gravit
8d1c7621cf
[FEATURE] Улучшение основы для HWID 2020-05-05 10:55:08 +07:00
Gravit
80e919f4a1
[ANY] Проверка на наличие файлов javafx для proguard 2020-05-05 06:40:03 +07:00
Gravit
ca5c435341
[FIX] ClientLauncherCore module in ClientLauncherEntryPoint 2020-05-01 23:56:08 +07:00
Gravit
96d2cfaf26
[FIX] Cyclic dependencies in LaunchServerCore Module 2020-05-01 10:32:35 +07:00
Gravit
4e4f606636
[ANY] Предупреждение при использовании Autogen сертификата 2020-05-01 09:57:14 +07:00
Gravit
19831a6f72
[FIX] GitHub Actions fix 2020-05-01 08:59:09 +07:00
Gravit
f676a5e1cf
[ANY] Обновлены модули 2020-05-01 08:45:45 +07:00
Gravit
65693fa867
[ANY] Обновлены модули 2020-05-01 08:30:36 +07:00
Gravit
7f48772663
[ANY] Обновлены модули 2020-05-01 07:56:58 +07:00
Gravit
4ab66caad0
[ANY] Обновлены модули 2020-04-29 10:57:37 +07:00
Gravit
2f414ae9e7
[ANY] Обновлены модули 2020-04-27 07:45:38 +07:00
Gravit
fe21427d17
[FEATURE] Raw WebSocketFrame API 2020-04-27 05:22:49 +07:00
Gravit
ea5449a196
[ANY] 5.1.5-dev 2020-04-27 04:22:32 +07:00
Gravit
6bee803f00
Merge tag 'v5.1.4' into dev
v5.1.4 Stable
2020-04-27 04:20:42 +07:00
Gravit
aed80e995a
Merge branch 'release/5.1.4' 2020-04-27 04:20:32 +07:00
Gravit
e81b098320
[ANY] 5.1.4-stable 2020-04-27 04:20:15 +07:00
sasha0552
a71b62ca1a
[ANY] Setup "jar filename" - specify for what (#385) 2020-04-24 20:32:08 +03:00
Zaxar163
ac64ec5483 [ANY] Удалены старые файлы. 2020-04-18 09:19:14 +03:00
Zaxar163
ac737c4ccb [FIX] Создаём папки при копировании... 2020-04-18 08:22:25 +03:00
Zaxar163
8a53e5a925
[FIX] Смена директории при помощи DirBridge.move (#374) 2020-04-17 18:37:34 +03:00
Gravit
1abb66fcb8
[ANY] 5.1.4-dev 2020-04-16 21:26:38 +07:00
Zaxar163
85400d968b [FIX] Loop в IOHelper.move 2020-04-14 18:41:49 +03:00
Zaxar163
72232e4cc7 [FIX] Перенос папок в IOHelper. 2020-04-14 18:29:01 +03:00
Zaxar163
6077a51fd4 [ANY] Выводим стектрейс, если DirBridge.move получил аргументом null. 2020-04-14 16:01:10 +03:00
Zaxar163
5ad1d58f62 [REFACTOR] Чистка от неиспользованных констант. 2020-04-13 20:29:13 +03:00
Zaxar163
d06f179e9c Merge branch 'dev' of github.com:GravitLauncher/Launcher into dev 2020-04-13 20:16:48 +03:00
Zaxar163
f94766679c [FEATURE] Перенос LauncherModuleLoader в LaunchServer. Поддержка конфигов с субклассами. 2020-04-13 20:16:17 +03:00
Gravit
4e472151e9
Merge pull request #371 from typicalcoder/patch-1
[FIX] Permissions
2020-04-12 21:00:57 +07:00
Gravit
d2ce49e41a [FIX] Missed = 2020-04-12 20:59:24 +07:00
Gravit
4592aeea2d [FEATURE] May be fix old wrapper's 2020-04-12 20:58:00 +07:00
Gravit
c438f08d7a
[FEATURE] Проверка HWID 2020-04-07 19:05:58 +07:00
Gravit
925c1170a3
[ANY] Обновлены модули 2020-04-07 18:17:00 +07:00
Gravit
bf932a4893
Merge branch 'dev' of github.com:GravitLauncher/Launcher into dev 2020-04-07 18:14:09 +07:00
Gravit
5c5240a2f3
[FEATURE] HWID возвращается 2020-04-07 18:11:58 +07:00
Alexey
ca694601df
[FIX] Permissions 2020-04-05 17:47:21 +03:00
Zaxar163
aa49c60fef [ANY] Обновлены модули. 2020-04-05 17:07:47 +03:00
Zaxar163
c0824a84cd [ANY] Удалены остатки т старой системы сокетов (ещё с 4.xx). + [ANY] Обновлены модули. 2020-04-05 13:46:04 +03:00
Zaxar163
49a2d79ce1 [ANY] Обновлены модули. 2020-04-05 12:01:20 +03:00
Gravit
447e03b999
Merge pull request #370 from GravitLauncher/any/refactor
[ANY] Рефакторинг.
2020-04-05 15:58:33 +07:00
Zaxar163
68c06de23e [FIX] StartLaunchServerTest теперь нормально работает. 2020-04-05 11:30:32 +03:00
Zaxar163
34e941ddfd [ANY] Обновлены модули. 2020-04-05 10:39:56 +03:00
Zaxar163
fd5803ca32 [REFACTOR] Заключительная часть. 2020-04-05 10:32:03 +03:00
Zaxar163
0d5dbe7794 [REFACTOR] Часть 1. 2020-04-05 10:27:04 +03:00
Zaxar163
3ed166f8e7 [FIX] Поддержка нового формата подключения PostgreSQL 2020-04-05 10:20:35 +03:00
Gravit
43a43ec30d
Merge tag 'v5.1.3' into dev
v5.1.3 stable
2020-04-04 20:54:58 +07:00
Gravit
45e3d0b18e
Merge branch 'release/5.1.3' 2020-04-04 20:54:44 +07:00
Gravit
fe59e27bfa
[ANY] 5.1.3 Stable 2020-04-04 20:51:14 +07:00
Gravit
63d9c73e00
[ANY] Обновлены модули 2020-04-04 19:42:22 +07:00
Gravit
3ce38a1b0d
[ANY] Обновлены модули 2020-04-04 19:14:08 +07:00
Gravit
e226cb397d
Merge branch 'dev' of github.com:GravitLauncher/Launcher into dev 2020-04-04 17:30:26 +07:00
Gravit
c49093cb31
[ANY] Обновлены модули 2020-04-04 17:30:12 +07:00
Zaxar163
0bda3a9dbd [REFACTOR] Удалены все Depcreated. 2020-04-04 10:28:15 +03:00
Gravit
77c797f906
[ANY] Обновлены модули 2020-04-04 13:27:49 +07:00
Gravit
6c873f71c9
[FIX] JavaFX swing library for java 9+ 2020-04-04 13:25:27 +07:00
Gravit
a2ec922d20
[ANY] Обновлены модули 2020-04-03 23:10:16 +07:00
Gravit
95e712e64e
[ANY] Обновлены модули 2020-04-03 19:24:19 +07:00
Gravit
2653a0da89
[ANY] Обновлены модули 2020-04-03 19:14:52 +07:00
Gravit
69ee06727a
[ANY] Обновлены модули 2020-04-03 18:05:42 +07:00
Gravit
e3bc798865
[ANY] Обновлены модули 2020-04-03 17:46:37 +07:00
Gravit
fabb2efe39
[ANY] Обновлены модули 2020-04-03 17:44:58 +07:00
Gravit
30cd1f21f0
[FEATURE] ClientProcessBuilder events 2020-04-03 17:44:25 +07:00
Gravit
eef76a2b4e
[ANY] Обновлены модули 2020-04-03 16:03:49 +07:00
Gravit
7598ba5d62
[FEATURE] New ClientProcess Events 2020-04-03 15:13:49 +07:00
Egor Koleda
edfc248351
Merge pull request #369 from GravitLauncher/feature/actions
Feature/actions
2020-04-01 22:58:30 +03:00
Gravit
775712d898
[FIX] ServerWrapper config auth_id final 2020-04-01 11:24:20 +07:00
Egor Koleda
d2ef0b3aff
[ANY] change cache key suffix (oops) and publish to releases 2020-03-31 18:55:02 +03:00
Egor Koleda
d3c14be4b1
[ANY] remove all other CI's :) 2020-03-31 18:19:52 +03:00
Egor Koleda
1587369ec5
[ANY] added artifacts creation & uploading 2020-03-31 18:00:15 +03:00
Egor Koleda
12583aec89
[ANY] added recursive submodule fetching to actions 2020-03-31 17:49:53 +03:00
Egor Koleda
6125d1c323
[ANY] added actions workflow 2020-03-31 17:43:36 +03:00
Egor Koleda
6dea2f81c0
[ANY] change submodule link type 2020-03-31 17:38:46 +03:00
Gravit
44f3a17973
[FIX] Hibernate reload fix 2020-03-31 11:12:54 +07:00
Gravit
56e1920a8d
[FEATURE][EXPERIMENTAL] Flags in AuthProvider 2020-03-31 10:53:48 +07:00
Gravit
0692fe367f
[FEATURE][EXPERIMENTAL] New permissions system 2020-03-30 10:58:17 +07:00
Gravit
8e1cf303ac
[FEATURE] Update guard interface 2020-03-30 09:52:00 +07:00
Gravit
144b15701b
[FEATURE] getAllUsers fix 2020-03-28 06:43:43 +07:00
Gravit
3201d2fc4f
[FEATURE] SaveProfilesCommand 2020-03-27 12:32:00 +07:00
Gravit
a9867d57c4
[FIX] launch4j build 2020-03-27 10:55:02 +07:00
Gravit
1f8a81ef58
[ANY] Обновлены библиотеки 2020-03-27 10:30:06 +07:00
Gravit
88966edffb
[ANY] 5.1.3 DEV 2020-03-27 10:17:25 +07:00
Gravit
def886b0d6
Merge tag 'v5.1.2' into dev
5.1.2 beta
2020-03-27 10:12:39 +07:00
692 changed files with 24675 additions and 16604 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

79
.github/workflows/push.yml vendored Normal file
View file

@ -0,0 +1,79 @@
name: push
on: push
jobs:
launcher:
name: Launcher
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive
- name: Cache Gradle
uses: actions/cache@v4
with:
path: ~/.gradle/caches
key: gravit-${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }}-launcher
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: 21
distribution: temurin
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build with Gradle
run: ./gradlew build
- name: Generate and submit dependency graph
uses: gradle/actions/dependency-submission@417ae3ccd767c252f5661f1ace9f835f9654f2b5
- name: Create artifacts
run: |
mkdir -p artifacts/modules
cd LaunchServer/build/libs
zip -r -9 ../../../artifacts/libraries.zip * -x "LaunchServer.jar" -x "LaunchServer-clean.jar"
cp LaunchServer.jar ../../../artifacts/LaunchServer.jar
cd ../../..
cp ServerWrapper/build/libs/ServerWrapper.jar artifacts/ServerWrapper.jar
cp LauncherAuthlib/build/libs/LauncherAuthlib.jar artifacts/LauncherAuthlib.jar || true
cp modules/*_module/build/libs/*.jar artifacts/modules || true
cp modules/*_lmodule/build/libs/*.jar artifacts/modules || true
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: Launcher
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
id: create_release
uses: softprops/action-gh-release@v2
if: startsWith(github.event.ref, 'refs/tags')
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Список настроек тута: https://github.com/softprops/action-gh-release#-customizing
# Можно сделать пуш описания релиза из файла
with:
name: GravitLauncher ${{ env.LAUNCHER_VERSION }}
draft: false
prerelease: false
files: |
artifacts/*

1
.gitignore vendored
View file

@ -107,5 +107,6 @@ buildnumber
*.directory *.directory
cmd.bat cmd.bat
cmd.sh cmd.sh
project/target
## PVS Studio ## PVS Studio
.PVS-Studio/ .PVS-Studio/

View file

@ -1,90 +0,0 @@
image: gradle:jdk11
stages:
- build
- test
- deploy
variables:
GRADLE_OPTS: "-Dorg.gradle.daemon=false"
before_script:
- apt-get -y update
- 'which zip || ( apt-get -y install zip )'
- 'which git || ( apt-get -y install git )'
- export GRADLE_USER_HOME=`pwd`/.gradle
- chmod +x gradlew
- 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
- eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY" | base64 -d | ssh-add - > /dev/null
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
- echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config
- git submodule sync
- mv modules modules_cache || true
- git submodule update --init --recursive --force
- cp -a modules_cache/* modules/ || true
build:
stage: build
script:
- gradle assemble
after_script:
- mkdir -p artifacts/modules
- cd LaunchServer/build/libs/
- zip -r -9 ../../../artifacts/libraries.zip * -x "LaunchServer.jar" -x "LaunchServer-clean.jar"
- cp LaunchServer.jar ../../../artifacts/LaunchServer.jar
- cd ../../../ServerWrapper/build/libs
- cp ServerWrapper.jar ../../../artifacts/ServerWrapper.jar
- cd ../../../LauncherAuthlib/build/libs
- cp LauncherAuthlib.jar ../../../artifacts/LauncherAuthlib.jar
- cd ../../../
- cp modules/*_module/build/libs/*.jar artifacts/modules
- cp modules/*_swmodule/build/libs/*.jar artifacts/modules
- cp modules/*_lmodule/build/libs/*.jar artifacts/modules
cache:
key: "$CI_COMMIT_REF_NAME"
paths:
- .gradle
- LaunchServer/build
- Launcher/build
- LauncherCore/build
- LauncherAPI/build
- LauncherAuthlib/build
- modules/*_*module/build
artifacts:
expire_in: 6 week
paths:
- artifacts
test:
stage: test
script:
- gradle check
cache:
key: "$CI_COMMIT_REF_NAME"
policy: pull
paths:
- .gradle
- LaunchServer/build
- Launcher/build
- LauncherCore/build
- LauncherAPI/build
- LauncherAuthlib/build
- modules/*_*module/build
deploy-demo:
stage: deploy
only: [dev]
script:
- gradle build
- eval $(ssh $SSH_USER@$SSH_HOST 'cd $SSH_DIR && cat deploy.sh')
cache:
key: "$CI_COMMIT_REF_NAME"
policy: pull
paths:
- .gradle
- LaunchServer/build
- Launcher/build
- LauncherCore/build
- LauncherAPI/build
- LauncherAuthlib/build
- modules/*_*module/build

2
.gitmodules vendored
View file

@ -1,3 +1,3 @@
[submodule "modules"] [submodule "modules"]
path = modules path = modules
url = git@github.com:GravitLauncher/LauncherModules.git url = https://github.com/GravitLauncher/LauncherModules.git

View file

@ -1,22 +0,0 @@
language: java
dist: trusty
jdk:
- openjdk11
# Use https (public access) instead of git for git-submodules. This modifies only Travis-CI behavior!
# disable the default submodule logic
git:
submodules: false
# use sed to replace the SSH URL with the public URL, then init and update submodules
before_install:
- sed -i 's/git@github.com:/https:\/\/github.com\//' .gitmodules
- git submodule update --init --recursive
# gradle
before_cache:
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
- rm -fr $HOME/.gradle/caches/*/plugin-resolution/
cache:
directories:
- $HOME/.gradle/caches/
- $HOME/.gradle/wrapper/
script:
- ./gradlew build

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 = '1.8' sourceCompatibility = '21'
targetCompatibility = '1.8' 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 {
@ -33,9 +40,7 @@
from(parent.childProjects.Launcher.tasks.genRuntimeJS) from(parent.childProjects.Launcher.tasks.genRuntimeJS)
manifest.attributes("Main-Class": mainClassName, manifest.attributes("Main-Class": mainClassName,
"Premain-Class": mainAgentName, "Premain-Class": mainAgentName,
"Can-Redefine-Classes": "true", "Multi-Release": "true",
"Can-Retransform-Classes": "true",
"Can-Set-Native-Method-Prefix": "true"
) )
} }
@ -46,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
} }
@ -70,97 +73,71 @@ task cleanjar(type: Jar, dependsOn: jar) {
dependencies { dependencies {
pack project(':LauncherAPI') pack project(':LauncherAPI')
bundle group: 'org.fusesource.jansi', name:'jansi', version: rootProject['verJansi'] bundle group: 'me.tongfei', name: 'progressbar', version: '0.10.1'
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: 'mysql', name: 'mysql-connector-java', version: rootProject['verMySQLConn'] bundle group: 'org.mariadb.jdbc', name: 'mariadb-java-client', version: rootProject['verMariaDBConn']
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-linux') { 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 {
@ -175,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'
@ -184,9 +161,9 @@ task dumpClientLibs(type: Copy) {
} }
developers { developers {
developer { developer {
id = 'gravit' id = 'gravita'
name = 'Gravit' name = 'Gravita'
email = 'gravit.min@ya.ru' email = 'gravita@gravit.pro'
} }
developer { developer {
id = 'zaxar163' id = 'zaxar163'
@ -198,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,55 +1,440 @@
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.manangers.CertificateManager; import pro.gravit.launchserver.helper.SignHelper;
import pro.gravit.launchserver.manangers.MirrorManager; import pro.gravit.launchserver.launchermodules.LauncherModuleLoader;
import pro.gravit.launchserver.manangers.ReconfigurableManager; import pro.gravit.launchserver.manangers.*;
import pro.gravit.launchserver.manangers.SessionManager;
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 {
/**
* Working folder path
*/
public final Path dir;
/**
* Environment type (test / production)
*/
public final LaunchServerEnv env;
/**
* The path to the folder with libraries for the launcher
*/
public final Path launcherLibraries;
/**
* The path to the folder with compile-only libraries for the launcher
*/
public final Path launcherLibrariesCompile;
public final Path launcherPack;
/**
* The path to the folder with updates/webroot
*/
@Deprecated
public final Path updatesDir;
// Constant paths
/**
* Save/Reload LaunchServer config
*/
public final LaunchServerConfigManager launchServerConfigManager;
/**
* 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;
/**
* Pipeline for building JAR
*/
public final JARLauncherBinary launcherBinary;
/**
* Pipeline for building EXE
*/
public final LauncherBinary launcherEXEBinary;
// Server config
public final AuthHookManager authHookManager;
public final LaunchServerModulesManager modulesManager;
// Launcher binary
public final MirrorManager mirrorManager;
public final AuthManager authManager;
public final ReconfigurableManager reconfigurableManager;
public final ConfigManager configManager;
public final FeaturesManager featuresManager;
public final KeyAgreementManager keyAgreementManager;
public final UpdatesManager updatesManager;
// HWID ban + anti-brutforce
public final CertificateManager certificateManager;
// Server
public final CommandHandler commandHandler;
public final NettyServerSocketHandler nettyServerSocketHandler;
public final SocketCommandServer socketCommandServer;
public final ScheduledExecutorService service;
public final AtomicBoolean started = new AtomicBoolean(false);
public final LauncherModuleLoader launcherModuleLoader;
private final Logger logger = LogManager.getLogger();
public final int shardId;
public LaunchServerConfig config;
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.tmpDir = directories.tmpDir;
this.env = env;
this.config = config;
this.launchServerConfigManager = launchServerConfigManager;
this.modulesManager = modulesManager;
this.updatesDir = directories.updatesDir;
this.keyAgreementManager = keyAgreementManager;
this.commandHandler = commandHandler;
this.runtime = runtimeConfig;
this.certificateManager = certificateManager;
this.service = Executors.newScheduledThreadPool(config.netty.performance.schedulerThread);
launcherLibraries = directories.launcherLibrariesDir;
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);
modulesManager.invokeEvent(new NewLaunchServerInstanceEvent(this));
// Print keypair fingerprints
runtime.verify();
config.verify();
// build hooks, anti-brutforce and other
mirrorManager = new MirrorManager();
reconfigurableManager = new ReconfigurableManager();
authHookManager = new AuthHookManager();
configManager = new ConfigManager();
featuresManager = new FeaturesManager(this);
authManager = new AuthManager(this);
updatesManager = new UpdatesManager(this);
RestoreResponse.registerProviders(this);
config.init(ReloadType.FULL);
registerObject("launchServer", this);
pro.gravit.launchserver.command.handler.CommandHandler.registerCommands(commandHandler, this);
// init modules
modulesManager.invokeEvent(new LaunchServerInitPhase(this));
// Set launcher EXE binary
launcherBinary = new JARLauncherBinary(this);
launcherEXEBinary = binary();
launcherBinary.init();
launcherEXEBinary.init();
syncLauncherBinaries();
launcherModuleLoader = new LauncherModuleLoader(this);
if (config.components != null) {
logger.debug("Init components");
config.components.forEach((k, v) -> {
logger.debug("Init component {}", k);
v.setComponentName(k);
v.init(this);
});
logger.debug("Init components successful");
}
launcherModuleLoader.init();
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
modulesManager.invokeEvent(new LaunchServerPostInitPhase(this));
}
public void reload(ReloadType type) throws Exception {
config.close(type);
Map<String, AuthProviderPair> pairs = null;
if (type.equals(ReloadType.NO_AUTH)) {
pairs = config.auth;
}
logger.info("Reading LaunchServer config file");
config = launchServerConfigManager.readConfig();
config.setLaunchServer(this);
if (type.equals(ReloadType.NO_AUTH)) {
config.auth = pairs;
}
config.verify();
config.init(type);
if (type.equals(ReloadType.FULL) && config.components != null) {
logger.debug("Init components");
config.components.forEach((k, v) -> {
logger.debug("Init component {}", k);
v.setComponentName(k);
v.init(this);
});
logger.debug("Init components successful");
}
if(!type.equals(ReloadType.NO_AUTH)) {
nettyServerSocketHandler.nettyServer.service.forEachActiveChannels((channel, wsHandler) -> {
Client client = wsHandler.getClient();
if(client.auth != null) {
client.auth = config.getAuthProviderPair(client.auth_id);
}
});
}
}
@Override
public Map<String, Command> getCommands() {
Map<String, Command> commands = new HashMap<>();
SubCommand reload = new SubCommand("[type]", "reload launchserver config") {
@Override
public void invoke(String... args) throws Exception {
if (args.length == 0) {
reload(ReloadType.FULL);
return;
}
switch (args[0]) {
case "full" -> reload(ReloadType.FULL);
case "no_components" -> reload(ReloadType.NO_COMPONENTS);
default -> reload(ReloadType.NO_AUTH);
}
}
};
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;
}
public void checkCertificateExpired() {
if(!config.sign.enabled) {
return;
}
try {
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) {
logger.error("Can't get certificate expire date", e);
}
}
private LauncherBinary binary() {
LaunchServerLauncherExeInit event = new LaunchServerLauncherExeInit(this, null);
modulesManager.invokeEvent(event);
if(event.binary != null) {
return event.binary;
}
return new EXELauncherBinary(this);
}
public void buildLauncherBinaries() throws IOException {
launcherBinary.build();
launcherEXEBinary.build();
}
public void close() throws Exception {
service.shutdownNow();
logger.info("Close server socket");
nettyServerSocketHandler.close();
// Close handlers & providers
config.close(ReloadType.FULL);
modulesManager.invokeEvent(new ClosePhase());
logger.info("Save LaunchServer runtime config");
launchServerConfigManager.writeRuntimeConfig(runtime);
// Print last message before death :(
logger.info("LaunchServer stopped");
}
@Deprecated
public Set<ClientProfile> getProfiles() {
return config.profileProvider.getProfiles();
}
@Deprecated
public void setProfiles(Set<ClientProfile> profilesList) {
throw new UnsupportedOperationException();
}
public void rebindNettyServerSocket() {
nettyServerSocketHandler.close();
CommonHelper.newThread("Netty Server Socket Thread", false, nettyServerSocketHandler).start();
}
@Override
public void run() {
if (started.getAndSet(true))
throw new IllegalStateException("LaunchServer has been already started");
// Add shutdown hook, then start LaunchServer
if (!this.env.equals(LaunchServerEnv.TEST)) {
JVMHelper.RUNTIME.addShutdownHook(CommonHelper.newThread(null, false, () -> {
try {
close();
} catch (Exception e) {
logger.error("LaunchServer close error", e);
}
}));
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)
rebindNettyServerSocket();
try {
modulesManager.fullInitializedLaunchServer(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 {
logger.info("Syncing launcher binaries");
// Syncing launcher binary
logger.info("Syncing launcher binary file");
if (!launcherBinary.sync()) logger.warn("Missing launcher binary file");
// Syncing launcher EXE binary
logger.info("Syncing launcher EXE binary file");
if (!launcherEXEBinary.sync())
logger.warn("Missing launcher EXE binary file");
}
public void syncProfilesDir() throws IOException {
logger.info("Syncing profiles dir");
config.profileProvider.sync();
if (config.netty.sendProfileUpdatesEvent) {
sendUpdateProfilesEvent();
}
}
private void sendUpdateProfilesEvent() {
if (nettyServerSocketHandler == null || nettyServerSocketHandler.nettyServer == null || nettyServerSocketHandler.nettyServer.service == null) {
return;
}
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 {
updatesManager.syncUpdatesDir(dirs);
}
public void registerObject(String name, Object object) {
if (object instanceof Reconfigurable) {
reconfigurableManager.registerReconfigurable(name, (Reconfigurable) object);
}
}
public void unregisterObject(String name, Object object) {
if (object instanceof Reconfigurable) {
reconfigurableManager.unregisterReconfigurable(name);
}
}
public enum ReloadType { public enum ReloadType {
NO_AUTH, NO_AUTH,
@ -74,496 +459,44 @@ public interface LaunchServerConfigManager {
void writeRuntimeConfig(LaunchServerRuntimeConfig config) throws IOException; void writeRuntimeConfig(LaunchServerRuntimeConfig config) throws IOException;
} }
public void reload(ReloadType type) throws Exception {
config.close(type);
Map<String, AuthProviderPair> pairs = null;
if (type.equals(ReloadType.NO_AUTH)) {
pairs = config.auth;
}
LogHelper.info("Reading LaunchServer config file");
config = launchServerConfigManager.readConfig();
config.setLaunchServer(this);
if (type.equals(ReloadType.NO_AUTH)) {
config.auth = pairs;
}
config.verify();
config.init(type);
if (type.equals(ReloadType.FULL) && 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");
LogHelper.debug("Init components");
config.components.forEach((k, v) -> {
LogHelper.subDebug("Init component %s", k);
registerObject("component.".concat(k), v);
v.init(this);
});
LogHelper.debug("Init components successful");
LogHelper.debug("PostInit components");
config.components.forEach((k, v) -> {
LogHelper.subDebug("PostInit component %s", k);
v.postInit(this);
});
LogHelper.debug("PostInit components successful");
}
}
@Override
public Map<String, Command> getCommands() {
Map<String, Command> commands = new HashMap<>();
SubCommand reload = new SubCommand() {
@Override
public void invoke(String... args) throws Exception {
if (args.length == 0) {
reload(ReloadType.FULL);
return;
}
switch (args[0]) {
case "full":
reload(ReloadType.FULL);
break;
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);
return commands;
}
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);
}
}
// Constant paths
public final Path dir;
public final LaunchServerEnv env;
public final Path launcherLibraries;
public final Path launcherLibrariesCompile;
public final Path caCertFile;
public final Path caKeyFile;
public final Path serverCertFile;
public final Path serverKeyFile;
public final Path updatesDir;
public final LaunchServerConfigManager launchServerConfigManager;
//public static LaunchServer server = null;
public final Path profilesDir;
// Server config
public LaunchServerConfig config;
public final LaunchServerRuntimeConfig runtime;
public final ECPublicKey publicKey;
public final ECPrivateKey privateKey;
// Launcher binary
public final JARLauncherBinary launcherBinary;
public final Class<? extends LauncherBinary> launcherEXEBinaryClass;
public final LauncherBinary launcherEXEBinary;
// HWID ban + anti-brutforce
public final SessionManager sessionManager;
public final AuthHookManager authHookManager;
// Server
public final LaunchServerModulesManager modulesManager;
public final MirrorManager mirrorManager;
public final ReconfigurableManager reconfigurableManager;
public final ConfigManager configManager;
public final CertificateManager certificateManager;
public final ProguardConf proguardConf;
public final CommandHandler commandHandler;
public final NettyServerSocketHandler nettyServerSocketHandler;
private final AtomicBoolean started = new AtomicBoolean(false);
// Updates and profiles
private volatile List<ClientProfile> profilesList;
public volatile Map<String, HashedDir> updatesDirMap;
public final Timer taskPool;
public static final Class<? extends LauncherBinary> defaultLauncherEXEBinaryClass = null;
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);
public LaunchServer(LaunchServerDirectories directories, LaunchServerEnv env, LaunchServerConfig config, LaunchServerRuntimeConfig runtimeConfig, LaunchServerConfigManager launchServerConfigManager, LaunchServerModulesManager modulesManager, ECPublicKey publicKey, ECPrivateKey privateKey, CommandHandler commandHandler, CertificateManager certificateManager) throws IOException { if (modules == null) modules = getPath(MODULES);
this.dir = directories.dir; if (launcherModules == null) launcherModules = getPath(LAUNCHER_MODULES);
this.env = env; if (librariesDir == null) librariesDir = getPath(LIBRARIES);
this.config = config; if (controlFile == null) controlFile = getPath(CONTROL_FILE);
this.launchServerConfigManager = launchServerConfigManager; if (tmpDir == null)
this.modulesManager = modulesManager; tmpDir = Paths.get(System.getProperty("java.io.tmpdir")).resolve("launchserver-%s".formatted(SecurityHelper.randomStringToken()));
this.profilesDir = directories.profilesDir;
this.updatesDir = directories.updatesDir;
this.publicKey = publicKey;
this.privateKey = privateKey;
this.commandHandler = commandHandler;
this.runtime = runtimeConfig;
this.certificateManager = certificateManager;
taskPool = new Timer("Timered task worker thread", true);
launcherLibraries = directories.launcherLibrariesDir;
launcherLibrariesCompile = directories.launcherLibrariesCompileDir;
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));
// Print keypair fingerprints
// Load class bindings.
launcherEXEBinaryClass = defaultLauncherEXEBinaryClass;
runtime.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 private Path getPath(String dirName) {
proguardConf = new ProguardConf(this); String property = System.getProperty("launchserver.dir." + dirName, null);
sessionManager = new SessionManager(); if (property == null) return dir.resolve(dirName);
mirrorManager = new MirrorManager(); else return Paths.get(property);
reconfigurableManager = new ReconfigurableManager();
authHookManager = new AuthHookManager();
configManager = new ConfigManager();
//Generate or set new Certificate API
certificateManager.orgName = config.projectName;
if (config.certificate != null && config.certificate.enabled) {
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);
registerObject("launchServer", this);
GarbageManager.registerNeedGC(sessionManager);
pro.gravit.launchserver.command.handler.CommandHandler.registerCommands(commandHandler, this);
// init modules
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
launcherBinary = new JARLauncherBinary(this);
launcherEXEBinary = binary();
launcherBinary.init();
launcherEXEBinary.init();
syncLauncherBinaries();
// Sync updates dir
if (!IOHelper.isDir(updatesDir))
Files.createDirectory(updatesDir);
syncUpdatesDir(null);
// Sync profiles dir
if (!IOHelper.isDir(profilesDir))
Files.createDirectory(profilesDir);
syncProfilesDir();
nettyServerSocketHandler = new NettyServerSocketHandler(this);
// post init modules
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");
}
}
private LauncherBinary binary() {
if (launcherEXEBinaryClass != null) {
try {
return (LauncherBinary) MethodHandles.publicLookup().findConstructor(launcherEXEBinaryClass, MethodType.methodType(void.class, LaunchServer.class)).invoke(this);
} catch (Throwable e) {
LogHelper.error(e);
}
}
try {
Class.forName("net.sf.launch4j.Builder");
if (config.launch4j.enabled) return new EXEL4JLauncherBinary(this);
} catch (ClassNotFoundException ignored) {
LogHelper.warning("Launch4J isn't in classpath.");
}
return new EXELauncherBinary(this);
}
public void buildLauncherBinaries() throws IOException {
launcherBinary.build();
launcherEXEBinary.build();
}
public void close() throws Exception {
// Close handlers & providers
config.close(ReloadType.FULL);
modulesManager.invokeEvent(new ClosePhase());
LogHelper.info("Save LaunchServer runtime config");
launchServerConfigManager.writeRuntimeConfig(runtime);
// Print last message before death :(
LogHelper.info("LaunchServer stopped");
}
public List<ClientProfile> getProfiles() {
return profilesList;
}
public void setProfiles(List<ClientProfile> profilesList) {
this.profilesList = Collections.unmodifiableList(profilesList);
}
public HashedDir getUpdateDir(String name) {
return updatesDirMap.get(name);
}
public Set<Entry<String, HashedDir>> getUpdateDirs() {
return updatesDirMap.entrySet();
}
public void rebindNettyServerSocket() {
nettyServerSocketHandler.close();
CommonHelper.newThread("Netty Server Socket Thread", false, nettyServerSocketHandler).start();
}
@Override
public void run() {
if (started.getAndSet(true))
throw new IllegalStateException("LaunchServer has been already started");
// Add shutdown hook, then start LaunchServer
if (!this.env.equals(LaunchServerEnv.TEST)) {
JVMHelper.RUNTIME.addShutdownHook(CommonHelper.newThread(null, false, () -> {
try {
close();
} catch (Exception e) {
LogHelper.error(e);
}
}));
CommonHelper.newThread("Command Thread", true, commandHandler).start();
}
if (config.netty != null)
rebindNettyServerSocket();
modulesManager.fullInitializedLaunchServer(this);
modulesManager.invokeEvent(new LaunchServerFullInitEvent(this));
}
public void syncLauncherBinaries() throws IOException {
LogHelper.info("Syncing launcher binaries");
// Syncing launcher binary
LogHelper.info("Syncing launcher binary file");
if (!launcherBinary.sync()) LogHelper.warning("Missing launcher binary file");
// Syncing launcher EXE binary
LogHelper.info("Syncing launcher EXE binary file");
if (!launcherEXEBinary.sync() && config.launch4j.enabled)
LogHelper.warning("Missing launcher EXE binary file");
}
public void syncProfilesDir() throws IOException {
LogHelper.info("Syncing profiles dir");
List<ClientProfile> newProfies = new LinkedList<>();
IOHelper.walk(profilesDir, new ProfilesFileVisitor(newProfies), false);
// Sort and set new profiles
newProfies.sort(Comparator.comparing(a -> a));
profilesList = Collections.unmodifiableList(newProfies);
}
public void syncUpdatesDir(Collection<String> dirs) throws IOException {
LogHelper.info("Syncing updates dir");
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) {
if (object instanceof Reconfigurable) {
reconfigurableManager.registerReconfigurable(name, (Reconfigurable) object);
}
if (object instanceof NeedGarbageCollection) {
GarbageManager.registerNeedGC((NeedGarbageCollection) object);
}
}
public void unregisterObject(String name, Object object) {
if (object instanceof Reconfigurable) {
reconfigurableManager.unregisterReconfigurable(name);
}
if (object instanceof NeedGarbageCollection) {
GarbageManager.unregisterNeedGC((NeedGarbageCollection) object);
}
}
public void fullyRestart() {
restart();
JVMHelper.RUNTIME.exit(0);
} }
} }

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,37 +67,47 @@ 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();
@Override
public LaunchServerConfig readConfig() {
throw new UnsupportedOperationException();
}
@Override
public LaunchServerRuntimeConfig readRuntimeConfig() {
throw new UnsupportedOperationException();
}
@Override
public void writeConfig(LaunchServerConfig config) {
throw new UnsupportedOperationException();
}
@Override
public void writeRuntimeConfig(LaunchServerRuntimeConfig config) {
throw new UnsupportedOperationException();
}
};
} }
return new LaunchServer(directories, env, config, runtimeConfig, launchServerConfigManager, modulesManager, publicKey, privateKey, commandHandler, certificateManager); 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) { public LaunchServerBuilder setCertificateManager(CertificateManager certificateManager) {
this.certificateManager = certificateManager; this.certificateManager = certificateManager;
return this; return this;
} }
public void setKeyAgreementManager(KeyAgreementManager keyAgreementManager) {
this.keyAgreementManager = keyAgreementManager;
}
private static class NullLaunchServerConfigManager implements LaunchServer.LaunchServerConfigManager {
@Override
public LaunchServerConfig readConfig() {
throw new UnsupportedOperationException();
}
@Override
public LaunchServerRuntimeConfig readRuntimeConfig() {
throw new UnsupportedOperationException();
}
@Override
public void writeConfig(LaunchServerConfig config) {
throw new UnsupportedOperationException();
}
@Override
public void writeRuntimeConfig(LaunchServerRuntimeConfig config) {
throw new UnsupportedOperationException();
}
}
} }

View file

@ -1,17 +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.modules.events.PreConfigPhase; import pro.gravit.launcher.core.LauncherTrustManager;
import pro.gravit.launcher.request.auth.AuthRequest; import pro.gravit.launcher.base.modules.events.PreConfigPhase;
import pro.gravit.launchserver.auth.handler.AuthHandler; import pro.gravit.launcher.base.profiles.optional.actions.OptionalAction;
import pro.gravit.launcher.base.profiles.optional.triggers.OptionalTrigger;
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.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;
@ -22,64 +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 pro.gravit.launcher.LauncherTrustManager;
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; Security.addProvider(new BouncyCastleProvider());
ECPrivateKey privateKey; } catch (ClassNotFoundException | NoClassDefFoundError ex) {
Security.addProvider(new BouncyCastleProvider()); 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 {
@ -96,104 +105,61 @@ 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);
@Override
public LaunchServerConfig readConfig() throws IOException {
LaunchServerConfig config1;
try (BufferedReader reader = IOHelper.newReader(configFile)) {
config1 = Launcher.gsonManager.gson.fromJson(reader, LaunchServerConfig.class);
}
return config1;
}
@Override
public LaunchServerRuntimeConfig readRuntimeConfig() throws IOException {
LaunchServerRuntimeConfig config1;
try (BufferedReader reader = IOHelper.newReader(runtimeConfigFile)) {
config1 = Launcher.gsonManager.gson.fromJson(reader, LaunchServerRuntimeConfig.class);
}
return config1;
}
@Override
public void writeConfig(LaunchServerConfig config) throws IOException {
try (Writer writer = IOHelper.newWriter(configFile)) {
if (Launcher.gsonManager.configGson != null) {
Launcher.gsonManager.configGson.toJson(config, writer);
} else {
LogHelper.error("Error writing LaunchServer runtime config file. Gson is null");
}
}
}
@Override
public void writeRuntimeConfig(LaunchServerRuntimeConfig config) throws IOException {
try (Writer writer = IOHelper.newWriter(runtimeConfigFile)) {
if (Launcher.gsonManager.configGson != null) {
Launcher.gsonManager.configGson.toJson(config, writer);
} else {
LogHelper.error("Error writing LaunchServer runtime config file. Gson is null");
}
}
}
};
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() LaunchServer server = new LaunchServerBuilder()
.setDirectories(directories) .setDirectories(directories)
.setEnv(env) .setEnv(env)
.setCommandHandler(localCommandHandler) .setCommandHandler(localCommandHandler)
.setPrivateKey(privateKey)
.setPublicKey(publicKey)
.setRuntimeConfig(runtimeConfig) .setRuntimeConfig(runtimeConfig)
.setConfig(config) .setConfig(config)
.setModulesManager(modulesManager) .setModulesManager(modulesManager)
.setLaunchServerConfigManager(launchServerConfigManager) .setLaunchServerConfigManager(launchServerConfigManager)
.setCertificateManager(certificateManager) .setCertificateManager(certificateManager)
.build(); .build();
server.run(); 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) { public static void initGson(LaunchServerModulesManager modulesManager) {
@ -202,15 +168,39 @@ public static void initGson(LaunchServerModulesManager modulesManager) {
} }
public static void registerAll() { public static void registerAll() {
AuthCoreProvider.registerProviders();
AuthHandler.registerHandlers(); PasswordVerifier.registerProviders();
AuthProvider.registerProviders();
TextureProvider.registerProviders(); TextureProvider.registerProviders();
Component.registerComponents(); Component.registerComponents();
ProtectHandler.registerHandlers(); ProtectHandler.registerHandlers();
WebSocketService.registerResponses(); WebSocketService.registerResponses();
DaoProvider.registerProviders();
AuthRequest.registerProviders(); 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 { public static void generateConfigIfNotExists(Path configFile, CommandHandler commandHandler, LaunchServer.LaunchServerEnv env) throws IOException {
@ -218,7 +208,7 @@ public static void generateConfigIfNotExists(Path configFile, CommandHandler com
return; return;
// Create new config // Create new config
LogHelper.info("Creating LaunchServer config"); logger.info("Creating LaunchServer config");
LaunchServerConfig newConfig = LaunchServerConfig.getDefault(env); LaunchServerConfig newConfig = LaunchServerConfig.getDefault(env);
@ -228,29 +218,113 @@ public static void generateConfigIfNotExists(Path configFile, CommandHandler com
address = "localhost"; address = "localhost";
newConfig.setProjectName("test"); newConfig.setProjectName("test");
} else { } else {
System.out.println("LaunchServer address(default: localhost): "); address = System.getenv("ADDRESS");
address = commandHandler.readLine(); if (address == null) {
System.out.println("LaunchServer projectName: "); address = System.getProperty("launchserver.address", null);
newConfig.setProjectName(commandHandler.readLine()); }
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()) { if (address == null || address.isEmpty()) {
LogHelper.error("Address null. Using localhost"); logger.error("Address null. Using localhost:9274");
address = "localhost"; address = "localhost:9274";
} }
if (newConfig.projectName == null || newConfig.projectName.isEmpty()) { if (newConfig.projectName == null || newConfig.projectName.isEmpty()) {
LogHelper.error("ProjectName null. Using MineCraft"); logger.error("ProjectName null. Using MineCraft");
newConfig.projectName = "MineCraft"; newConfig.projectName = "MineCraft";
} }
int port = 9274;
newConfig.netty.address = "ws://" + address + ":9274/api"; if(address.contains(":")) {
newConfig.netty.downloadURL = "http://" + address + ":9274/%dirname%/"; String portString = address.substring(address.indexOf(':')+1);
newConfig.netty.launcherURL = "http://" + address + ":9274/Launcher.jar"; try {
newConfig.netty.launcherEXEURL = "http://" + address + ":9274/Launcher.exe"; 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 // Write LaunchServer config
LogHelper.info("Writing LaunchServer config file"); logger.info("Writing LaunchServer config file");
try (BufferedWriter writer = IOHelper.newWriter(configFile)) { try (BufferedWriter writer = IOHelper.newWriter(configFile)) {
Launcher.gsonManager.configGson.toJson(newConfig, writer); 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
public LaunchServerConfig readConfig() throws IOException {
LaunchServerConfig config1;
try (BufferedReader reader = IOHelper.newReader(configFile)) {
config1 = Launcher.gsonManager.gson.fromJson(reader, LaunchServerConfig.class);
}
return config1;
}
@Override
public LaunchServerRuntimeConfig readRuntimeConfig() throws IOException {
LaunchServerRuntimeConfig config1;
try (BufferedReader reader = IOHelper.newReader(runtimeConfigFile)) {
config1 = Launcher.gsonManager.gson.fromJson(reader, LaunchServerRuntimeConfig.class);
}
return config1;
}
@Override
public void writeConfig(LaunchServerConfig config) throws IOException {
ByteArrayOutputStream output = new ByteArrayOutputStream();
try (Writer writer = IOHelper.newWriter(output)) {
if (Launcher.gsonManager.configGson != null) {
Launcher.gsonManager.configGson.toJson(config, writer);
} else {
logger.error("Error writing LaunchServer config file. Gson is null");
}
}
byte[] bytes = output.toByteArray();
if(bytes.length > 0) {
IOHelper.write(configFile, bytes);
}
}
@Override
public void writeRuntimeConfig(LaunchServerRuntimeConfig config) throws IOException {
ByteArrayOutputStream output = new ByteArrayOutputStream();
try (Writer writer = IOHelper.newWriter(output)) {
if (Launcher.gsonManager.configGson != null) {
Launcher.gsonManager.configGson.toJson(config, writer);
} else {
logger.error("Error writing LaunchServer runtime config file. Gson is null");
}
}
byte[] bytes = output.toByteArray();
if(bytes.length > 0) {
IOHelper.write(runtimeConfigFile, bytes);
}
}
}
} }

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,55 +1,10 @@
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.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import java.util.jar.JarFile;
public final class StarterAgent { public final class StarterAgent {
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 Path filef;
private final boolean fixLib;
private StarterVisitor() {
this.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);
}
}
public static Instrumentation inst = null; public static Instrumentation inst = null;
public static Path libraries = null; public static Path libraries = null;
private static boolean isStarted = false; private static boolean isStarted = false;
@ -59,13 +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(e -> e.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);
}
} }
} }

View file

@ -18,35 +18,20 @@
* чего угодно. Работает через поиск class-файлов в classpath. * чего угодно. Работает через поиск class-файлов в classpath.
*/ */
public class ClassMetadataReader implements Closeable { public class ClassMetadataReader implements Closeable {
private static class CheckSuperClassVisitor extends ClassVisitor {
String superClassName;
public CheckSuperClassVisitor() {
super(Opcodes.ASM7);
}
@Override
public void visit(int version, int access, String name, String signature, String superName,
String[] interfaces) {
superClassName = superName;
}
}
private final List<JarFile> cp; private final List<JarFile> cp;
public ClassMetadataReader(List<JarFile> cp) { public ClassMetadataReader(List<JarFile> cp) {
this.cp = cp; this.cp = cp;
} }
public List<JarFile> getCp() {
return cp;
}
public ClassMetadataReader() { public ClassMetadataReader() {
this.cp = new ArrayList<>(); this.cp = new ArrayList<>();
} }
public List<JarFile> getCp() {
return cp;
}
public void acceptVisitor(byte[] classData, ClassVisitor visitor) { public void acceptVisitor(byte[] classData, ClassVisitor visitor) {
new ClassReader(classData).accept(visitor, 0); new ClassReader(classData).accept(visitor, 0);
} }
@ -63,7 +48,6 @@ public void acceptVisitor(String className, ClassVisitor visitor, int flags) thr
acceptVisitor(getClassData(className), visitor, flags); acceptVisitor(getClassData(className), visitor, flags);
} }
public byte[] getClassData(String className) throws IOException { public byte[] getClassData(String className) throws IOException {
for (JarFile f : cp) { for (JarFile f : cp) {
if (f.getEntry(className + ".class") != null) { if (f.getEntry(className + ".class") != null) {
@ -111,4 +95,19 @@ public void close() {
cp.clear(); cp.clear();
} }
private static class CheckSuperClassVisitor extends ClassVisitor {
String superClassName;
public CheckSuperClassVisitor() {
super(Opcodes.ASM7);
}
@Override
public void visit(int version, int access, String name, String signature, String superName,
String[] interfaces) {
superClassName = superName;
}
}
} }

View file

@ -1,245 +1,254 @@
package pro.gravit.launchserver.asm; package pro.gravit.launchserver.asm;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.AnnotationVisitor;
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.core.LauncherInject;
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 pro.gravit.launcher.LauncherInject;
import pro.gravit.launcher.LauncherInjectionConstructor; import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
public class InjectClassAcceptor implements MainBuildTask.ASMTransformer { public class InjectClassAcceptor implements MainBuildTask.ASMTransformer {
private final Map<String, Object> values; private static final List<Class<?>> primitiveLDCClasses = Arrays.asList(java.lang.Integer.class, java.lang.Long.class,
java.lang.Float.class, java.lang.Double.class, java.lang.String.class);
private static final String INJECTED_FIELD_DESC = Type.getDescriptor(LauncherInject.class);
private static final String INJECTED_CONSTRUCTOR_DESC = Type.getDescriptor(LauncherInjectionConstructor.class);
private static final List<String> primitiveLDCDescriptors = Arrays.asList(Type.INT_TYPE.getDescriptor(), Type.DOUBLE_TYPE.getDescriptor(),
Type.FLOAT_TYPE.getDescriptor(), Type.LONG_TYPE.getDescriptor(), Type.getDescriptor(String.class));
private static final Map<Class<?>, Serializer<?>> serializers;
public InjectClassAcceptor(Map<String, Object> values) { static {
this.values = values; serializers = new HashMap<>();
} serializers.put(List.class, new ListSerializer());
serializers.put(Map.class, new MapSerializer());
serializers.put(byte[].class, new ByteArraySerializer());
serializers.put(Short.class, serializerClass(Opcodes.I2S));
serializers.put(Byte.class, serializerClass(Opcodes.I2B));
serializers.put(Type.class, (Serializer<Type>) e -> { // ow.Type == java.lang.Class in LDC
InsnList ret = new InsnList();
ret.add(new LdcInsnNode(e));
return ret;
});
serializers.put(Boolean.class, (Serializer<Boolean>) e -> {
InsnList ret = new InsnList();
ret.add(new InsnNode(e ? Opcodes.ICONST_1 : Opcodes.ICONST_0));
return ret;
});
serializers.put(Character.class, (Serializer<Character>) e -> {
InsnList ret = new InsnList();
ret.add(NodeUtils.push((int) e));
ret.add(new InsnNode(Opcodes.I2C));
return ret;
});
serializers.put(Enum.class, (Serializer<Enum>) NodeUtils::makeValueEnumGetter);
}
private static final List<Class<?>> primitiveLDCClasses = Arrays.asList(java.lang.Integer.class, java.lang.Long.class, private final Map<String, Object> values;
java.lang.Float.class, java.lang.Double.class, java.lang.String.class);
private static final String INJECTED_FIELD_DESC = Type.getDescriptor(LauncherInject.class);
private static final String INJECTED_CONSTRUCTOR_DESC = Type.getDescriptor(LauncherInjectionConstructor.class);
private static final List<String> primitiveLDCDescriptors = Arrays.asList(Type.INT_TYPE.getDescriptor(), Type.DOUBLE_TYPE.getDescriptor(),
Type.FLOAT_TYPE.getDescriptor(), Type.LONG_TYPE.getDescriptor(), Type.getDescriptor(String.class));
private static void visit(ClassNode classNode, Map<String, Object> values) { public InjectClassAcceptor(Map<String, Object> values) {
MethodNode clinitMethod = classNode.methods.stream().filter(methodNode -> "<clinit>".equals(methodNode.name)) this.values = values;
.findFirst().orElseGet(() -> { }
MethodNode newClinitMethod = new MethodNode(Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC,
"<clinit>", "()V", null, null);
newClinitMethod.instructions.add(new InsnNode(Opcodes.RETURN));
classNode.methods.add(newClinitMethod);
return newClinitMethod;
});
List<MethodNode> constructors = classNode.methods.stream().filter(method -> "<init>".equals(method.name))
.collect(Collectors.toList());
MethodNode initMethod = constructors.stream().filter(method -> method.invisibleAnnotations != null
&& method.invisibleAnnotations.stream().anyMatch(annotation -> INJECTED_CONSTRUCTOR_DESC.equals(annotation.desc))).findFirst()
.orElseGet(() -> constructors.stream().filter(method -> method.desc.equals("()V")).findFirst().orElse(null));
classNode.fields.forEach(field -> {
// Notice that fields that will be used with this algo should not have default
// value by = ...;
AnnotationNode valueAnnotation = field.invisibleAnnotations != null ? field.invisibleAnnotations.stream()
.filter(annotation -> INJECTED_FIELD_DESC.equals(annotation.desc)).findFirst()
.orElse(null) : null;
if (valueAnnotation == null) {
return;
}
field.invisibleAnnotations.remove(valueAnnotation);
AtomicReference<String> valueName = new AtomicReference<String>(null);
valueAnnotation.accept(new AnnotationVisitor(Opcodes.ASM7) {
@Override
public void visit(final String name, final Object value) {
if ("value".equals(name)) {
if (value.getClass() != String.class)
throw new IllegalArgumentException(
String.format("Invalid annotation with value class %s", field.getClass().getName()));
valueName.set(value.toString());
}
}
});
if (valueName.get() == null) {
throw new IllegalArgumentException("Annotation should always contains 'value' key");
}
if (!values.containsKey(valueName.get())) {
return;
}
Object value = values.get(valueName.get());
if ((field.access & Opcodes.ACC_STATIC) != 0) {
if (primitiveLDCDescriptors.contains(field.desc) && primitiveLDCClasses.contains(value.getClass())) {
field.value = value;
return;
}
List<FieldInsnNode> putStaticNodes = Arrays.stream(clinitMethod.instructions.toArray())
.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());
InsnList setter = serializeValue(value);
if (putStaticNodes.isEmpty()) {
setter.add(new FieldInsnNode(Opcodes.PUTSTATIC, classNode.name, field.name, field.desc));
Arrays.stream(clinitMethod.instructions.toArray()).filter(node -> node.getOpcode() == Opcodes.RETURN)
.forEach(node -> clinitMethod.instructions.insertBefore(node, setter));
} else {
setter.insert(new InsnNode(Type.getType(field.desc).getSize() == 1 ? Opcodes.POP : Opcodes.POP2));
for (FieldInsnNode fieldInsnNode : putStaticNodes) {
clinitMethod.instructions.insertBefore(fieldInsnNode, setter);
}
}
} else {
if (initMethod == null) {
throw new IllegalArgumentException(String.format("Not found init in target: %s", classNode.name));
}
List<FieldInsnNode> putFieldNodes = Arrays.stream(initMethod.instructions.toArray())
.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());
InsnList setter = serializeValue(value);
if (putFieldNodes.isEmpty()) {
setter.insert(new VarInsnNode(Opcodes.ALOAD, 0));
setter.add(new FieldInsnNode(Opcodes.PUTFIELD, classNode.name, field.name, field.desc));
Arrays.stream(initMethod.instructions.toArray())
.filter(node -> node.getOpcode() == Opcodes.RETURN)
.forEach(node -> initMethod.instructions.insertBefore(node, setter));
} else {
setter.insert(new InsnNode(Type.getType(field.desc).getSize() == 1 ? Opcodes.POP : Opcodes.POP2));
for (FieldInsnNode fieldInsnNode : putFieldNodes) {
initMethod.instructions.insertBefore(fieldInsnNode, setter);
}
}
}
});
}
private static final Map<Class<?>, Serializer<?>> serializers; private static void visit(ClassNode classNode, Map<String, Object> values) {
MethodNode clinitMethod = classNode.methods.stream().filter(methodNode -> "<clinit>".equals(methodNode.name))
.findFirst().orElseGet(() -> {
MethodNode newClinitMethod = new MethodNode(Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC,
"<clinit>", "()V", null, null);
newClinitMethod.instructions.add(new InsnNode(Opcodes.RETURN));
classNode.methods.add(newClinitMethod);
return newClinitMethod;
});
List<MethodNode> constructors = classNode.methods.stream().filter(method -> "<init>".equals(method.name))
.toList();
MethodNode initMethod = constructors.stream().filter(method -> method.invisibleAnnotations != null
&& method.invisibleAnnotations.stream().anyMatch(annotation -> INJECTED_CONSTRUCTOR_DESC.equals(annotation.desc))).findFirst()
.orElseGet(() -> constructors.stream().filter(method -> method.desc.equals("()V")).findFirst().orElse(null));
classNode.fields.forEach(field -> {
// Notice that fields that will be used with this algo should not have default
// value by = ...;
boolean isStatic = (field.access & Opcodes.ACC_STATIC) != 0;
injectTo(isStatic ? clinitMethod : initMethod, classNode, field, isStatic, values);
});
}
static { public static void injectTo(MethodNode initMethod, ClassNode classNode, FieldNode field, boolean isStatic, Map<String, Object> values) {
serializers = new HashMap<>(); AnnotationNode valueAnnotation = field.invisibleAnnotations != null ? field.invisibleAnnotations.stream()
serializers.put(List.class, new ListSerializer()); .filter(annotation -> INJECTED_FIELD_DESC.equals(annotation.desc)).findFirst()
serializers.put(Map.class, new MapSerializer()); .orElse(null) : null;
serializers.put(byte[].class, new ByteArraySerializer()); if (valueAnnotation == null) {
serializers.put(Short.class, serializerClass(Opcodes.I2S)); return;
serializers.put(Byte.class, serializerClass(Opcodes.I2B)); }
serializers.put(Type.class, (Serializer<Type>) e -> { // ow.Type == java.lang.Class in LDC field.invisibleAnnotations.remove(valueAnnotation);
InsnList ret = new InsnList(); AtomicReference<String> valueName = new AtomicReference<>(null);
ret.add(new LdcInsnNode(e)); valueAnnotation.accept(new AnnotationVisitor(Opcodes.ASM7) {
return ret; @Override
}); public void visit(final String name, final Object value) {
serializers.put(Boolean.class, (Serializer<Boolean>) e -> { if ("value".equals(name)) {
InsnList ret = new InsnList(); if (value.getClass() != String.class)
ret.add(new InsnNode(e ? Opcodes.ICONST_1 : Opcodes.ICONST_0)); throw new IllegalArgumentException(
return ret; "Invalid annotation with value class %s".formatted(field.getClass().getName()));
}); valueName.set(value.toString());
serializers.put(Character.class, (Serializer<Character>) e -> { }
InsnList ret = new InsnList(); }
ret.add(NodeUtils.push((int) e)); });
ret.add(new InsnNode(Opcodes.I2C)); if (valueName.get() == null) {
return ret; throw new IllegalArgumentException("Annotation should always contains 'value' key");
}); }
serializers.put(Enum.class, (Serializer<Enum>) NodeUtils::makeValueEnumGetter); if (!values.containsKey(valueName.get())) {
} return;
}
Object value = values.get(valueName.get());
//if ((field.access & Opcodes.ACC_STATIC) != 0) {
if (isStatic) {
if (primitiveLDCDescriptors.contains(field.desc) && primitiveLDCClasses.contains(value.getClass())) {
field.value = value;
return;
}
List<FieldInsnNode> putStaticNodes = Arrays.stream(initMethod.instructions.toArray())
.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)).toList();
InsnList setter = serializeValue(value);
if (putStaticNodes.isEmpty()) {
setter.add(new FieldInsnNode(Opcodes.PUTSTATIC, classNode.name, field.name, field.desc));
Arrays.stream(initMethod.instructions.toArray()).filter(node -> node.getOpcode() == Opcodes.RETURN)
.forEach(node -> initMethod.instructions.insertBefore(node, setter));
} else {
setter.insert(new InsnNode(Type.getType(field.desc).getSize() == 1 ? Opcodes.POP : Opcodes.POP2));
for (FieldInsnNode fieldInsnNode : putStaticNodes) {
initMethod.instructions.insertBefore(fieldInsnNode, setter);
}
}
} else {
if (initMethod == null) {
throw new IllegalArgumentException("Not found init in target: %s".formatted(classNode.name));
}
List<FieldInsnNode> putFieldNodes = Arrays.stream(initMethod.instructions.toArray())
.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)).toList();
InsnList setter = serializeValue(value);
if (putFieldNodes.isEmpty()) {
setter.insert(new VarInsnNode(Opcodes.ALOAD, 0));
setter.add(new FieldInsnNode(Opcodes.PUTFIELD, classNode.name, field.name, field.desc));
Arrays.stream(initMethod.instructions.toArray())
.filter(node -> node.getOpcode() == Opcodes.RETURN)
.forEach(node -> initMethod.instructions.insertBefore(node, setter));
} else {
setter.insert(new InsnNode(Type.getType(field.desc).getSize() == 1 ? Opcodes.POP : Opcodes.POP2));
for (FieldInsnNode fieldInsnNode : putFieldNodes) {
initMethod.instructions.insertBefore(fieldInsnNode, setter);
}
}
}
}
private static Serializer<?> serializerClass(int opcode) { private static Serializer<?> serializerClass(int opcode) {
return new Serializer<Number>() { return (Serializer<Number>) value -> {
@Override InsnList ret = new InsnList();
public InsnList serialize(Number value) { ret.add(NodeUtils.push(value.intValue()));
InsnList ret = new InsnList(); ret.add(new InsnNode(opcode));
ret.add(NodeUtils.push(((Number) value).intValue())); return ret;
ret.add(new InsnNode(opcode)); };
return ret; }
}
}; @SuppressWarnings("unchecked")
} private static InsnList serializeValue(Object value) {
if (value == null) {
InsnList insnList = new InsnList();
insnList.add(new InsnNode(Opcodes.ACONST_NULL));
return insnList;
}
if (primitiveLDCClasses.contains(value.getClass())) {
InsnList insnList = new InsnList();
insnList.add(new LdcInsnNode(value));
return insnList;
}
for (Map.Entry<Class<?>, Serializer<?>> serializerEntry : serializers.entrySet()) {
if (serializerEntry.getKey().isInstance(value)) {
return ((Serializer) serializerEntry.getValue()).serialize(value);
}
}
throw new UnsupportedOperationException("Serialization of type %s is not supported".formatted(value.getClass()));
}
@FunctionalInterface public static boolean isSerializableValue(Object value) {
private interface Serializer<T> { if (value == null) return true;
InsnList serialize(T value); if (primitiveLDCClasses.contains(value.getClass())) return true;
} for (Map.Entry<Class<?>, Serializer<?>> serializerEntry : serializers.entrySet()) {
if (serializerEntry.getKey().isInstance(value)) {
return true;
}
}
return false;
}
@SuppressWarnings("unchecked") @Override
private static InsnList serializeValue(Object value) { public void transform(ClassNode classNode, String className, BuildContext context) {
if (value == null) { visit(classNode, values);
InsnList insnList = new InsnList(); }
insnList.add(new InsnNode(Opcodes.ACONST_NULL));
return insnList;
}
if (primitiveLDCClasses.contains(value.getClass())) {
InsnList insnList = new InsnList();
insnList.add(new LdcInsnNode(value));
return insnList;
}
for (Map.Entry<Class<?>, Serializer<?>> serializerEntry : serializers.entrySet()) {
if (serializerEntry.getKey().isInstance(value)) {
return ((Serializer) serializerEntry.getValue()).serialize(value);
}
}
throw new UnsupportedOperationException(String.format("Serialization of type %s is not supported",
value.getClass()));
}
private static class ListSerializer implements Serializer<List> { @FunctionalInterface
@Override private interface Serializer<T> {
public InsnList serialize(List value) { InsnList serialize(T value);
InsnList insnList = new InsnList(); }
insnList.add(new TypeInsnNode(Opcodes.NEW, Type.getInternalName(ArrayList.class)));
insnList.add(new InsnNode(Opcodes.DUP));
insnList.add(NodeUtils.push(value.size()));
insnList.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, Type.getInternalName(ArrayList.class), "<init>",
Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE), false));
for (Object object : value) {
insnList.add(new InsnNode(Opcodes.DUP));
insnList.add(serializeValue(object));
insnList.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE, Type.getInternalName(List.class), "add",
Type.getMethodDescriptor(Type.BOOLEAN_TYPE, Type.getType(Object.class)), true));
insnList.add(new InsnNode(Opcodes.POP));
}
return insnList;
}
}
private static class MapSerializer implements Serializer<Map> { private static class ListSerializer implements Serializer<List> {
@Override @Override
public InsnList serialize(Map value) { public InsnList serialize(List value) {
InsnList insnList = new InsnList(); InsnList insnList = new InsnList();
insnList.add(new TypeInsnNode(Opcodes.NEW, Type.getInternalName(value.getClass()))); insnList.add(new TypeInsnNode(Opcodes.NEW, Type.getInternalName(ArrayList.class)));
insnList.add(new InsnNode(Opcodes.DUP)); insnList.add(new InsnNode(Opcodes.DUP));
insnList.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, Type.getInternalName(value.getClass()), "<init>", insnList.add(NodeUtils.push(value.size()));
Type.getMethodDescriptor(Type.VOID_TYPE), false)); insnList.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, Type.getInternalName(ArrayList.class), "<init>",
for (Object entryObject : value.entrySet()) { Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE), false));
Map.Entry entry = (Map.Entry) entryObject; for (Object object : value) {
insnList.add(new InsnNode(Opcodes.DUP)); insnList.add(new InsnNode(Opcodes.DUP));
insnList.add(serializeValue(entry.getKey())); insnList.add(serializeValue(object));
insnList.add(serializeValue(entry.getValue())); insnList.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE, Type.getInternalName(List.class), "add",
insnList.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE, Type.getInternalName(Map.class), "put", Type.getMethodDescriptor(Type.BOOLEAN_TYPE, Type.getType(Object.class)), true));
Type.getMethodDescriptor(Type.getType(Object.class), Type.getType(Object.class), Type.getType(Object.class)), insnList.add(new InsnNode(Opcodes.POP));
true)); }
insnList.add(new InsnNode(Opcodes.POP)); return insnList;
} }
return insnList; }
}
}
private static class ByteArraySerializer implements Serializer<byte[]> { private static class MapSerializer implements Serializer<Map> {
@Override @Override
public InsnList serialize(byte[] value) { public InsnList serialize(Map value) {
InsnList insnList = new InsnList(); InsnList insnList = new InsnList();
insnList.add(new MethodInsnNode(Opcodes.INVOKESTATIC, Type.getInternalName(Base64.class), insnList.add(new TypeInsnNode(Opcodes.NEW, Type.getInternalName(value.getClass())));
"getDecoder", Type.getMethodDescriptor(Type.getType(Base64.Decoder.class)), false)); insnList.add(new InsnNode(Opcodes.DUP));
insnList.add(NodeUtils.getSafeStringInsnList(Base64.getEncoder().encodeToString(value))); insnList.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, Type.getInternalName(value.getClass()), "<init>",
insnList.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Base64.Decoder.class), Type.getMethodDescriptor(Type.VOID_TYPE), false));
"decode", Type.getMethodDescriptor(Type.getType(byte[].class), Type.getType(String.class)), for (Object entryObject : value.entrySet()) {
false)); Map.Entry entry = (Map.Entry) entryObject;
return insnList; insnList.add(new InsnNode(Opcodes.DUP));
} insnList.add(serializeValue(entry.getKey()));
} insnList.add(serializeValue(entry.getValue()));
insnList.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE, Type.getInternalName(Map.class), "put",
Type.getMethodDescriptor(Type.getType(Object.class), Type.getType(Object.class), Type.getType(Object.class)),
true));
insnList.add(new InsnNode(Opcodes.POP));
}
return insnList;
}
}
@Override private static class ByteArraySerializer implements Serializer<byte[]> {
public void transform(ClassNode classNode, String className, BuildContext context) { @Override
visit(classNode, values); public InsnList serialize(byte[] value) {
} InsnList insnList = new InsnList();
insnList.add(new MethodInsnNode(Opcodes.INVOKESTATIC, Type.getInternalName(Base64.class),
"getDecoder", Type.getMethodDescriptor(Type.getType(Base64.Decoder.class)), false));
insnList.add(NodeUtils.getSafeStringInsnList(Base64.getEncoder().encodeToString(value)));
insnList.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Base64.Decoder.class),
"decode", Type.getMethodDescriptor(Type.getType(byte[].class), Type.getType(String.class)),
false));
return insnList;
}
}
} }

View file

@ -17,6 +17,8 @@
public final class NodeUtils { public final class NodeUtils {
public static final int MAX_SAFE_BYTE_COUNT = 65535 - Byte.MAX_VALUE;
private NodeUtils() { private NodeUtils() {
} }
@ -147,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:
@ -186,8 +185,6 @@ public static InsnList getSafeStringInsnList(String string) {
return insnList; return insnList;
} }
public static final int MAX_SAFE_BYTE_COUNT = 65535 - Byte.MAX_VALUE;
public static String[] splitUtf8ToChunks(String text, int maxBytes) { public static String[] splitUtf8ToChunks(String text, int maxBytes) {
List<String> parts = new ArrayList<>(); List<String> parts = new ArrayList<>();
@ -238,9 +235,9 @@ else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE)
} }
public static InsnList makeValueEnumGetter(@SuppressWarnings("rawtypes") Enum u) { public static InsnList makeValueEnumGetter(@SuppressWarnings("rawtypes") Enum u) {
InsnList ret = new InsnList(); InsnList ret = new InsnList();
Type e = Type.getType(u.getClass()); Type e = Type.getType(u.getClass());
ret.add(new FieldInsnNode(Opcodes.GETSTATIC, e.getInternalName(), u.name(), e.getDescriptor())); ret.add(new FieldInsnNode(Opcodes.GETSTATIC, e.getInternalName(), u.name(), e.getDescriptor()));
return ret; return ret;
} }
} }

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,66 +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 AuthProvider provider; private transient final Logger logger = LogManager.getLogger();
public AuthHandler handler; public boolean isDefault = true;
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 final boolean isDefault = true; 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 void init(LaunchServer srv, String name) { public static Set<String> getFeatures(Class<?> clazz) {
this.name = name; Set<String> list = new HashSet<>();
if(links != null) link(srv); getFeatures(clazz, list);
if(provider == null) throw new NullPointerException(String.format("Auth %s provider null", name)); return list;
if(handler == null) throw new NullPointerException(String.format("Auth %s handler null", name));
if(textureProvider == null) throw new NullPointerException(String.format("Auth %s textureProvider null", name));
provider.init(srv);
handler.init(srv);
} }
public void link(LaunchServer srv)
{ public Set<String> getFeatures() {
links.forEach((k,v) -> { 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) {
this.name = name;
if (links != null) link(srv);
core.init(srv, this);
features = new HashSet<>();
getFeatures(core.getClass(), features);
if(mixes != null) {
for(var m : mixes.values()) {
m.init(srv, core);
getFeatures(m.getClass(), features);
}
}
}
public void link(LaunchServer srv) {
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("Auth %s link failed. Pair %s not found".formatted(name, v));
throw new NullPointerException(String.format("Auth %s link failed. Pair %s not found", name, v));
} }
if("provider".equals(k)) if ("core".equals(k)) {
{ if (pair.core == null)
if(pair.provider == 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;
@ -42,6 +47,21 @@ public MySQLSourceConfig(String poolName) {
this.poolName = poolName; this.poolName = poolName;
} }
public MySQLSourceConfig(String poolName, String address, int port, String username, String password, String database) {
this.poolName = poolName;
this.address = address;
this.port = port;
this.username = username;
this.password = password;
this.database = database;
}
public MySQLSourceConfig(String poolName, DataSource source, boolean hikari) {
this.poolName = poolName;
this.source = source;
this.hikari = hikari;
}
@Override @Override
public synchronized void close() { public synchronized void close() {
if (hikari) if (hikari)
@ -76,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,35 +1,40 @@
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 address; private String[] addresses;
private int port; private int[] ports;
private String username; private String username;
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() {
@ -43,8 +48,8 @@ public synchronized Connection getConnection() throws SQLException {
PGSimpleDataSource postgresqlSource = new PGSimpleDataSource(); PGSimpleDataSource postgresqlSource = new PGSimpleDataSource();
// Set credentials // Set credentials
postgresqlSource.setServerName(address); postgresqlSource.setServerNames(addresses);
postgresqlSource.setPortNumber(port); postgresqlSource.setPortNumbers(ports);
postgresqlSource.setUser(username); postgresqlSource.setUser(username);
postgresqlSource.setPassword(password); postgresqlSource.setPassword(password);
postgresqlSource.setDatabaseName(database); postgresqlSource.setDatabaseName(database);
@ -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

@ -0,0 +1,98 @@
package pro.gravit.launchserver.auth.core.interfaces.provider;
import pro.gravit.launcher.base.request.secure.HardwareReportRequest;
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 java.util.Arrays;
public interface AuthSupportHardware extends AuthSupport {
UserHardware getHardwareInfoByPublicKey(byte[] publicKey);
UserHardware getHardwareInfoByData(HardwareReportRequest.HardwareInfo info);
UserHardware getHardwareInfoById(String id);
UserHardware createHardwareInfo(HardwareReportRequest.HardwareInfo info, byte[] publicKey);
void connectUserAndHardware(UserSession userSession, UserHardware hardware);
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)
hardwareInfo.baseboardSerialNumber = hardwareInfo.baseboardSerialNumber.trim();
if (hardwareInfo.hwDiskId != null) hardwareInfo.hwDiskId = hardwareInfo.hwDiskId.trim();
}
//Required normalize HardwareInfo
default HardwareInfoCompareResult compareHardwareInfo(HardwareReportRequest.HardwareInfo first, HardwareReportRequest.HardwareInfo second) {
HardwareInfoCompareResult result = new HardwareInfoCompareResult();
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.baseboardSerialNumber == null || first.baseboardSerialNumber.trim().isEmpty())
result.firstSpoofingLevel += 0.2;
if (second.hwDiskId == null || second.hwDiskId.trim().isEmpty()) result.secondSpoofingLevel += 0.9;
if (second.displayId == null || second.displayId.length < 4) result.secondSpoofingLevel += 0.3;
if (second.baseboardSerialNumber == null || second.baseboardSerialNumber.trim().isEmpty())
result.secondSpoofingLevel += 0.2;
if (first.hwDiskId != null && second.hwDiskId != null) {
int hwDIskIdRate = DamerauHelper.calculateDistance(first.hwDiskId.toLowerCase(), second.hwDiskId.toLowerCase());
if (hwDIskIdRate == 0) // 100% compare
{
result.compareLevel += 0.99;
} else if (hwDIskIdRate < 3) //Very small change
{
result.compareLevel += 0.85;
} else if (hwDIskIdRate < (first.hwDiskId.length() + second.hwDiskId.length()) / 4) {
double addLevel = hwDIskIdRate / ((double) (first.hwDiskId.length() + second.hwDiskId.length()) / 2.0);
if (addLevel > 0.0 && addLevel < 0.85) result.compareLevel += addLevel;
}
}
if (first.baseboardSerialNumber != null && second.baseboardSerialNumber != null) {
int baseboardSerialRate = DamerauHelper.calculateDistance(first.baseboardSerialNumber.toLowerCase(), second.baseboardSerialNumber.toLowerCase());
if (baseboardSerialRate == 0) // 100% compare
{
result.compareLevel += 0.3;
} else if (baseboardSerialRate < 3) //Very small change
{
result.compareLevel += 0.15;
}
}
if (first.displayId != null && second.displayId != null) {
if (Arrays.equals(first.displayId, second.displayId)) {
result.compareLevel += 0.75;
}
}
//Check statistic info
if (first.logicalProcessors == 0 || first.physicalProcessors == 0 || first.logicalProcessors < first.physicalProcessors) //WTF
result.firstSpoofingLevel += 0.9;
if (second.logicalProcessors == 0 || second.physicalProcessors == 0 || second.logicalProcessors < second.physicalProcessors) //WTF
result.secondSpoofingLevel += 0.9;
if (first.physicalProcessors == second.physicalProcessors && first.logicalProcessors == second.logicalProcessors)
result.compareLevel += 0.05;
if (first.battery != second.battery)
result.compareLevel -= 0.05;
if (first.processorMaxFreq == second.processorMaxFreq)
result.compareLevel += 0.1;
if (first.totalMemory == second.totalMemory)
result.compareLevel += 0.1;
if (Math.abs(first.totalMemory - second.totalMemory) < 32 * 1024)
result.compareLevel += 0.05;
return result;
}
class HardwareInfoCompareResult {
public double firstSpoofingLevel = 0.0;
public double secondSpoofingLevel = 0.0;
public double compareLevel;
}
}

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,79 +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;
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;
}
}
protected transient LaunchServer srv;
/**
* 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,204 +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 {
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;
}
@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;
}
private transient final Map<UUID, Entry> entryCache = new HashMap<>(1024);
private transient final Map<String, UUID> usernamesCache = new HashMap<>(1024);
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;
}
}

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(), username, 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,81 +0,0 @@
package pro.gravit.launchserver.auth.handler;
import pro.gravit.launcher.Launcher;
import pro.gravit.launcher.HTTPRequest;
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 static class EntryRequestByUsername {
public final String username;
public EntryRequestByUsername(String username) {
this.username = username;
}
}
public static class EntryRequestByUUID {
public final UUID uuid;
public EntryRequestByUUID(UUID uuid) {
this.uuid = uuid;
}
}
public static class UpdateAuthRequest {
public final UUID uuid;
public final String username;
public final String accessToken;
public UpdateAuthRequest(UUID uuid, String username, String accessToken) {
this.uuid = uuid;
this.username = username;
this.accessToken = accessToken;
}
}
public static class UpdateServerIDRequest {
public final UUID uuid;
public final String serverID;
public UpdateServerIDRequest(UUID uuid, String serverID) {
this.uuid = uuid;
this.serverID = serverID;
}
}
public static class SuccessResponse {
public boolean success;
}
@Override
protected Entry fetchEntry(String username) throws IOException {
return Launcher.gsonManager.configGson.fromJson(HTTPRequest.jsonRequest(Launcher.gsonManager.configGson.toJsonTree(new EntryRequestByUsername(username)), getUrl), Entry.class);
}
@Override
protected Entry fetchEntry(UUID uuid) throws IOException {
return Launcher.gsonManager.configGson.fromJson(HTTPRequest.jsonRequest(Launcher.gsonManager.configGson.toJsonTree(new EntryRequestByUUID(uuid)), getUrl), Entry.class);
}
@Override
protected boolean updateAuth(UUID uuid, String username, String accessToken) throws IOException {
return Launcher.gsonManager.configGson.fromJson(HTTPRequest.jsonRequest(Launcher.gsonManager.configGson.toJsonTree(new UpdateAuthRequest(uuid, username, accessToken)), updateAuthUrl), SuccessResponse.class).success;
}
@Override
protected boolean updateServerID(UUID uuid, String serverID) throws IOException {
return Launcher.gsonManager.configGson.fromJson(HTTPRequest.jsonRequest(Launcher.gsonManager.configGson.toJsonTree(new UpdateServerIDRequest(uuid, serverID)), updateServerIdUrl), SuccessResponse.class).success;
}
@Override
public void close() {
}
}

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,91 +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 String usernameFetch;
private String uuidFetch;
private String updateAuth;
private String updateServerID;
private final String splitSymbol = ":";
private final String goodResponse = "OK";
@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,22 +1,33 @@
package pro.gravit.launchserver.auth.protect; package pro.gravit.launchserver.auth.protect;
import pro.gravit.launcher.events.request.GetSecureLevelInfoRequestEvent; import io.jsonwebtoken.JwtParser;
import io.jsonwebtoken.Jwts;
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.auth.AuthProviderPair;
import pro.gravit.launchserver.auth.core.interfaces.UserHardware;
import pro.gravit.launchserver.auth.core.interfaces.provider.AuthSupportHardware;
import pro.gravit.launchserver.auth.protect.interfaces.HardwareProtectHandler;
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.utils.helper.SecurityHelper; import pro.gravit.launchserver.socket.response.secure.HardwareReportResponse;
public class AdvancedProtectHandler extends ProtectHandler implements SecureProtectHandler { import java.util.Base64;
import java.util.Date;
import java.util.UUID;
@Override import static java.util.concurrent.TimeUnit.SECONDS;
public boolean allowGetAccessToken(AuthResponse.AuthContext context) {
return (context.authType == AuthResponse.ConnectTypes.CLIENT) && context.client.checkSign;
}
@Override public class AdvancedProtectHandler extends StdProtectHandler implements SecureProtectHandler, HardwareProtectHandler, JoinServerProtectHandler {
public void checkLaunchServerLicense() { private transient final Logger logger = LogManager.getLogger();
public boolean enableHardwareFeature;
} private transient LaunchServer server;
@Override @Override
public GetSecureLevelInfoRequestEvent onGetSecureLevelInfo(GetSecureLevelInfoRequestEvent event) { public GetSecureLevelInfoRequestEvent onGetSecureLevelInfo(GetSecureLevelInfoRequestEvent event) {
@ -27,4 +38,152 @@ public GetSecureLevelInfoRequestEvent onGetSecureLevelInfo(GetSecureLevelInfoReq
public boolean allowGetSecureLevelInfo(Client client) { public boolean allowGetSecureLevelInfo(Client client) {
return client.checkSign; return client.checkSign;
} }
@Override
public void onHardwareReport(HardwareReportResponse response, Client client) {
if (!enableHardwareFeature) {
response.sendResult(new HardwareReportRequestEvent());
return;
}
if (!client.isAuth || client.trustLevel == null || client.trustLevel.publicKey == null) {
response.sendError("Access denied");
return;
}
if(client.trustLevel.hardwareInfo != null) {
response.sendResult(new HardwareReportRequestEvent(createHardwareToken(client.username, client.trustLevel.hardwareInfo), SECONDS.toMillis(server.config.netty.security.hardwareTokenExpire)));
return;
}
logger.debug("HardwareInfo received");
{
var authSupportHardware = client.auth.isSupport(AuthSupportHardware.class);
if (authSupportHardware != null) {
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");
}
}
}
@Override
public VerifySecureLevelKeyRequestEvent onSuccessVerify(Client client) {
if (enableHardwareFeature) {
var authSupportHardware = client.auth.isSupport(AuthSupportHardware.class);
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 {
logger.warn("AuthCoreProvider not supported hardware. HardwareInfo not checked!");
}
}
return new VerifySecureLevelKeyRequestEvent(false, false, createPublicKeyToken(client.username, client.trustLevel.publicKey), SECONDS.toMillis(server.config.netty.security.publicKeyTokenExpire));
}
@Override
public boolean onJoinServer(String serverID, String username, UUID uuid, Client client) {
return !enableHardwareFeature || (client.trustLevel != null && client.trustLevel.hardwareInfo != null);
}
@Override
public void init(LaunchServer server) {
this.server = 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
public boolean accept(Client client, AuthProviderPair pair, String extendedToken) {
try {
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,7 +1,7 @@
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;
import pro.gravit.utils.helper.SecurityHelper;
public class NoProtectHandler extends ProtectHandler { public class NoProtectHandler extends ProtectHandler {
@ -11,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,5 +1,7 @@
package pro.gravit.launchserver.auth.protect; package pro.gravit.launchserver.auth.protect;
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;
@ -18,7 +20,16 @@ public static void registerHandlers() {
} }
public abstract boolean allowGetAccessToken(AuthResponse.AuthContext context); public abstract boolean allowGetAccessToken(AuthResponse.AuthContext context);
public boolean allowJoinServer(Client client) {
return client.isAuth && client.type == AuthResponse.ConnectTypes.CLIENT;
}
public abstract void checkLaunchServerLicense(); //Выдает SecurityException при ошибке проверки лицензии public void init(LaunchServer server) {
}
public void close() {
}
//public abstract //public abstract
} }

View file

@ -1,48 +1,59 @@
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 pro.gravit.utils.helper.SecurityHelper;
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<>();
@Override @Override
public boolean allowGetAccessToken(AuthResponse.AuthContext context) { public boolean allowGetAccessToken(AuthResponse.AuthContext context) {
return (context.authType == AuthResponse.ConnectTypes.CLIENT) && context.client.checkSign; return (context.authType == AuthResponse.ConnectTypes.CLIENT) && context.client.checkSign;
} }
@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
public boolean canGetUpdates(String updatesDirName, Client client) { 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) {
{ if (client.permissions != null) {
List<String> allowedUsername = profileWhitelist.get(profileTitle); String permByUUID = property.formatted(profile.getUUID());
if(allowedUsername == null) return true; if (client.permissions.hasPerm(permByUUID)) {
return allowedUsername.contains(username); 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

@ -0,0 +1,8 @@
package pro.gravit.launchserver.auth.protect.interfaces;
import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.response.secure.HardwareReportResponse;
public interface HardwareProtectHandler {
void onHardwareReport(HardwareReportResponse response, Client client);
}

View file

@ -0,0 +1,11 @@
package pro.gravit.launchserver.auth.protect.interfaces;
import pro.gravit.launchserver.socket.Client;
import java.util.UUID;
public interface JoinServerProtectHandler {
default boolean onJoinServer(String serverID, String username, UUID uuid, Client client) {
return true;
}
}

View file

@ -1,23 +1,22 @@
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 {
default boolean canGetProfiles(Client client) default boolean canGetProfiles(Client client) {
{
return true; return true;
} }
default boolean canGetProfile(ClientProfile profile, Client client)
{ default boolean canGetProfile(ClientProfile profile, Client client) {
return true; return true;
} }
default boolean canChangeProfile(ClientProfile profile, Client client)
{ default boolean canChangeProfile(ClientProfile profile, Client client) {
return client.isAuth; return client.isAuth;
} }
default boolean canGetUpdates(String updatesDirName, Client client)
{ default boolean canGetUpdates(String updatesDirName, Client client) {
return true; return true;
} }
} }

View file

@ -1,7 +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.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;
@ -12,21 +13,27 @@
import java.security.spec.InvalidKeySpecException; import java.security.spec.InvalidKeySpecException;
public interface SecureProtectHandler { public interface SecureProtectHandler {
default byte[] generateSecureLevelKey() default byte[] generateSecureLevelKey() {
{
return SecurityHelper.randomBytes(128); return SecurityHelper.randomBytes(128);
} }
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);
} }
GetSecureLevelInfoRequestEvent onGetSecureLevelInfo(GetSecureLevelInfoRequestEvent event); GetSecureLevelInfoRequestEvent onGetSecureLevelInfo(GetSecureLevelInfoRequestEvent event);
boolean allowGetSecureLevelInfo(Client client); boolean allowGetSecureLevelInfo(Client client);
default SecurityReportRequestEvent onSecurityReport(SecurityReportResponse report, Client client)
{ default SecurityReportRequestEvent onSecurityReport(SecurityReportResponse report, Client client) {
return new SecurityReportRequestEvent(); return new SecurityReportRequestEvent();
} }
default VerifySecureLevelKeyRequestEvent onSuccessVerify(Client client) {
return new VerifySecureLevelKeyRequestEvent();
}
} }

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 GetAvailabilityAuthRequestEvent.AuthAvailability.AuthType getFirstAuthType() {
return GetAvailabilityAuthRequestEvent.AuthAvailability.AuthType.PASSWORD;
}
public GetAvailabilityAuthRequestEvent.AuthAvailability.AuthType getSecondAuthType() {
return GetAvailabilityAuthRequestEvent.AuthAvailability.AuthType.NONE;
}
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;
}
}
/**
* 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,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 AuthProviderResult(login, SecurityHelper.randomStringToken(), user.getPermissions());
}
@Override
public void close() {
}
}

View file

@ -1,68 +0,0 @@
package pro.gravit.launchserver.auth.provider;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
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.launcher.HTTPRequest;
import pro.gravit.utils.helper.SecurityHelper;
import java.io.IOException;
import java.net.URL;
public final class JsonAuthProvider extends AuthProvider {
private static final Gson gson = new Gson();
private URL url;
private String apiKey;
public static class authResult {
String username;
String error;
long permissions;
}
public static class authRequest {
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;
}
final String username;
final String password;
final String ip;
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");
authRequest authRequest = new authRequest(login, ((AuthPlainPassword) password).password, ip, apiKey);
JsonElement request = gson.toJsonTree(authRequest);
JsonElement content = HTTPRequest.jsonRequest(request, url);
if (!content.isJsonObject())
return authError("Authentication server response is malformed");
authResult result = gson.fromJson(content, authResult.class);
if (result.username != null)
return new AuthProviderResult(result.username, SecurityHelper.randomStringToken(), new ClientPermissions(result.permissions));
else if (result.error != null)
return authError(result.error);
else
return authError("Authentication server response is malformed");
}
@Override
public void close() {
// pass
}
}

View file

@ -1,54 +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;
@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))) : 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;
}
}

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