diff --git a/README.creole b/README.creole deleted file mode 100644 index f8bdfa5c1..000000000 --- a/README.creole +++ /dev/null @@ -1,142 +0,0 @@ -= LuckPerms -A (fairly bad) permissions implementation for Bukkit/BungeeCord. - -== Features -* **Group inheritance** - users can be members of multiple groups, groups can inherit other groups -* **Temporary permissions** - users/groups can be given permissions that expire after a given time -* **Temporary groups** - users/groups can be added to/inherit other groups temporarily -* **Multi-server support** - data is synced across all servers/platforms -* **Per-server permissions/groups** - define user/group permissions that only apply on certain servers -* **Server-specific groups** - define groups that only apply on certain servers -* **Tracks / paths** - users can be promoted/demoted along multiple group tracks -* **Vault Support** - hooks into Vault to integrate with other plugins -* **Easy and simple setup and configuration using commands** - no editing yml files, yuck -* **Efficient/lightweight** - maybe? Who knows, it might be. -* **BungeeCord compatible** - permissions, users and groups are synced across Bukkit/BungeeCord instances -* **Support for MySQL, SQLite & Flatfile (JSON)** - other storage methods coming soon (maybe) - -===== Possible Caveats -* Currently only supports MySQL, SQLite & Flatfile (JSON) (support for more methods might come in the future) -* Not at all tested and could produce unexpected/buggy results and errors - -== Setup -All configuration options are in the **config.yml** file, which is generated automagically when the plugin first starts. - -You can define the settings for per-server permissions, the storage method and credentials within this file. - -== Info -=== Permission Calculation -Permissions are calculated based on a priority system as follows. - -* Temporary permissions will override non-temporary permissions. - -Example: if a user has a false permission set for "test.node", and a temporary true permission set for "test.node", the temporary permission will override the permanent one, and the user will be granted the true node. - -* Server specific permissions will override generic/global permissions. - -Example: if a user has a global "fly.use" permission, and then has a negated "fly.use" permission on the "factions" server, the server specific permission will override the globally defined one, and the user will be granted the negated node. - -* Inherited permissions will be overridden by an objects own permissions. - -Example: A user is a member of the default group, which grants "some.thing.perm", but the users own permissions has "some.thing.perm" set to false. The inherited permission will be overridden by the users own permissions, and the user will be granted the negative node. - -=== Temporary Permissions -Temporary permissions are checked each time a user/group is loaded, and when the sync task runs. This means if you set a temporary permission to expire after 30 seconds, it won't actually be removed until the sync task runs. - -The only way around this is to decrease the sync interval. - -== Commands - -Command usage is printed to the console/chat whenever invalid arguments are provided. Simply typing /perms will list all commands a user has permission to use. - -=== Aliases -| Bukkit | /luckperms | -| | /perms | -| | /permissions | -| | /lp | -| | /perm | -| Bungee | /luckpermsbungee | -| | /bperms | -| | /bpermissions | -| | /lpb | -| | /bperm | - -Arguments: [optional] - -Users with OP have access to all commands. - -Additionally, you can use wildcards to grant users access to a selection of commands. -* **All commands** - luckperms.* -* **All user commands** - luckperms.user.* -* **All group commands** - luckperms.group.* -* **All track commands** - luckperms.track.* - - -| **Command** | **Description** | **Permission** | -| /perms | Sends plugin info and usage | N/A | -| /perms sync | Runs an update task and reloads the data of all online users | luckperms.sync | -| /perms info | Shows information about the plugin | luckperms.info | -| /perms debug | Shows some very brief debug info about the number of users/groups loaded | luckperms.debug | -| | | | -| | | | -| | | | -| /perms creategroup | Creates a new group with the given name | luckperms.creategroup | -| /perms deletegroup | Deletes an existing group | luckperms.deletegroup | -| /perms listgroups | Shows a list of the groups registered within the system | luckperms.listgroups | -| | | | -| | | | -| | | | -| /perms createtrack | Creates a new track with the given name | luckperms.createtrack | -| /perms deletetrack | Deletes an existing track | luckperms.deletetrack | -| /perms listtracks | Shows a list of the tracks registered within the system | luckperms.listtracks | -| | | | -| | | | -| | | | -| /perms user info | Shows info about the user | luckperms.user.info | -| /perms user getuuid | Shows the users Mojang UUID | luckperms.user.getuuid | -| /perms user listnodes | Lists all of the permission nodes the user has | luckperms.user.listnodes | -| /perms user haspermission [server] | Checks if the user has a permission on a certain server | luckperms.user.haspermission | -| /perms user inheritspermission [server] | Checks if the user inherits a permission on a certain server. (This checks all parent groups unlike haspermission) | luckperms.user.inheritspermission | -| /perms user set [server]| Sets a permission for the user | luckperms.user.setpermission | -| /perms user unset [server] | Unsets a permission for the user | luckperms.user.unsetpermission | -| /perms user addgroup [server] | Adds the user to a group | luckperms.user.addgroup | -| /perms user removegroup [server] | Removes the user from a group | luckperms.user.removegroup | -| /perms user settemp [server]| Sets a temporary permission for the user | luckperms.user.settemppermission | -| /perms user unsettemp [server] | Unsets a temporary permission for the user | luckperms.user.unsettemppermission | -| /perms user addtempgroup [server] | Adds the user to a group temporarily | luckperms.user.addtempgroup | -| /perms user removetempgroup [server] | Removes the user from a temporary group | luckperms.user.removetempgroup | -| /perms user setprimarygroup | Sets the users primary group | luckperms.user.setprimarygroup | -| /perms user showtracks | Shows a list of the tracks the user can be promoted/demoted on | luckperms.user.showtracks | -| /perms user promote | Promotes the user along a given track | luckperms.user.promote | -| /perms user demote | Demotes the user along a given track | luckperms.user.demote | -| /perms user showpos | Shows the users position on a given track | luckperms.user.showpos | -| /perms user clear | Clears all permissions the user has | luckperms.user.clear | -| | | | -| | | | -| | | | -| /perms group info | Shows info about the group | luckperms.group.info | -| /perms group listnodes | Lists all of the permission nodes the group has | luckperms.group.listnodes | -| /perms group haspermission [server] | Checks if the group has a permission on a certain server | luckperms.group.haspermission | -| /perms group inheritspermission [server] | Checks if the group inherits a permission on a certain server. (This checks all parent groups unlike haspermission) | luckperms.group.inheritspermission | -| /perms group set [server]| Sets a permission for the group | luckperms.group.setpermission | -| /perms group unset [server] | Unsets a permission for the group | luckperms.group.unsetpermission | -| /perms group setinherit [server]| Sets the group to inherit all permissions from another group | luckperms.group.setinherit | -| /perms group unsetinherit [server] | Unsets a previously defined inheritance rule | luckperms.group.unsetinherit | -| /perms group settemp [server] | Sets a temporary permission for the group | luckperms.group.settemppermission | -| /perms group unsettemp [server] | Unsets a temporary permission for the group | luckperms.group.unsettemppermission | -| /perms group settempinherit [server] | Sets the group to inherit all permissions from another group temporarily | luckperms.group.settempinherit | -| /perms group unsettempinherit [server] | Unsets a previously defined temporary inheritance rule | luckperms.group.unsettempinherit | -| /perms group showtracks | Shows a list of the tracks that the users in the group can be promoted/demoted on | luckperms.group.showtracks | -| /perms group clear | Clears all permissions the group has | luckperms.group.clear | -| | | | -| | | | -| | | | -| /perms track info | Shows info about the track | luckperms.track.info | -| /perms track append | Appends a group to the end of the track | luckperms.track.append | -| /perms track insert | Inserts a group at the given position on the track | luckperms.track.insert | -| /perms track remove | Removes a group from the track | luckperms.track.remove | -| /perms track clear | Clears all groups on the track | luckperms.track.clear | - - -== License -See LICENSE.md. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 000000000..7d6400aa5 --- /dev/null +++ b/README.md @@ -0,0 +1,143 @@ +# LuckPerms +A (fairly bad) permissions implementation for Bukkit/BungeeCord. + +## Features +* **Group inheritance** - users can be members of multiple groups, groups can inherit other groups +* **Temporary permissions** - users/groups can be given permissions that expire after a given time +* **Temporary groups** - users/groups can be added to/inherit other groups temporarily +* **Multi-server support** - data is synced across all servers/platforms +* **Per-server permissions/groups** - define user/group permissions that only apply on certain servers +* **Server-specific groups** - define groups that only apply on certain servers +* **Tracks / paths** - users can be promoted/demoted along multiple group tracks +* **Vault Support** - hooks into Vault to integrate with other plugins +* **Developer API** - easily integrate LuckPerms into your own projects +* **Easy and simple setup and configuration using commands** - no editing yml files, yuck +* **Efficient/lightweight** - maybe? Who knows, it might be. +* **BungeeCord compatible** - permissions, users and groups are synced across Bukkit/BungeeCord instances +* **Support for MySQL, SQLite & Flatfile (JSON)** - other storage methods coming soon (maybe) + +##### Possible Caveats +* Currently only supports MySQL, SQLite & Flatfile (JSON) (support for more methods might come in the future) +* Not at all tested and could produce unexpected/buggy results and errors + +## Setup +All configuration options are in the **config.yml** file, which is generated automagically when the plugin first starts. + +You can define the settings for per-server permissions, the storage method and credentials within this file. + +## Info +### Permission Calculation +Permissions are calculated based on a priority system as follows. + +* Temporary permissions will override non-temporary permissions. + +Example: if a user has a false permission set for "test.node", and a temporary true permission set for "test.node", the temporary permission will override the permanent one, and the user will be granted the true node. + +* Server specific permissions will override generic/global permissions. + +Example: if a user has a global "fly.use" permission, and then has a negated "fly.use" permission on the "factions" server, the server specific permission will override the globally defined one, and the user will be granted the negated node. + +* Inherited permissions will be overridden by an objects own permissions. + +Example: A user is a member of the default group, which grants "some.thing.perm", but the users own permissions has "some.thing.perm" set to false. The inherited permission will be overridden by the users own permissions, and the user will be granted the negative node. + +### Temporary Permissions +Temporary permissions are checked each time a user/group is loaded, and when the sync task runs. This means if you set a temporary permission to expire after 30 seconds, it won't actually be removed until the sync task runs. + +The only way around this is to decrease the sync interval. + +## API +LuckPerms has an extensive API, allowing for easy integration with other projects. To use the Api, you need to obtain an instance of the `LuckPermsApi` interface. This can be done in two ways, (one way on BungeeCord). + +```java +final LuckPermsApi api = LuckPerms.getApi(); +final LuckPermsApi api = Bukkit.getServicesManager().getRegistration(LuckPermsApi.class).getProvider(); +``` + +If you want to use LuckPerms in your onEnable method, you need to add the following to your plugins `plugin.yml`. +```yml +depend: [LuckPerms] +``` +All of the available methods can be seen in the various interfaces in the `luckperms-api` module. + +## Commands + +Command usage is printed to the console/chat whenever invalid arguments are provided. Simply typing /perms will list all commands a user has permission to use. + +### Aliases + +| Bukkit | Bungee | +|------------------|------------------| +| /luckperms | /luckpermsbungee | +| /perms | /bperms | +| /permissions | /bpermissions | +| /lp | /lpb | +| /perm | /bperm | + +Arguments: [optional] + +Users with OP have access to all commands. + +Additionally, you can use wildcards to grant users access to a selection of commands. +* **All commands** - luckperms.* +* **All user commands** - luckperms.user.* +* **All group commands** - luckperms.group.* +* **All track commands** - luckperms.track.* + +### General +* /perms - n/a +* /perms sync - luckperms.sync +* /perms info - luckperms.info +* /perms debug - luckperms.debug +* /perms creategroup - luckperms.creategroup +* /perms deletegroup - luckperms.deletegroup +* /perms listgroups - luckperms.listgroups +* /perms createtrack - luckperms.createtrack +* /perms deletetrack - luckperms.deletetrack +* /perms listtracks - luckperms.listtracks + +### User +* /perms user info - luckperms.user.info +* /perms user getuuid - luckperms.user.getuuid +* /perms user listnodes - luckperms.user.listnodes +* /perms user haspermission [server] - luckperms.user.haspermission +* /perms user inheritspermission [server] - luckperms.user.inheritspermission +* /perms user set [server] - luckperms.user.setpermission +* /perms user unset [server] - luckperms.user.unsetpermission +* /perms user addgroup [server] - luckperms.user.addgroup +* /perms user removegroup [server] - luckperms.user.removegroup +* /perms user settemp [server] - luckperms.user.settemppermission +* /perms user addtempgroup [server] - luckperms.user.addtempgroup +* /perms user removetempgroup [server] - luckperms.user.removetempgroup +* /perms user setprimarygroup - luckperms.user.setprimarygroup +* /perms user showtracks - luckperms.user.showtracks +* /perms user promote - luckperms.user.promote +* /perms user demote - luckperms.user.demote +* /perms user showpos - luckperms.user.showpos +* /perms user clear - luckperms.user.clear + +### Group +* /perms group info - luckperms.group.info +* /perms group listnodes - luckperms.group.listnodes +* /perms group haspermission [server] - luckperms.group.haspermission +* /perms group inheritspermission [server] - luckperms.group.inheritspermission +* /perms group set [server] - luckperms.group.setpermission +* /perms group unset [server] - luckperms.group.unsetpermission +* /perms group setinherit [server] - luckperms.group.setinherit +* /perms group unsetinherit [server] - luckperms.group.unsetinherit +* /perms group settemp [server] - settemppermission +* /perms group unsettemp [server] - luckperms.group.unsettemppermission +* /perms group settempinherit [server] - luckperms.group.settempinherit +* /perms group unsettempinherit [server] - luckperms.group.unsettempinherit +* /perms group showtracks - luckperms.group.showtracks +* /perms group clear - luckperms.group.clear + +### Track +* /perms track info - luckperms.track.info +* /perms track append - luckperms.track.append +* /perms track insert - luckperms.track.insert +* /perms track remove - luckperms.track.remove +* /perms track clear - luckperms.track.clear + +## License +See LICENSE.md. \ No newline at end of file diff --git a/api/pom.xml b/api/pom.xml new file mode 100644 index 000000000..843ee655c --- /dev/null +++ b/api/pom.xml @@ -0,0 +1,26 @@ + + + + luckperms + me.lucko + 1.3 + + 4.0.0 + + luckperms-api + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.5.1 + + 1.8 + 1.8 + + + + + diff --git a/api/src/main/java/me/lucko/luckperms/LuckPerms.java b/api/src/main/java/me/lucko/luckperms/LuckPerms.java new file mode 100644 index 000000000..18a49cb94 --- /dev/null +++ b/api/src/main/java/me/lucko/luckperms/LuckPerms.java @@ -0,0 +1,30 @@ +package me.lucko.luckperms; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import me.lucko.luckperms.api.LuckPermsApi; + +/** + * Static access to LuckPerms + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class LuckPerms { + + private static LuckPermsApi api = null; + + public static LuckPermsApi getApi() { + if (api == null) { + throw new IllegalStateException("API is not loaded."); + } + return api; + } + + static void registerProvider(LuckPermsApi luckPermsApi) { + api = luckPermsApi; + } + + static void unregisterProvider() { + api = null; + } + +} diff --git a/api/src/main/java/me/lucko/luckperms/api/Datastore.java b/api/src/main/java/me/lucko/luckperms/api/Datastore.java new file mode 100644 index 000000000..a85e0bcd6 --- /dev/null +++ b/api/src/main/java/me/lucko/luckperms/api/Datastore.java @@ -0,0 +1,56 @@ +package me.lucko.luckperms.api; + +import me.lucko.luckperms.api.data.Callback; + +import java.util.UUID; + +/** + * Wrapper interface for the internal Datastore instance + * The implementations of this interface limit access to the datastore and add parameter checks to further prevent + * errors and ensure all API interactions to not damage the state of the plugin. + */ +@SuppressWarnings("unused") +public interface Datastore { + + String getName(); + boolean isAcceptingLogins(); + + Async async(); + Sync sync(); + + interface Async { + void loadOrCreateUser(UUID uuid, String username, Callback callback); + void loadUser(UUID uuid, Callback callback); + void saveUser(User user, Callback callback); + void createAndLoadGroup(String name, Callback callback); + void loadGroup(String name, Callback callback); + void loadAllGroups(Callback callback); + void saveGroup(Group group, Callback callback); + void deleteGroup(Group group, Callback callback); + void createAndLoadTrack(String name, Callback callback); + void loadTrack(String name, Callback callback); + void loadAllTracks(Callback callback); + void saveTrack(Track track, Callback callback); + void deleteTrack(Track track, Callback callback); + void saveUUIDData(String username, UUID uuid, Callback callback); + void getUUID(String username, Callback.GetUUID callback); + } + + interface Sync { + boolean loadOrCreateUser(UUID uuid, String username); + boolean loadUser(UUID uuid); + boolean saveUser(User user); + boolean createAndLoadGroup(String name); + boolean loadGroup(String name); + boolean loadAllGroups(); + boolean saveGroup(Group group); + boolean deleteGroup(Group group); + boolean createAndLoadTrack(String name); + boolean loadTrack(String name); + boolean loadAllTracks(); + boolean saveTrack(Track track); + boolean deleteTrack(Track track); + boolean saveUUIDData(String username, UUID uuid); + UUID getUUID(String username); + } +} diff --git a/api/src/main/java/me/lucko/luckperms/api/Group.java b/api/src/main/java/me/lucko/luckperms/api/Group.java new file mode 100644 index 000000000..fdc98aeca --- /dev/null +++ b/api/src/main/java/me/lucko/luckperms/api/Group.java @@ -0,0 +1,18 @@ +package me.lucko.luckperms.api; + +/** + * Wrapper interface for internal Group instances + * The implementations of this interface limit access to the Group and add parameter checks to further prevent + * errors and ensure all API interactions to not damage the state of the group. + */ +@SuppressWarnings("unused") +public interface Group extends PermissionObject { + + String getName(); + + /** + * Clear all of the groups permission nodes + */ + void clearNodes(); + +} diff --git a/api/src/main/java/me/lucko/luckperms/api/LuckPermsApi.java b/api/src/main/java/me/lucko/luckperms/api/LuckPermsApi.java new file mode 100644 index 000000000..d0571eaf8 --- /dev/null +++ b/api/src/main/java/me/lucko/luckperms/api/LuckPermsApi.java @@ -0,0 +1,23 @@ +package me.lucko.luckperms.api; + +import java.util.UUID; + +@SuppressWarnings("unused") +public interface LuckPermsApi { + + void runUpdateTask(); + String getVersion(); + + Datastore getDatastore(); + + User getUser(UUID uuid); + User getUser(String name); + boolean isUserLoaded(UUID uuid); + + Group getGroup(String name); + boolean isGroupLoaded(String name); + + Track getTrack(String name); + boolean isTrackLoaded(String name); + +} diff --git a/api/src/main/java/me/lucko/luckperms/api/PermissionObject.java b/api/src/main/java/me/lucko/luckperms/api/PermissionObject.java new file mode 100644 index 000000000..ac9825658 --- /dev/null +++ b/api/src/main/java/me/lucko/luckperms/api/PermissionObject.java @@ -0,0 +1,166 @@ +package me.lucko.luckperms.api; + +import me.lucko.luckperms.exceptions.ObjectAlreadyHasException; +import me.lucko.luckperms.exceptions.ObjectLacksException; + +import java.util.List; +import java.util.Map; + +/** + * Wrapper interface for internal PermissionObject (user/group) instances + * The implementations of this interface limit access to the object and add parameter checks to further prevent + * errors and ensure all API interactions to not damage the state of the object. + */ +@SuppressWarnings("unused") +public interface PermissionObject { + + String getObjectName(); + + Map getNodes(); + + /** + * Checks to see if the object has a certain permission + * @param node The permission node + * @param b If the node is true/false(negated) + * @return true if the user has the permission + */ + boolean hasPermission(String node, boolean b); + + /** + * Checks to see the the object has a permission on a certain server + * @param node The permission node + * @param b If the node is true/false(negated) + * @param server The server + * @return true if the user has the permission + */ + boolean hasPermission(String node, boolean b, String server); + + /** + * Checks to see the the object has a permission on a certain server + * @param node The permission node + * @param b If the node is true/false(negated) + * @param temporary if the permission is temporary + * @return true if the user has the permission + */ + boolean hasPermission(String node, boolean b, boolean temporary); + + /** + * Checks to see if the object inherits a certain permission + * @param node The permission node + * @param b If the node is true/false(negated) + * @return true if the user inherits the permission + */ + boolean inheritsPermission(String node, boolean b); + + /** + * Checks to see the the object inherits a permission on a certain server + * @param node The permission node + * @param b If the node is true/false(negated) + * @param server The server + * @return true if the user inherits the permission + */ + boolean inheritsPermission(String node, boolean b, String server); + + /** + * Checks to see if the object inherits a certain permission + * @param node The permission node + * @param b If the node is true/false(negated) + * @param temporary if the permission is temporary + * @return true if the user inherits the permission + */ + boolean inheritsPermission(String node, boolean b, boolean temporary); + + /** + * Sets a permission for the object + * @param node The node to be set + * @param value What to set the node to - true/false(negated) + * @throws ObjectAlreadyHasException if the object already has the permission + */ + void setPermission(String node, boolean value) throws ObjectAlreadyHasException; + + /** + * Sets a permission for the object + * @param node The node to set + * @param value What to set the node to - true/false(negated) + * @param server The server to set the permission on + * @throws ObjectAlreadyHasException if the object already has the permission + */ + void setPermission(String node, boolean value, String server) throws ObjectAlreadyHasException; + + /** + * Sets a permission for the object + * @param node The node to set + * @param value What to set the node to - true/false(negated) + * @param expireAt The time in unixtime when the permission will expire + * @throws ObjectAlreadyHasException if the object already has the permission + */ + void setPermission(String node, boolean value, long expireAt) throws ObjectAlreadyHasException; + + /** + * Sets a permission for the object + * @param node The node to set + * @param value What to set the node to - true/false(negated) + * @param server The server to set the permission on + * @param expireAt The time in unixtime when the permission will expire + * @throws ObjectAlreadyHasException if the object already has the permission + */ + void setPermission(String node, boolean value, String server, long expireAt) throws ObjectAlreadyHasException; + + /** + * Unsets a permission for the object + * @param node The node to be unset + * @param temporary if the permission being removed is temporary + * @throws ObjectLacksException if the node wasn't already set + */ + void unsetPermission(String node, boolean temporary) throws ObjectLacksException; + + /** + * Unsets a permission for the object + * @param node The node to be unset + * @throws ObjectLacksException if the node wasn't already set + */ + void unsetPermission(String node) throws ObjectLacksException; + + /** + * Unsets a permission for the object + * @param node The node to be unset + * @param server The server to unset the node on + * @throws ObjectLacksException if the node wasn't already set + */ + void unsetPermission(String node, String server) throws ObjectLacksException; + + /** + * Unsets a permission for the object + * @param node The node to be unset + * @param server The server to unset the node on + * @param temporary if the permission being unset is temporary + * @throws ObjectLacksException if the node wasn't already set + */ + void unsetPermission(String node, String server, boolean temporary) throws ObjectLacksException; + + /** + * Gets the permissions and inherited permissions that apply to a specific server + * @param server The server to get nodes for (can be null) + * @param excludedGroups Groups that shouldn't be inherited, can be null (to prevent circular inheritance issues) + * @return a {@link Map} of the permissions + */ + Map getLocalPermissions(String server, List excludedGroups); + + /** + * Processes the objects and returns the temporary ones. + * @return a map of temporary nodes + */ + Map, Long> getTemporaryNodes(); + + /** + * Processes the objects and returns the non-temporary ones. + * @return a map of permanent nodes + */ + Map getPermanentNodes(); + + /** + * Removes temporary permissions that have expired + */ + void auditTemporaryPermissions(); + +} diff --git a/api/src/main/java/me/lucko/luckperms/api/Track.java b/api/src/main/java/me/lucko/luckperms/api/Track.java new file mode 100644 index 000000000..5f8c9f1f3 --- /dev/null +++ b/api/src/main/java/me/lucko/luckperms/api/Track.java @@ -0,0 +1,95 @@ +package me.lucko.luckperms.api; + +import me.lucko.luckperms.exceptions.ObjectAlreadyHasException; +import me.lucko.luckperms.exceptions.ObjectLacksException; + +import java.util.List; + +/** + * Wrapper interface for internal Track instances + * The implementations of this interface limit access to the Track and add parameter checks to further prevent + * errors and ensure all API interactions to not damage the state of the track. + */ +@SuppressWarnings("unused") +public interface Track { + + String getName(); + + /** + * Gets an ordered list of the groups on this track + * @return am ordered {@link List} of the groups on this track + */ + List getGroups(); + + /** + * Gets the number of groups on this track + * @return the number of groups on this track + */ + int getSize(); + + /** + * Gets the next group on the track, after the one provided + * @param current the group before the group being requested + * @return the group name, or null if the end of the track has been reached + * @throws ObjectLacksException if the track does not contain the group given + */ + String getNext(Group current) throws ObjectLacksException; + + /** + * Gets the group before the group provided + * @param current the group after the group being requested + * @return the group name, or null if the start of the track has been reached + * @throws ObjectLacksException if the track does not contain the group given + */ + String getPrevious(Group current) throws ObjectLacksException; + + /** + * Appends a group to the end of this track + * @param group the group to append + * @throws ObjectAlreadyHasException if the group is already on this track somewhere + */ + void appendGroup(Group group) throws ObjectAlreadyHasException; + + /** + * Inserts a group at a certain position on this track + * @param group the group to be inserted + * @param position the index position (a value of 0 inserts at the start) + * @throws ObjectAlreadyHasException if the group is already on this track somewhere + * @throws IndexOutOfBoundsException if the position is less than 0 or greater than the size of the track + */ + void insertGroup(Group group, int position) throws ObjectAlreadyHasException, IndexOutOfBoundsException; + + /** + * Removes a group from this track + * @param group the group to remove + * @throws ObjectLacksException if the group is not on this track + */ + void removeGroup(Group group) throws ObjectLacksException; + + /** + * Removes a group from this track + * @param group the group to remove + * @throws ObjectLacksException if the group is not on this track + */ + void removeGroup(String group) throws ObjectLacksException; + + /** + * Checks if a group features on this track + * @param group the group to check + * @return true if the group is on this track + */ + boolean containsGroup(Group group); + + /** + * Checks if a group features on this track + * @param group the group to check + * @return true if the group is on this track + */ + boolean containsGroup(String group); + + /** + * Clear all of the groups within this track + */ + void clearGroups(); + +} diff --git a/api/src/main/java/me/lucko/luckperms/api/User.java b/api/src/main/java/me/lucko/luckperms/api/User.java new file mode 100644 index 000000000..f09a98862 --- /dev/null +++ b/api/src/main/java/me/lucko/luckperms/api/User.java @@ -0,0 +1,120 @@ +package me.lucko.luckperms.api; + +import me.lucko.luckperms.exceptions.ObjectAlreadyHasException; +import me.lucko.luckperms.exceptions.ObjectLacksException; + +import java.util.List; +import java.util.UUID; + +/** + * Wrapper interface for internal User instances + * The implementations of this interface limit access to the User and add parameter checks to further prevent + * errors and ensure all API interactions to not damage the state of the user. + */ +@SuppressWarnings("unused") +public interface User extends PermissionObject { + + UUID getUuid(); + String getName(); + String getPrimaryGroup(); + void setPrimaryGroup(String s) throws ObjectAlreadyHasException; + void refreshPermissions(); + + /** + * Check to see if the user is a member of a group + * @param group The group to check membership of + * @return true if the user is a member of the group + */ + boolean isInGroup(Group group); + + /** + * Check to see if a user is a member of a group on a specific server + * @param group The group to check membership of + * @param server The server to check on + * @return true if the user is a member of the group + */ + boolean isInGroup(Group group, String server); + + /** + * Add a user to a group + * @param group The group to add the user to + * @throws ObjectAlreadyHasException if the user is already a member of the group + */ + void addGroup(Group group) throws ObjectAlreadyHasException; + + /** + * Add a user to a group on a specific server + * @param group The group to add the user to + * @param server The server to add the group on + * @throws ObjectAlreadyHasException if the user is already a member of the group on that server + */ + void addGroup(Group group, String server) throws ObjectAlreadyHasException; + + /** + * Add a user to a group on a specific server + * @param group The group to add the user to + * @param expireAt when the group should expire + * @throws ObjectAlreadyHasException if the user is already a member of the group on that server + */ + void addGroup(Group group, long expireAt) throws ObjectAlreadyHasException; + + /** + * Add a user to a group on a specific server + * @param group The group to add the user to + * @param server The server to add the group on + * @param expireAt when the group should expire + * @throws ObjectAlreadyHasException if the user is already a member of the group on that server + */ + void addGroup(Group group, String server, long expireAt) throws ObjectAlreadyHasException; + + /** + * Remove the user from a group + * @param group the group to remove the user from + * @throws ObjectLacksException if the user isn't a member of the group + */ + void removeGroup(Group group) throws ObjectLacksException; + + /** + * Remove the user from a group + * @param group the group to remove the user from + * @param temporary if the group being removed is temporary + * @throws ObjectLacksException if the user isn't a member of the group + */ + void removeGroup(Group group, boolean temporary) throws ObjectLacksException; + + /** + * Remove the user from a group + * @param group The group to remove the user from + * @param server The server to remove the group on + * @throws ObjectLacksException if the user isn't a member of the group + */ + void removeGroup(Group group, String server) throws ObjectLacksException; + + /** + * Remove the user from a group + * @param group The group to remove the user from + * @param server The server to remove the group on + * @param temporary if the group being removed is temporary + * @throws ObjectLacksException if the user isn't a member of the group + */ + void removeGroup(Group group, String server, boolean temporary) throws ObjectLacksException; + + /** + * Clear all of the users permission nodes + */ + void clearNodes(); + + /** + * Get a {@link List} of all of the groups the user is a member of, on all servers + * @return a {@link List} of group names + */ + List getGroupNames(); + + /** + * Get a {@link List} of the groups the user is a member of on a specific server + * @param server the server to check + * @return a {@link List} of group names + */ + List getLocalGroups(String server); + +} diff --git a/api/src/main/java/me/lucko/luckperms/api/data/Callback.java b/api/src/main/java/me/lucko/luckperms/api/data/Callback.java new file mode 100644 index 000000000..307610d74 --- /dev/null +++ b/api/src/main/java/me/lucko/luckperms/api/data/Callback.java @@ -0,0 +1,11 @@ +package me.lucko.luckperms.api.data; + +import java.util.UUID; + +public interface Callback { + void onComplete(boolean success); + + interface GetUUID { + void onComplete(UUID uuid); + } +} diff --git a/common/src/main/java/me/lucko/luckperms/exceptions/ObjectAlreadyHasException.java b/api/src/main/java/me/lucko/luckperms/exceptions/ObjectAlreadyHasException.java similarity index 100% rename from common/src/main/java/me/lucko/luckperms/exceptions/ObjectAlreadyHasException.java rename to api/src/main/java/me/lucko/luckperms/exceptions/ObjectAlreadyHasException.java diff --git a/common/src/main/java/me/lucko/luckperms/exceptions/ObjectLacksException.java b/api/src/main/java/me/lucko/luckperms/exceptions/ObjectLacksException.java similarity index 100% rename from common/src/main/java/me/lucko/luckperms/exceptions/ObjectLacksException.java rename to api/src/main/java/me/lucko/luckperms/exceptions/ObjectLacksException.java diff --git a/bukkit/pom.xml b/bukkit/pom.xml index 0909068e0..56c2617f1 100644 --- a/bukkit/pom.xml +++ b/bukkit/pom.xml @@ -5,7 +5,7 @@ luckperms me.lucko - 1.2 + 1.3 4.0.0 @@ -75,5 +75,12 @@ ${project.parent.version} compile + + + me.lucko + luckperms-api + ${project.parent.version} + compile + \ No newline at end of file diff --git a/bukkit/src/main/java/me/lucko/luckperms/LPBukkitPlugin.java b/bukkit/src/main/java/me/lucko/luckperms/LPBukkitPlugin.java index d71077366..6638d24b9 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/LPBukkitPlugin.java +++ b/bukkit/src/main/java/me/lucko/luckperms/LPBukkitPlugin.java @@ -1,6 +1,8 @@ package me.lucko.luckperms; import lombok.Getter; +import me.lucko.luckperms.api.LuckPermsApi; +import me.lucko.luckperms.api.implementation.ApiProvider; import me.lucko.luckperms.data.Datastore; import me.lucko.luckperms.data.MySQLConfiguration; import me.lucko.luckperms.data.methods.FlatfileDatastore; @@ -18,6 +20,7 @@ import org.bukkit.Bukkit; import org.bukkit.command.PluginCommand; import org.bukkit.entity.Player; import org.bukkit.plugin.PluginManager; +import org.bukkit.plugin.ServicePriority; import org.bukkit.plugin.java.JavaPlugin; import java.io.File; @@ -28,7 +31,7 @@ import java.util.stream.Collectors; @Getter public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin { - public static final String VERSION = "v1.2"; + public static final String VERSION = "v1.3"; private LPConfiguration configuration; private UserManager userManager; @@ -84,7 +87,11 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin { // Run update task to refresh any online users getLogger().info("Scheduling Update Task to refresh any online users."); - runUpdateTask(); + try { + new UpdateTask(this).run(); + } catch (Exception e) { + e.printStackTrace(); + } int mins = getConfiguration().getSyncTime(); if (mins > 0) { @@ -106,12 +113,22 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin { e.printStackTrace(); } + getLogger().info("Registering API..."); + final ApiProvider provider = new ApiProvider(this); + LuckPerms.registerProvider(provider); + getServer().getServicesManager().register(LuckPermsApi.class, provider, this, ServicePriority.Normal); + getLogger().info("Successfully loaded."); } @Override public void onDisable() { + getLogger().info("Closing datastore..."); datastore.shutdown(); + + getLogger().info("Unregistering API..."); + LuckPerms.unregisterProvider(); + getServer().getServicesManager().unregisterAll(this); } @Override diff --git a/bungee/pom.xml b/bungee/pom.xml index e1d6889c7..6e6450c5d 100644 --- a/bungee/pom.xml +++ b/bungee/pom.xml @@ -5,7 +5,7 @@ luckperms me.lucko - 1.2 + 1.3 4.0.0 @@ -68,6 +68,13 @@ ${project.parent.version} compile + + + me.lucko + luckperms-api + ${project.parent.version} + compile + \ No newline at end of file diff --git a/bungee/src/main/java/me/lucko/luckperms/LPBungeePlugin.java b/bungee/src/main/java/me/lucko/luckperms/LPBungeePlugin.java index 876c648aa..6ad6a2040 100644 --- a/bungee/src/main/java/me/lucko/luckperms/LPBungeePlugin.java +++ b/bungee/src/main/java/me/lucko/luckperms/LPBungeePlugin.java @@ -1,6 +1,7 @@ package me.lucko.luckperms; import lombok.Getter; +import me.lucko.luckperms.api.implementation.ApiProvider; import me.lucko.luckperms.commands.CommandManager; import me.lucko.luckperms.data.Datastore; import me.lucko.luckperms.data.MySQLConfiguration; @@ -23,7 +24,7 @@ import java.util.stream.Collectors; @Getter public class LPBungeePlugin extends Plugin implements LuckPermsPlugin { - public static final String VERSION = "v1.2"; + public static final String VERSION = "v1.3"; private LPConfiguration configuration; private UserManager userManager; @@ -74,19 +75,30 @@ public class LPBungeePlugin extends Plugin implements LuckPermsPlugin { // Run update task to refresh any online users getLogger().info("Scheduling Update Task to refresh any online users."); - runUpdateTask(); + try { + new UpdateTask(this).run(); + } catch (Exception e) { + e.printStackTrace(); + } int mins = getConfiguration().getSyncTime(); if (mins > 0) { getProxy().getScheduler().schedule(this, new UpdateTask(this), mins, mins, TimeUnit.MINUTES); } + getLogger().info("Registering API..."); + LuckPerms.registerProvider(new ApiProvider(this)); + getLogger().info("Successfully loaded."); } @Override public void onDisable() { + getLogger().info("Closing datastore..."); datastore.shutdown(); + + getLogger().info("Unregistering API..."); + LuckPerms.unregisterProvider(); } @Override diff --git a/common/pom.xml b/common/pom.xml index d08931e66..ce6b9a436 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -5,7 +5,7 @@ luckperms me.lucko - 1.2 + 1.3 4.0.0 @@ -23,4 +23,15 @@ + + + + + me.lucko + luckperms-api + ${project.parent.version} + compile + + + \ No newline at end of file diff --git a/common/src/main/java/me/lucko/luckperms/api/implementation/ApiProvider.java b/common/src/main/java/me/lucko/luckperms/api/implementation/ApiProvider.java new file mode 100644 index 000000000..52d14331e --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/api/implementation/ApiProvider.java @@ -0,0 +1,90 @@ +package me.lucko.luckperms.api.implementation; + +import lombok.AllArgsConstructor; +import lombok.NonNull; +import me.lucko.luckperms.LuckPermsPlugin; +import me.lucko.luckperms.api.*; +import me.lucko.luckperms.api.implementation.internal.DatastoreLink; +import me.lucko.luckperms.api.implementation.internal.GroupLink; +import me.lucko.luckperms.api.implementation.internal.TrackLink; +import me.lucko.luckperms.api.implementation.internal.UserLink; + +import java.util.UUID; + +/** + * Provides static access to LuckPerms + */ +@AllArgsConstructor +public class ApiProvider implements LuckPermsApi { + private final LuckPermsPlugin plugin; + + @Override + public void runUpdateTask() { + plugin.runUpdateTask(); + } + + @Override + public String getVersion() { + return plugin.getVersion(); + } + + @Override + public Datastore getDatastore() { + return new DatastoreLink(plugin.getDatastore()); + } + + @Override + public User getUser(@NonNull UUID uuid) { + final me.lucko.luckperms.users.User user = plugin.getUserManager().getUser(uuid); + if (user == null) { + return null; + } + + return new UserLink(user); + } + + @Override + public User getUser(@NonNull String name) { + final me.lucko.luckperms.users.User user = plugin.getUserManager().getUser(name); + if (user == null) { + return null; + } + + return new UserLink(user); + } + + @Override + public boolean isUserLoaded(@NonNull UUID uuid) { + return plugin.getUserManager().isLoaded(uuid); + } + + @Override + public Group getGroup(@NonNull String name) { + final me.lucko.luckperms.groups.Group group = plugin.getGroupManager().getGroup(name); + if (group == null) { + return null; + } + + return new GroupLink(group); + } + + @Override + public boolean isGroupLoaded(@NonNull String name) { + return plugin.getGroupManager().isLoaded(name); + } + + @Override + public Track getTrack(@NonNull String name) { + final me.lucko.luckperms.tracks.Track track = plugin.getTrackManager().getTrack(name); + if (track == null) { + return null; + } + + return new TrackLink(track); + } + + @Override + public boolean isTrackLoaded(@NonNull String name) { + return plugin.getTrackManager().isLoaded(name); + } +} diff --git a/common/src/main/java/me/lucko/luckperms/api/implementation/internal/DatastoreLink.java b/common/src/main/java/me/lucko/luckperms/api/implementation/internal/DatastoreLink.java new file mode 100644 index 000000000..fa49477f6 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/api/implementation/internal/DatastoreLink.java @@ -0,0 +1,252 @@ +package me.lucko.luckperms.api.implementation.internal; + +import lombok.AllArgsConstructor; +import lombok.NonNull; +import me.lucko.luckperms.api.Datastore; +import me.lucko.luckperms.api.Group; +import me.lucko.luckperms.api.Track; +import me.lucko.luckperms.api.User; +import me.lucko.luckperms.api.data.Callback; +import me.lucko.luckperms.utils.Patterns; + +import java.util.UUID; + +/** + * Provides a link between {@link Datastore} and {@link me.lucko.luckperms.data.Datastore} + */ +@SuppressWarnings({"unused", "WeakerAccess"}) +public class DatastoreLink implements Datastore { + + private final me.lucko.luckperms.data.Datastore master; + private final Async async; + private final Sync sync; + + public DatastoreLink(@NonNull me.lucko.luckperms.data.Datastore master) { + this.master = master; + this.async = new Async(master); + this.sync = new Sync(master); + } + + private static String checkUsername(String s) { + if (s.length() > 16 || Patterns.NON_USERNAME.matcher(s).find()) { + throw new IllegalArgumentException("Invalid username entry '" + s + "'. Usernames must be less than 16 chars" + + " and only contain 'a-z A-Z 1-9 _'."); + } + return s; + } + + private static String checkName(String s) { + if (s.length() > 36 || Patterns.NON_ALPHA_NUMERIC.matcher(s).find()) { + throw new IllegalArgumentException("Invalid name entry '" + s + "'. Names must be less than 37 chars" + + " and only contain 'a-z A-Z 1-9'."); + } + return s.toLowerCase(); + } + + private static Callback checkCallback(Callback c) { + // If no callback was given, just send an empty one + if (c == null) { + c = success -> {}; + } + return c; + } + + private static Callback.GetUUID checkCallback(Callback.GetUUID c) { + // If no callback was given, just send an empty one + if (c == null) { + c = success -> {}; + } + return c; + } + + @Override + public String getName() { + return master.getName(); + } + + @Override + public boolean isAcceptingLogins() { + return master.isAcceptingLogins(); + } + + @Override + public Async async() { + return async; + } + + @Override + public Sync sync() { + return sync; + } + + @AllArgsConstructor + public class Async implements Datastore.Async { + private final me.lucko.luckperms.data.Datastore master; + + @Override + public void loadOrCreateUser(@NonNull UUID uuid, @NonNull String username, Callback callback) { + master.loadOrCreateUser(uuid, checkUsername(username), checkCallback(callback)); + } + + @Override + public void loadUser(@NonNull UUID uuid, Callback callback) { + master.loadUser(uuid, checkCallback(callback)); + } + + @Override + public void saveUser(@NonNull User user, Callback callback) { + Utils.checkUser(user); + master.saveUser(((UserLink) user).getMaster(), checkCallback(callback)); + } + + @Override + public void createAndLoadGroup(@NonNull String name, Callback callback) { + master.createAndLoadGroup(checkName(name), checkCallback(callback)); + } + + @Override + public void loadGroup(@NonNull String name, Callback callback) { + master.loadGroup(checkName(name), checkCallback(callback)); + } + + @Override + public void loadAllGroups(Callback callback) { + master.loadAllGroups(checkCallback(callback)); + } + + @Override + public void saveGroup(@NonNull Group group, Callback callback) { + Utils.checkGroup(group); + master.saveGroup(((GroupLink) group).getMaster(), checkCallback(callback)); + } + + @Override + public void deleteGroup(@NonNull Group group, Callback callback) { + Utils.checkGroup(group); + master.deleteGroup(((GroupLink) group).getMaster(), checkCallback(callback)); + } + + @Override + public void createAndLoadTrack(@NonNull String name, Callback callback) { + master.createAndLoadTrack(checkName(name), checkCallback(callback)); + } + + @Override + public void loadTrack(@NonNull String name, Callback callback) { + master.loadTrack(checkName(name), checkCallback(callback)); + } + + @Override + public void loadAllTracks(Callback callback) { + master.loadAllTracks(checkCallback(callback)); + } + + @Override + public void saveTrack(@NonNull Track track, Callback callback) { + Utils.checkTrack(track); + master.saveTrack(((TrackLink) track).getMaster(), checkCallback(callback)); + } + + @Override + public void deleteTrack(@NonNull Track track, Callback callback) { + Utils.checkTrack(track); + master.deleteTrack(((TrackLink) track).getMaster(), checkCallback(callback)); + } + + @Override + public void saveUUIDData(@NonNull String username, @NonNull UUID uuid, Callback callback) { + master.saveUUIDData(checkUsername(username), uuid, checkCallback(callback)); + } + + @Override + public void getUUID(@NonNull String username, Callback.GetUUID callback) { + master.getUUID(checkUsername(username), checkCallback(callback)); + } + } + + @AllArgsConstructor + public class Sync implements Datastore.Sync { + private final me.lucko.luckperms.data.Datastore master; + + @Override + public boolean loadOrCreateUser(@NonNull UUID uuid, @NonNull String username) { + return master.loadOrCreateUser(uuid, checkUsername(username)); + } + + @Override + public boolean loadUser(@NonNull UUID uuid) { + return master.loadUser(uuid); + } + + @Override + public boolean saveUser(@NonNull User user) { + Utils.checkUser(user); + return master.saveUser(((UserLink) user).getMaster()); + } + + @Override + public boolean createAndLoadGroup(@NonNull String name) { + return master.createAndLoadGroup(checkName(name)); + } + + @Override + public boolean loadGroup(@NonNull String name) { + return master.loadGroup(checkName(name)); + } + + @Override + public boolean loadAllGroups() { + return master.loadAllGroups(); + } + + @Override + public boolean saveGroup(@NonNull Group group) { + Utils.checkGroup(group); + return master.saveGroup(((GroupLink) group).getMaster()); + } + + @Override + public boolean deleteGroup(@NonNull Group group) { + Utils.checkGroup(group); + return master.deleteGroup(((GroupLink) group).getMaster()); + } + + @Override + public boolean createAndLoadTrack(@NonNull String name) { + return master.createAndLoadTrack(checkName(name)); + } + + @Override + public boolean loadTrack(@NonNull String name) { + return master.loadTrack(checkName(name)); + } + + @Override + public boolean loadAllTracks() { + return master.loadAllTracks(); + } + + @Override + public boolean saveTrack(@NonNull Track track) { + Utils.checkTrack(track); + return master.saveTrack(((TrackLink) track).getMaster()); + } + + @Override + public boolean deleteTrack(@NonNull Track track) { + Utils.checkTrack(track); + return master.deleteTrack(((TrackLink) track).getMaster()); + } + + @Override + public boolean saveUUIDData(@NonNull String username, @NonNull UUID uuid) { + return master.saveUUIDData(checkUsername(username), uuid); + } + + @Override + public UUID getUUID(@NonNull String username) { + return master.getUUID(checkUsername(username)); + } + } + +} diff --git a/common/src/main/java/me/lucko/luckperms/api/implementation/internal/GroupLink.java b/common/src/main/java/me/lucko/luckperms/api/implementation/internal/GroupLink.java new file mode 100644 index 000000000..b94806ba1 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/api/implementation/internal/GroupLink.java @@ -0,0 +1,31 @@ +package me.lucko.luckperms.api.implementation.internal; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NonNull; +import me.lucko.luckperms.api.Group; + +/** + * Provides a link between {@link Group} and {@link me.lucko.luckperms.groups.Group} + */ +@SuppressWarnings("unused") +public class GroupLink extends PermissionObjectLink implements Group { + + @Getter(AccessLevel.PACKAGE) + private final me.lucko.luckperms.groups.Group master; + + public GroupLink(@NonNull me.lucko.luckperms.groups.Group master) { + super(master); + this.master = master; + } + + @Override + public String getName() { + return master.getName(); + } + + @Override + public void clearNodes() { + master.clearNodes(); + } +} diff --git a/common/src/main/java/me/lucko/luckperms/api/implementation/internal/PermissionObjectLink.java b/common/src/main/java/me/lucko/luckperms/api/implementation/internal/PermissionObjectLink.java new file mode 100644 index 000000000..a28f448d9 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/api/implementation/internal/PermissionObjectLink.java @@ -0,0 +1,147 @@ +package me.lucko.luckperms.api.implementation.internal; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.NonNull; +import me.lucko.luckperms.api.PermissionObject; +import me.lucko.luckperms.exceptions.ObjectAlreadyHasException; +import me.lucko.luckperms.exceptions.ObjectLacksException; +import me.lucko.luckperms.utils.DateUtil; +import me.lucko.luckperms.utils.Patterns; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +/** + * Provides a link between {@link PermissionObject} and {@link me.lucko.luckperms.utils.PermissionObject} + */ +@SuppressWarnings("unused") +@AllArgsConstructor(access = AccessLevel.PACKAGE) +class PermissionObjectLink implements PermissionObject { + + @NonNull + private final me.lucko.luckperms.utils.PermissionObject master; + + static String checkServer(String s) { + if (Patterns.NON_ALPHA_NUMERIC.matcher(s).find()) { + throw new IllegalArgumentException("Invalid server entry '" + s + "'. Server names can only contain alphanumeric characters."); + } + return s; + } + + private static String checkNode(String s) { + if (s.contains("/") || s.contains("$")) { + throw new IllegalArgumentException("Invalid node entry '" + s + "'. Nodes cannot contain '/' or '$' characters."); + } + return s; + } + + static long checkTime(long l) { + if (DateUtil.shouldExpire(l)) { + throw new IllegalArgumentException("Unix time '" + l + "' is invalid, as it has already passed."); + } + return l; + } + + @Override + public String getObjectName() { + return master.getObjectName(); + } + + @Override + public Map getNodes() { + return Collections.unmodifiableMap(master.getNodes()); + } + + @Override + public boolean hasPermission(@NonNull String node, @NonNull boolean b) { + return master.hasPermission(node, b); + } + + @Override + public boolean hasPermission(@NonNull String node, @NonNull boolean b, @NonNull String server) { + return master.hasPermission(node, b, checkServer(server)); + } + + @Override + public boolean hasPermission(@NonNull String node, @NonNull boolean b, @NonNull boolean temporary) { + return master.hasPermission(node, b, temporary); + } + + @Override + public boolean inheritsPermission(@NonNull String node, @NonNull boolean b) { + return master.inheritsPermission(node, b); + } + + @Override + public boolean inheritsPermission(@NonNull String node, @NonNull boolean b, @NonNull String server) { + return master.inheritsPermission(node, b, checkServer(server)); + } + + @Override + public boolean inheritsPermission(@NonNull String node, @NonNull boolean b, @NonNull boolean temporary) { + return master.inheritsPermission(node, b, temporary); + } + + @Override + public void setPermission(@NonNull String node, @NonNull boolean value) throws ObjectAlreadyHasException { + master.setPermission(checkNode(node), value); + } + + @Override + public void setPermission(@NonNull String node, @NonNull boolean value, @NonNull String server) throws ObjectAlreadyHasException { + master.setPermission(checkNode(node), value, checkServer(server)); + } + + @Override + public void setPermission(@NonNull String node, @NonNull boolean value, @NonNull long expireAt) throws ObjectAlreadyHasException { + master.setPermission(checkNode(node), value, checkTime(expireAt)); + } + + @Override + public void setPermission(@NonNull String node, @NonNull boolean value, @NonNull String server, @NonNull long expireAt) throws ObjectAlreadyHasException { + master.setPermission(checkNode(node), value, checkServer(server), checkTime(expireAt)); + } + + @Override + public void unsetPermission(@NonNull String node, @NonNull boolean temporary) throws ObjectLacksException { + master.unsetPermission(checkNode(node), temporary); + } + + @Override + public void unsetPermission(@NonNull String node) throws ObjectLacksException { + master.unsetPermission(checkNode(node)); + } + + @Override + public void unsetPermission(@NonNull String node, @NonNull String server) throws ObjectLacksException { + master.unsetPermission(checkNode(node), checkServer(server)); + } + + @Override + public void unsetPermission(@NonNull String node, @NonNull String server, @NonNull boolean temporary) throws ObjectLacksException { + master.unsetPermission(checkNode(node), checkServer(server), temporary); + } + + @Override + public Map getLocalPermissions(String server, List excludedGroups) { + return master.getLocalPermissions(server, excludedGroups); + } + + @Override + public Map, Long> getTemporaryNodes() { + return master.getTemporaryNodes(); + } + + @Override + public Map getPermanentNodes() { + return master.getPermanentNodes(); + } + + @Override + public void auditTemporaryPermissions() { + master.auditTemporaryPermissions(); + } + +} diff --git a/common/src/main/java/me/lucko/luckperms/api/implementation/internal/TrackLink.java b/common/src/main/java/me/lucko/luckperms/api/implementation/internal/TrackLink.java new file mode 100644 index 000000000..773b3349b --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/api/implementation/internal/TrackLink.java @@ -0,0 +1,90 @@ +package me.lucko.luckperms.api.implementation.internal; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NonNull; +import me.lucko.luckperms.api.Group; +import me.lucko.luckperms.api.Track; +import me.lucko.luckperms.exceptions.ObjectAlreadyHasException; +import me.lucko.luckperms.exceptions.ObjectLacksException; + +import java.util.List; + +/** + * Provides a link between {@link Track} and {@link me.lucko.luckperms.tracks.Track} + */ +@SuppressWarnings("unused") +@AllArgsConstructor +public class TrackLink implements Track { + + @NonNull + @Getter(AccessLevel.PACKAGE) + private final me.lucko.luckperms.tracks.Track master; + + @Override + public String getName() { + return master.getName(); + } + + @Override + public List getGroups() { + return master.getGroups(); + } + + @Override + public int getSize() { + return master.getSize(); + } + + @Override + public String getNext(@NonNull Group current) throws ObjectLacksException { + Utils.checkGroup(current); + return master.getNext(((GroupLink) current).getMaster()); + } + + @Override + public String getPrevious(@NonNull Group current) throws ObjectLacksException { + Utils.checkGroup(current); + return master.getPrevious(((GroupLink) current).getMaster()); + } + + @Override + public void appendGroup(@NonNull Group group) throws ObjectAlreadyHasException { + Utils.checkGroup(group); + master.appendGroup(((GroupLink) group).getMaster()); + } + + @Override + public void insertGroup(@NonNull Group group, @NonNull int position) throws ObjectAlreadyHasException, IndexOutOfBoundsException { + Utils.checkGroup(group); + master.insertGroup(((GroupLink) group).getMaster(), position); + } + + @Override + public void removeGroup(@NonNull Group group) throws ObjectLacksException { + Utils.checkGroup(group); + master.removeGroup(((GroupLink) group).getMaster()); + } + + @Override + public void removeGroup(@NonNull String group) throws ObjectLacksException { + master.removeGroup(group); + } + + @Override + public boolean containsGroup(@NonNull Group group) { + Utils.checkGroup(group); + return master.containsGroup(((GroupLink) group).getMaster()); + } + + @Override + public boolean containsGroup(@NonNull String group) { + return master.containsGroup(group); + } + + @Override + public void clearGroups() { + master.clearGroups(); + } +} diff --git a/common/src/main/java/me/lucko/luckperms/api/implementation/internal/UserLink.java b/common/src/main/java/me/lucko/luckperms/api/implementation/internal/UserLink.java new file mode 100644 index 000000000..12256b4d0 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/api/implementation/internal/UserLink.java @@ -0,0 +1,136 @@ +package me.lucko.luckperms.api.implementation.internal; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NonNull; +import me.lucko.luckperms.api.Group; +import me.lucko.luckperms.api.User; +import me.lucko.luckperms.exceptions.ObjectAlreadyHasException; +import me.lucko.luckperms.exceptions.ObjectLacksException; + +import java.util.List; +import java.util.UUID; + +/** + * Provides a link between {@link User} and {@link me.lucko.luckperms.users.User} + */ +@SuppressWarnings("unused") +public class UserLink extends PermissionObjectLink implements User { + + @Getter(AccessLevel.PACKAGE) + private final me.lucko.luckperms.users.User master; + + public UserLink(@NonNull me.lucko.luckperms.users.User master) { + super(master); + this.master = master; + } + + @Override + public UUID getUuid() { + return master.getUuid(); + } + + @Override + public String getName() { + return master.getName(); + } + + @Override + public String getPrimaryGroup() { + return master.getPrimaryGroup(); + } + + @Override + public void setPrimaryGroup(String s) throws ObjectAlreadyHasException { + if (getPrimaryGroup().equalsIgnoreCase(s)) { + throw new ObjectAlreadyHasException(); + } + + if (!getGroupNames().contains(s.toLowerCase())) { + throw new IllegalStateException("User is not a member of that group."); + } + + master.setPrimaryGroup(s.toLowerCase()); + } + + @Override + public void refreshPermissions() { + master.refreshPermissions(); + } + + @Override + public boolean isInGroup(@NonNull Group group) { + Utils.checkGroup(group); + return master.isInGroup(((GroupLink) group).getMaster()); + } + + @Override + public boolean isInGroup(@NonNull Group group, @NonNull String server) { + Utils.checkGroup(group); + return master.isInGroup(((GroupLink) group).getMaster(), server); + } + + @Override + public void addGroup(@NonNull Group group) throws ObjectAlreadyHasException { + Utils.checkGroup(group); + master.addGroup(((GroupLink) group).getMaster()); + } + + @Override + public void addGroup(@NonNull Group group, @NonNull String server) throws ObjectAlreadyHasException { + Utils.checkGroup(group); + master.addGroup(((GroupLink) group).getMaster(), checkServer(server)); + } + + @Override + public void addGroup(@NonNull Group group, @NonNull long expireAt) throws ObjectAlreadyHasException { + Utils.checkGroup(group); + master.addGroup(((GroupLink) group).getMaster(), checkTime(expireAt)); + } + + @Override + public void addGroup(@NonNull Group group, @NonNull String server, @NonNull long expireAt) throws ObjectAlreadyHasException { + Utils.checkGroup(group); + master.addGroup(((GroupLink) group).getMaster(), checkServer(server), checkTime(expireAt)); + } + + @Override + public void removeGroup(@NonNull Group group) throws ObjectLacksException { + Utils.checkGroup(group); + master.removeGroup(((GroupLink) group).getMaster()); + } + + @Override + public void removeGroup(@NonNull Group group, @NonNull boolean temporary) throws ObjectLacksException { + Utils.checkGroup(group); + master.removeGroup(((GroupLink) group).getMaster(), temporary); + } + + @Override + public void removeGroup(@NonNull Group group, @NonNull String server) throws ObjectLacksException { + Utils.checkGroup(group); + master.removeGroup(((GroupLink) group).getMaster(), checkServer(server)); + } + + @Override + public void removeGroup(@NonNull Group group, @NonNull String server, @NonNull boolean temporary) throws ObjectLacksException { + Utils.checkGroup(group); + master.removeGroup(((GroupLink) group).getMaster(), checkServer(server), temporary); + } + + @Override + public void clearNodes() { + master.clearNodes(); + } + + @Override + public List getGroupNames() { + return master.getGroupNames(); + } + + @Override + public List getLocalGroups(@NonNull String server) { + return master.getLocalGroups(checkServer(server)); + } + +} diff --git a/common/src/main/java/me/lucko/luckperms/api/implementation/internal/Utils.java b/common/src/main/java/me/lucko/luckperms/api/implementation/internal/Utils.java new file mode 100644 index 000000000..efed0d6b4 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/api/implementation/internal/Utils.java @@ -0,0 +1,27 @@ +package me.lucko.luckperms.api.implementation.internal; + +import me.lucko.luckperms.api.Group; +import me.lucko.luckperms.api.Track; +import me.lucko.luckperms.api.User; + +class Utils { + + static void checkUser(User user) { + if (!(user instanceof UserLink)) { + throw new IllegalArgumentException("User instance cannot be handled by this implementation."); + } + } + + static void checkGroup(Group group) { + if (!(group instanceof GroupLink)) { + throw new IllegalArgumentException("Group instance cannot be handled by this implementation."); + } + } + + static void checkTrack(Track track) { + if (!(track instanceof TrackLink)) { + throw new IllegalArgumentException("Track instance cannot be handled by this implementation."); + } + } + +} diff --git a/common/src/main/java/me/lucko/luckperms/commands/Util.java b/common/src/main/java/me/lucko/luckperms/commands/Util.java index f9fc85732..174d6dae4 100644 --- a/common/src/main/java/me/lucko/luckperms/commands/Util.java +++ b/common/src/main/java/me/lucko/luckperms/commands/Util.java @@ -1,7 +1,6 @@ package me.lucko.luckperms.commands; -import lombok.AccessLevel; -import lombok.NoArgsConstructor; +import lombok.experimental.UtilityClass; import me.lucko.luckperms.constants.Message; import me.lucko.luckperms.utils.DateUtil; @@ -9,7 +8,7 @@ import java.util.List; import java.util.Map; import java.util.UUID; -@NoArgsConstructor(access = AccessLevel.PRIVATE) +@UtilityClass public class Util { public static void sendPluginMessage(Sender sender, String message) { diff --git a/common/src/main/java/me/lucko/luckperms/data/Datastore.java b/common/src/main/java/me/lucko/luckperms/data/Datastore.java index 84595eab0..5e931d6ae 100644 --- a/common/src/main/java/me/lucko/luckperms/data/Datastore.java +++ b/common/src/main/java/me/lucko/luckperms/data/Datastore.java @@ -5,6 +5,7 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.Setter; import me.lucko.luckperms.LuckPermsPlugin; +import me.lucko.luckperms.api.data.Callback; import me.lucko.luckperms.groups.Group; import me.lucko.luckperms.tracks.Track; import me.lucko.luckperms.users.User; @@ -42,6 +43,10 @@ public abstract class Datastore { doSync(() -> callback.onComplete(result)); } + private void runCallback(UUID result, Callback.GetUUID callback) { + doSync(() -> callback.onComplete(result)); + } + /* These methods are called immediately and in the same thread as they are called in. */ @@ -125,15 +130,7 @@ public abstract class Datastore { doAsync(() -> runCallback(saveUUIDData(username, uuid), callback)); } - public void getUUID(String username, GetUUIDCallback callback) { - doAsync(() -> doSync(() -> callback.onComplete(getUUID(username)))); - } - - public interface Callback { - void onComplete(boolean success); - } - - public interface GetUUIDCallback { - void onComplete(UUID uuid); + public void getUUID(String username, Callback.GetUUID callback) { + doAsync(() -> runCallback(getUUID(username), callback)); } } diff --git a/common/src/main/java/me/lucko/luckperms/data/methods/SQLDatastore.java b/common/src/main/java/me/lucko/luckperms/data/methods/SQLDatastore.java index e9bfe21f8..36a8b776c 100644 --- a/common/src/main/java/me/lucko/luckperms/data/methods/SQLDatastore.java +++ b/common/src/main/java/me/lucko/luckperms/data/methods/SQLDatastore.java @@ -66,7 +66,7 @@ abstract class SQLDatastore extends Datastore { throw new IllegalStateException("SQL connection is null"); } - @Cleanup PreparedStatement preparedStatement = connection.prepareStatement(queryPS.getQuery()); + @Cleanup PreparedStatement preparedStatement = connection.prepareStatement(queryPS.getQuery()); queryPS.onRun(preparedStatement); preparedStatement.execute(); success = true; @@ -389,6 +389,8 @@ abstract class SQLDatastore extends Datastore { preparedStatement.setString(1, track.getName()); } }); + + if (success) plugin.getTrackManager().unloadTrack(track); return success; } diff --git a/common/src/main/java/me/lucko/luckperms/utils/DateUtil.java b/common/src/main/java/me/lucko/luckperms/utils/DateUtil.java index ec4276a92..ceccbac8f 100644 --- a/common/src/main/java/me/lucko/luckperms/utils/DateUtil.java +++ b/common/src/main/java/me/lucko/luckperms/utils/DateUtil.java @@ -1,7 +1,6 @@ package me.lucko.luckperms.utils; -import lombok.AccessLevel; -import lombok.NoArgsConstructor; +import lombok.experimental.UtilityClass; import java.util.Calendar; import java.util.GregorianCalendar; @@ -13,7 +12,7 @@ import java.util.regex.Pattern; * https://github.com/drtshock/Essentials/blob/2.x/Essentials/src/com/earth2me/essentials/utils/DateUtil.java * https://github.com/essentials/Essentials/blob/2.x/Essentials/src/com/earth2me/essentials/utils/DateUtil.java */ -@NoArgsConstructor(access = AccessLevel.PRIVATE) +@UtilityClass public class DateUtil { private static final Pattern TIME_PATTERN = Pattern.compile("(?:([0-9]+)\\s*y[a-z]*[,\\s]*)?" + "(?:([0-9]+)\\s*mo[a-z]*[,\\s]*)?" + "(?:([0-9]+)\\s*w[a-z]*[,\\s]*)?" + "(?:([0-9]+)\\s*d[a-z]*[,\\s]*)?" + "(?:([0-9]+)\\s*h[a-z]*[,\\s]*)?" + "(?:([0-9]+)\\s*m[a-z]*[,\\s]*)?" + "(?:([0-9]+)\\s*(?:s[a-z]*)?)?", Pattern.CASE_INSENSITIVE); private static final int MAX_YEARS = 100000; diff --git a/common/src/main/java/me/lucko/luckperms/utils/Patterns.java b/common/src/main/java/me/lucko/luckperms/utils/Patterns.java index d5cf02d32..8dea0935a 100644 --- a/common/src/main/java/me/lucko/luckperms/utils/Patterns.java +++ b/common/src/main/java/me/lucko/luckperms/utils/Patterns.java @@ -1,11 +1,10 @@ package me.lucko.luckperms.utils; -import lombok.AccessLevel; -import lombok.NoArgsConstructor; +import lombok.experimental.UtilityClass; import java.util.regex.Pattern; -@NoArgsConstructor(access = AccessLevel.PRIVATE) +@UtilityClass public class Patterns { public static final Pattern SERVER_SPLIT = Pattern.compile("\\/"); public static final Pattern TEMP_SPLIT = Pattern.compile("\\$"); diff --git a/pom.xml b/pom.xml index 023bda538..940cd554a 100644 --- a/pom.xml +++ b/pom.xml @@ -7,11 +7,12 @@ me.lucko luckperms - 1.2 + 1.3 bukkit common bungee + api pom @@ -61,4 +62,4 @@ provided - \ No newline at end of file +