[FEATURE] AuthSocialProvider

This commit is contained in:
Gravita 2021-05-28 19:13:16 +07:00
parent 6c966b6126
commit 568852b951
18 changed files with 306 additions and 39 deletions

View file

@ -2,12 +2,15 @@
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.auth.core.AuthCoreProvider;
import pro.gravit.launchserver.auth.core.AuthSocialProvider;
import pro.gravit.launchserver.auth.handler.AuthHandler;
import pro.gravit.launchserver.auth.provider.AuthProvider;
import pro.gravit.launchserver.auth.texture.TextureProvider;
import java.io.IOException;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class AuthProviderPair {
public final boolean isDefault = true;
@ -15,8 +18,10 @@ public class AuthProviderPair {
public AuthHandler handler;
public TextureProvider textureProvider;
public AuthCoreProvider core;
public AuthSocialProvider social;
public Map<String, String> links;
public transient String name;
public transient Set<String> features;
public String displayName;
public AuthProviderPair(AuthProvider provider, AuthHandler handler, TextureProvider textureProvider) {
@ -31,15 +36,21 @@ public void init(LaunchServer srv, String name) {
if (core == null) {
if (provider == null) throw new NullPointerException(String.format("Auth %s provider null", name));
if (handler == null) throw new NullPointerException(String.format("Auth %s handler null", name));
if (social != null)
throw new IllegalArgumentException(String.format("Auth %s social can't be used in provider/handler method", name));
provider.init(srv);
handler.init(srv);
} else {
if (provider != null) throw new IllegalArgumentException(String.format("Auth %s provider not null", name));
if (handler != null) throw new IllegalArgumentException(String.format("Auth %s handler not null", name));
core.init(srv);
features = new HashSet<>();
getFeatures(core.getClass(), features);
if (social != null) {
social.init(srv, core);
getFeatures(social.getClass(), features);
}
}
if (textureProvider == null)
throw new NullPointerException(String.format("Auth %s textureProvider null", name));
}
public void link(LaunchServer srv) {
@ -60,24 +71,58 @@ public void link(LaunchServer srv) {
if (pair.textureProvider == null)
throw new NullPointerException(String.format("Auth %s link failed. %s.textureProvider is null", name, v));
textureProvider = pair.textureProvider;
} else if ("core".equals(k)) {
if (pair.core == null)
throw new NullPointerException(String.format("Auth %s link failed. %s.core is null", name, v));
core = pair.core;
}
});
}
public void close() throws IOException {
if (social != null) {
social.close();
}
if (core == null) {
provider.close();
handler.close();
} else {
core.close();
}
textureProvider.close();
if (textureProvider != null) {
textureProvider.close();
}
}
public static Set<String> getFeatures(Class<?> clazz) {
Set<String> list = new HashSet<>();
getFeatures(clazz, list);
return list;
}
public static void getFeatures(Class<?> clazz, Set<String> list) {
Features features = clazz.getAnnotation(Features.class);
for (Feature feature : features.value()) {
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 boolean isUseCore() {
return core != null;
}
public boolean isUseSocial() {
return core != null && social != null;
}
public boolean isUseProviderAndHandler() {
return !isUseCore();
}

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,55 @@
package pro.gravit.launchserver.auth.core;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
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.launchserver.socket.Client;
import pro.gravit.launchserver.socket.response.auth.AuthResponse;
import pro.gravit.utils.ProviderMap;
import java.io.IOException;
import java.util.List;
public abstract class AuthSocialProvider implements AutoCloseable {
public static final ProviderMap<AuthSocialProvider> providers = new ProviderMap<>("AuthSocialProvider");
private static final Logger logger = LogManager.getLogger();
private static boolean registredProviders = false;
public static void registerProviders() {
if (!registredProviders) {
registredProviders = true;
}
}
public static class SocialResult {
public String login;
public AuthRequest.AuthPasswordInterface password;
public User user;
public SocialResult(String login, AuthRequest.AuthPasswordInterface password, User user) {
this.login = login;
this.password = password;
this.user = user;
}
public static SocialResult ofLoginAndPassword(String login, AuthRequest.AuthPasswordInterface password) {
return new SocialResult(login, password, null);
}
public static SocialResult ofUser(User user) {
return new SocialResult(null, null, user);
}
}
public abstract void init(LaunchServer server, AuthCoreProvider provider);
public abstract List<GetAvailabilityAuthRequestEvent.AuthAvailabilityDetails> getDetails(Client client);
public abstract SocialResult preAuth(AuthResponse.AuthContext context, AuthRequest.AuthPasswordInterface password) throws AuthException;
@Override
public abstract void close() throws IOException;
}

View file

@ -1,9 +1,11 @@
package pro.gravit.launchserver.auth.core.interfaces.provider;
import pro.gravit.launchserver.auth.Feature;
import pro.gravit.launchserver.auth.core.User;
import java.util.List;
@Feature("users")
public interface AuthSupportGetAllUsers {
List<User> getAllUsers();
}

View file

@ -1,10 +1,12 @@
package pro.gravit.launchserver.auth.core.interfaces.provider;
import pro.gravit.launchserver.auth.Feature;
import pro.gravit.launchserver.auth.core.User;
import pro.gravit.launchserver.auth.core.UserSession;
import java.util.List;
@Feature("sessions")
public interface AuthSupportGetSessionsFromUser {
List<UserSession> getSessionsByUser(User user);

View file

@ -0,0 +1,12 @@
package pro.gravit.launchserver.auth.core.interfaces.provider;
import pro.gravit.launchserver.auth.Feature;
import pro.gravit.launchserver.auth.core.User;
import pro.gravit.launchserver.auth.core.interfaces.user.UserSupportMoney;
@Feature("money")
public interface AuthSupportMoney {
default UserSupportMoney fetchUserMoney(User user) {
return (UserSupportMoney) user;
}
}

View file

@ -1,10 +1,12 @@
package pro.gravit.launchserver.auth.core.interfaces.provider;
import pro.gravit.launcher.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 {
User registration(String login, AuthRequest.AuthPasswordInterface password, Map<String, String> properties);
User registration(String login, String email, AuthRequest.AuthPasswordInterface password, Map<String, String> properties);
}

View file

@ -0,0 +1,18 @@
package pro.gravit.launchserver.auth.core.interfaces.user;
import pro.gravit.launcher.profiles.ClientProfile;
import pro.gravit.launcher.profiles.Texture;
public interface UserSupportTextures {
Texture getSkinTexture();
Texture getCloakTexture();
default Texture getSkinTexture(ClientProfile profile) {
return getSkinTexture();
}
default Texture getCloakTexture(ClientProfile profile) {
return getCloakTexture();
}
}

View file

@ -209,6 +209,7 @@ public void init(LaunchServer.ReloadType type) {
server.registerObject("auth.".concat(pair.name).concat(".provider"), pair.provider);
server.registerObject("auth.".concat(pair.name).concat(".handler"), pair.handler);
server.registerObject("auth.".concat(pair.name).concat(".core"), pair.core);
server.registerObject("auth.".concat(pair.name).concat(".social"), pair.core);
server.registerObject("auth.".concat(pair.name).concat(".texture"), pair.textureProvider);
}
}
@ -221,6 +222,7 @@ public void close(LaunchServer.ReloadType type) {
for (AuthProviderPair pair : auth.values()) {
server.unregisterObject("auth.".concat(pair.name).concat(".provider"), pair.provider);
server.unregisterObject("auth.".concat(pair.name).concat(".handler"), pair.handler);
server.unregisterObject("auth.".concat(pair.name).concat(".social"), pair.social);
server.unregisterObject("auth.".concat(pair.name).concat(".core"), pair.core);
server.unregisterObject("auth.".concat(pair.name).concat(".texture"), pair.textureProvider);
pair.close();

View file

@ -4,17 +4,23 @@
import org.apache.logging.log4j.Logger;
import pro.gravit.launcher.ClientPermissions;
import pro.gravit.launcher.events.request.AuthRequestEvent;
import pro.gravit.launcher.profiles.ClientProfile;
import pro.gravit.launcher.profiles.PlayerProfile;
import pro.gravit.launcher.profiles.Texture;
import pro.gravit.launcher.request.auth.AuthRequest;
import pro.gravit.launcher.request.auth.password.*;
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.auth.AuthException;
import pro.gravit.launchserver.auth.AuthProviderPair;
import pro.gravit.launchserver.auth.core.AuthCoreProvider;
import pro.gravit.launchserver.auth.core.AuthSocialProvider;
import pro.gravit.launchserver.auth.core.User;
import pro.gravit.launchserver.auth.core.UserSession;
import pro.gravit.launchserver.auth.core.interfaces.user.UserSupportTextures;
import pro.gravit.launchserver.auth.provider.AuthProvider;
import pro.gravit.launchserver.auth.provider.AuthProviderDAOResult;
import pro.gravit.launchserver.auth.provider.AuthProviderResult;
import pro.gravit.launchserver.auth.texture.TextureProvider;
import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.response.auth.AuthResponse;
import pro.gravit.utils.helper.IOHelper;
@ -133,14 +139,28 @@ private AuthReport authWithCore(AuthResponse.AuthContext context, AuthRequest.Au
return AuthReport.ofMinecraftAccessToken(null);
}
User user = null;
if (context.login != null) {
boolean skipPasswordCheck = false;
String login = context.login;
if (context.pair.social != null) {
AuthSocialProvider.SocialResult result = context.pair.social.preAuth(context, password);
if (result != null) {
if (result.user != null) user = result.user;
if (result.login != null) login = result.login;
if (result.password != null) password = result.password;
if (result.user != null && result.password == null) skipPasswordCheck = true;
}
}
if (user == null && login != null) {
user = provider.getUserByLogin(context.login);
if (user == null) {
throw new AuthException(AuthRequestEvent.USER_NOT_FOUND_ERROR_MESSAGE);
}
}
AuthCoreProvider.PasswordVerifyReport report = provider.verifyPassword(user, password);
if (report.success) {
AuthCoreProvider.PasswordVerifyReport report = null;
if (!skipPasswordCheck) {
report = provider.verifyPassword(user, password);
}
if (skipPasswordCheck || report.success) {
AuthReport result;
try {
result = provider.createOAuthSession(user, context, report, context.authType == AuthResponse.ConnectTypes.CLIENT && server.config.protectHandler.allowGetAccessToken(context));
@ -208,6 +228,103 @@ public boolean joinServer(Client client, String username, String accessToken, St
}
}
public PlayerProfile getPlayerProfile(Client client) {
if (client.auth == null) return null;
PlayerProfile playerProfile;
if (client.useOAuth) {
User user = client.getUser();
playerProfile = getPlayerProfile(user);
if (playerProfile != null) return playerProfile;
}
if (client.auth.textureProvider != null) {
return getPlayerProfile(client.uuid, client.username, client.profile == null ? null : client.profile.getTitle(), client.auth.textureProvider);
}
// Return combined profile
return new PlayerProfile(client.uuid, client.username, null, null);
}
public PlayerProfile getPlayerProfile(AuthProviderPair pair, String username) {
return getPlayerProfile(pair, username, null);
}
public PlayerProfile getPlayerProfile(AuthProviderPair pair, String username, ClientProfile profile) {
UUID uuid = null;
if (pair.isUseCore()) {
User user = pair.core.getUserByUsername(username);
PlayerProfile playerProfile = getPlayerProfile(user);
uuid = user.getUUID();
if (playerProfile != null) return playerProfile;
} else {
try {
uuid = pair.handler.usernameToUUID(username);
} catch (IOException e) {
logger.error("UsernameToUUID failed", e);
}
}
if (uuid == null) {
return null;
}
if (pair.textureProvider != null) {
return getPlayerProfile(uuid, username, profile == null ? null : profile.getTitle(), pair.textureProvider);
}
return new PlayerProfile(uuid, username, null, null);
}
public PlayerProfile getPlayerProfile(AuthProviderPair pair, UUID uuid) {
return getPlayerProfile(pair, uuid, null);
}
public PlayerProfile getPlayerProfile(AuthProviderPair pair, UUID uuid, ClientProfile profile) {
String username = null;
if (pair.isUseCore()) {
User user = pair.core.getUserByUUID(uuid);
PlayerProfile playerProfile = getPlayerProfile(user);
username = user.getUsername();
if (playerProfile != null) return playerProfile;
} else {
try {
username = pair.handler.uuidToUsername(uuid);
} catch (IOException e) {
logger.error("UUIDToUsername failed", e);
}
}
if (username == null) {
return null;
}
if (pair.textureProvider != null) {
return getPlayerProfile(uuid, username, profile == null ? null : profile.getTitle(), pair.textureProvider);
}
return new PlayerProfile(uuid, username, null, null);
}
public PlayerProfile getPlayerProfile(User user) {
if (user instanceof UserSupportTextures) {
return new PlayerProfile(user.getUUID(), user.getUsername(), ((UserSupportTextures) user).getSkinTexture(), ((UserSupportTextures) user).getCloakTexture());
}
return null;
}
private PlayerProfile getPlayerProfile(UUID uuid, String username, String client, TextureProvider textureProvider) {
// Get skin texture
Texture skin;
try {
skin = textureProvider.getSkinTexture(uuid, username, client);
} catch (IOException e) {
skin = null;
}
// Get cloak texture
Texture cloak;
try {
cloak = textureProvider.getCloakTexture(uuid, username, client);
} catch (IOException e) {
cloak = null;
}
// Return combined profile
return new PlayerProfile(uuid, username, skin, cloak);
}
public AuthRequest.AuthPasswordInterface decryptPassword(AuthRequest.AuthPasswordInterface password) throws AuthException {
if (password instanceof Auth2FAPassword) {
Auth2FAPassword auth2FAPassword = (Auth2FAPassword) password;

View file

@ -10,7 +10,6 @@
import pro.gravit.launchserver.manangers.AuthManager;
import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.response.SimpleResponse;
import pro.gravit.launchserver.socket.response.profile.ProfileByUUIDResponse;
import pro.gravit.utils.HookException;
import java.util.UUID;
@ -60,7 +59,7 @@ public void execute(ChannelHandlerContext ctx, Client clientData) throws Excepti
if (context.report.minecraftAccessToken != null) {
result.accessToken = context.report.minecraftAccessToken;
}
result.playerProfile = ProfileByUUIDResponse.getProfile(clientData.uuid, clientData.username, client, clientData.auth.textureProvider);
result.playerProfile = server.authManager.getPlayerProfile(clientData);
sendResult(result);
} catch (AuthException | HookException e) {
sendError(e.getMessage());

View file

@ -7,7 +7,6 @@
import pro.gravit.launchserver.auth.AuthException;
import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.response.SimpleResponse;
import pro.gravit.launchserver.socket.response.profile.ProfileByUUIDResponse;
import pro.gravit.utils.HookException;
public class CheckServerResponse extends SimpleResponse {
@ -32,7 +31,7 @@ public void execute(ChannelHandlerContext ctx, Client pClient) {
server.authHookManager.checkServerHook.hook(this, pClient);
result.uuid = server.authManager.checkServer(pClient, username, serverID);
if (result.uuid != null) {
result.playerProfile = ProfileByUUIDResponse.getProfile(result.uuid, username, client, pClient.auth.textureProvider);
result.playerProfile = server.authManager.getPlayerProfile(pClient);
logger.debug("checkServer: {} uuid: {} serverID: {}", result.playerProfile.username, result.uuid, serverID);
}
} catch (AuthException | HookException e) {

View file

@ -2,6 +2,7 @@
import io.netty.channel.ChannelHandlerContext;
import pro.gravit.launcher.events.request.CurrentUserRequestEvent;
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.response.SimpleResponse;
import pro.gravit.launchserver.socket.response.profile.ProfileByUUIDResponse;
@ -10,6 +11,7 @@
import java.util.UUID;
public class CurrentUserResponse extends SimpleResponse {
@Deprecated
public static CurrentUserRequestEvent.UserInfo collectUserInfoFromClient(Client client) throws IOException {
CurrentUserRequestEvent.UserInfo result = new CurrentUserRequestEvent.UserInfo();
if (client.auth != null && client.isAuth && client.username != null) {
@ -22,6 +24,15 @@ public static CurrentUserRequestEvent.UserInfo collectUserInfoFromClient(Client
return result;
}
public static CurrentUserRequestEvent.UserInfo collectUserInfoFromClient(LaunchServer server, Client client) throws IOException {
CurrentUserRequestEvent.UserInfo result = new CurrentUserRequestEvent.UserInfo();
if (client.auth != null && client.isAuth && client.username != null) {
result.playerProfile = server.authManager.getPlayerProfile(client);
}
result.permissions = client.permissions;
return result;
}
@Override
public String getType() {
return "currentUser";
@ -29,6 +40,6 @@ public String getType() {
@Override
public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
sendResult(new CurrentUserRequestEvent(collectUserInfoFromClient(client)));
sendResult(new CurrentUserRequestEvent(collectUserInfoFromClient(server, client)));
}
}

View file

@ -19,7 +19,7 @@ public String getType() {
public void execute(ChannelHandlerContext ctx, Client client) {
List<GetAvailabilityAuthRequestEvent.AuthAvailability> list = new ArrayList<>();
for (AuthProviderPair pair : server.config.auth.values()) {
list.add(new GetAvailabilityAuthRequestEvent.AuthAvailability(pair.name, pair.displayName, pair.isUseCore() ? pair.core.getDetails(client) : pair.provider.getDetails(client)));
list.add(new GetAvailabilityAuthRequestEvent.AuthAvailability(pair.name, pair.displayName, pair.isUseCore() ? (pair.isUseSocial() ? pair.social.getDetails(client) : pair.core.getDetails(client)) : pair.provider.getDetails(client)));
}
sendResult(new GetAvailabilityAuthRequestEvent(list));
}

View file

@ -4,12 +4,9 @@
import pro.gravit.launcher.events.request.BatchProfileByUsernameRequestEvent;
import pro.gravit.launcher.profiles.PlayerProfile;
import pro.gravit.launchserver.auth.AuthProviderPair;
import pro.gravit.launchserver.auth.core.User;
import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.response.SimpleResponse;
import java.util.UUID;
public class BatchProfileByUsername extends SimpleResponse {
Entry[] list;
@ -27,19 +24,11 @@ public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
}
result.playerProfiles = new PlayerProfile[list.length];
for (int i = 0; i < list.length; ++i) {
UUID uuid;
AuthProviderPair pair = client.auth;
if (pair == null) {
pair = server.config.getAuthProviderPair();
}
if (pair.isUseCore()) {
User user = pair.core.getUserByUsername(list[i].username);
if (user == null) uuid = null;
else uuid = user.getUUID();
} else {
uuid = pair.handler.usernameToUUID(list[i].username);
}
result.playerProfiles[i] = ProfileByUUIDResponse.getProfile(uuid, list[i].username, list[i].client, pair.textureProvider);
result.playerProfiles[i] = server.authManager.getPlayerProfile(pair, list[i].username);
}
sendResult(result);
}

View file

@ -17,6 +17,7 @@ public class ProfileByUUIDResponse extends SimpleResponse {
public UUID uuid;
public String client;
@Deprecated
public static PlayerProfile getProfile(UUID uuid, String username, String client, TextureProvider textureProvider) {
// Get skin texture
Texture skin;
@ -69,6 +70,6 @@ public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
return;
}
}
sendResult(new ProfileByUUIDRequestEvent(getProfile(uuid, username, this.client, pair.textureProvider)));
sendResult(new ProfileByUUIDRequestEvent(server.authManager.getPlayerProfile(pair, uuid)));
}
}

View file

@ -2,13 +2,11 @@
import io.netty.channel.ChannelHandlerContext;
import pro.gravit.launcher.events.request.ProfileByUsernameRequestEvent;
import pro.gravit.launcher.profiles.PlayerProfile;
import pro.gravit.launchserver.auth.AuthProviderPair;
import pro.gravit.launchserver.auth.core.User;
import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.response.SimpleResponse;
import java.util.UUID;
public class ProfileByUsername extends SimpleResponse {
String username;
String client;
@ -20,20 +18,13 @@ public String getType() {
@Override
public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
UUID uuid;
AuthProviderPair pair = client.auth;
if (pair == null) pair = server.config.getAuthProviderPair();
if (pair.isUseCore()) {
User user = pair.core.getUserByUsername(username);
if (user == null) uuid = null;
else uuid = user.getUUID();
} else {
uuid = pair.handler.usernameToUUID(username);
}
if (uuid == null) {
PlayerProfile profile = server.authManager.getPlayerProfile(pair, username);
if (profile == null) {
sendError("User not found");
return;
}
sendResult(new ProfileByUsernameRequestEvent(ProfileByUUIDResponse.getProfile(uuid, username, this.client, pair.textureProvider)));
sendResult(new ProfileByUsernameRequestEvent(profile));
}
}