Del all files
This commit is contained in:
parent
912e607144
commit
b014da245d
674
LICENSE
674
LICENSE
@ -1,674 +0,0 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
{one line to give the program's name and a brief idea of what it does.}
|
||||
Copyright (C) {year} {name of author}
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
{project} Copyright (C) {year} {fullname}
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||
21
README.md
21
README.md
@ -1,21 +0,0 @@
|
||||
# AuthMeReReloaded
|
||||
**"A Fork of Authme that contains many bug fixes!"**
|
||||
**请前往Releases下载最新版本**(**Download the latest version from Releases**)
|
||||
|
||||
|
||||
|
||||
|
||||
Changes to the original AuthMe(相较于原版AuthMe的变化):
|
||||
1.Memory improvement
|
||||
2.Fixed GeoIP database
|
||||
3.Hook floodgate
|
||||
4.Choose the best performance method by server brand
|
||||
5.Anti ghost player
|
||||
6.GUI captcha for unregistered players
|
||||
7.Block unsafe passwords by querying API
|
||||
8.Self hash check to prevent jar virus infection(abandoned)
|
||||
9.Updater
|
||||
|
||||
|
||||
**Known Folia issue**
|
||||
1.If the teleport distance too far away,the player may be frozen
|
||||
111
docs/commands.md
111
docs/commands.md
@ -1,111 +0,0 @@
|
||||
<!-- AUTO-GENERATED FILE! Do not edit this directly -->
|
||||
<!-- File auto-generated on Sun Apr 04 21:31:42 CEST 2021. See docs/commands/commands.tpl.md -->
|
||||
|
||||
## AuthMe Commands
|
||||
You can use the following commands to use the features of AuthMe. Mandatory arguments are marked with `< >`
|
||||
brackets; optional arguments are enclosed in square brackets (`[ ]`).
|
||||
|
||||
- **/authme**: The main AuthMeReloaded command. The root for all admin commands.
|
||||
- **/authme register** <player> <password>: Register the specified player with the specified password.
|
||||
<br />Requires `authme.admin.register`
|
||||
- **/authme unregister** <player>: Unregister the specified player.
|
||||
<br />Requires `authme.admin.unregister`
|
||||
- **/authme forcelogin** [player]: Enforce the specified player to login.
|
||||
<br />Requires `authme.admin.forcelogin`
|
||||
- **/authme password** <player> <pwd>: Change the password of a player.
|
||||
<br />Requires `authme.admin.changepassword`
|
||||
- **/authme lastlogin** [player]: View the date of the specified players last login.
|
||||
<br />Requires `authme.admin.lastlogin`
|
||||
- **/authme accounts** [player]: Display all accounts of a player by his player name or IP.
|
||||
<br />Requires `authme.admin.accounts`
|
||||
- **/authme email** [player]: Display the email address of the specified player if set.
|
||||
<br />Requires `authme.admin.getemail`
|
||||
- **/authme setemail** <player> <email>: Change the email address of the specified player.
|
||||
<br />Requires `authme.admin.changemail`
|
||||
- **/authme getip** <player>: Get the IP address of the specified online player.
|
||||
<br />Requires `authme.admin.getip`
|
||||
- **/authme totp** <player>: Returns whether the specified player has enabled two-factor authentication.
|
||||
<br />Requires `authme.admin.totpviewstatus`
|
||||
- **/authme disabletotp** <player>: Disable two-factor authentication for a player.
|
||||
<br />Requires `authme.admin.totpdisable`
|
||||
- **/authme spawn**: Teleport to the spawn.
|
||||
<br />Requires `authme.admin.spawn`
|
||||
- **/authme setspawn**: Change the player's spawn to your current position.
|
||||
<br />Requires `authme.admin.setspawn`
|
||||
- **/authme firstspawn**: Teleport to the first spawn.
|
||||
<br />Requires `authme.admin.firstspawn`
|
||||
- **/authme setfirstspawn**: Change the first player's spawn to your current position.
|
||||
<br />Requires `authme.admin.setfirstspawn`
|
||||
- **/authme purge** <days>: Purge old AuthMeReloaded data longer than the specified number of days ago.
|
||||
<br />Requires `authme.admin.purge`
|
||||
- **/authme purgeplayer** <player> [options]: Purges data of the given player.
|
||||
<br />Requires `authme.admin.purgeplayer`
|
||||
- **/authme backup**: Creates a backup of the registered users.
|
||||
<br />Requires `authme.admin.backup`
|
||||
- **/authme resetpos** <player/*>: Purge the last know position of the specified player or all of them.
|
||||
<br />Requires `authme.admin.purgelastpos`
|
||||
- **/authme purgebannedplayers**: Purge all AuthMeReloaded data for banned players.
|
||||
<br />Requires `authme.admin.purgebannedplayers`
|
||||
- **/authme switchantibot** [mode]: Switch or toggle the AntiBot mode to the specified state.
|
||||
<br />Requires `authme.admin.switchantibot`
|
||||
- **/authme reload**: Reload the AuthMeReloaded plugin.
|
||||
<br />Requires `authme.admin.reload`
|
||||
- **/authme version**: Show detailed information about the installed AuthMeReloaded version, the developers, contributors, and license.
|
||||
- **/authme converter** [job]: Converter command for AuthMeReloaded.
|
||||
<br />Requires `authme.admin.converter`
|
||||
- **/authme messages**: Adds missing texts to the current help messages file.
|
||||
<br />Requires `authme.admin.updatemessages`
|
||||
- **/authme recent**: Shows the last players that have logged in.
|
||||
<br />Requires `authme.admin.seerecent`
|
||||
- **/authme debug** [child] [arg] [arg]: Allows various operations for debugging.
|
||||
<br />Requires `authme.debug.command`
|
||||
- **/authme help** [query]: View detailed help for /authme commands.
|
||||
- **/email**: The AuthMeReloaded email command base.
|
||||
- **/email show**: Show your current email address.
|
||||
<br />Requires `authme.player.email.see`
|
||||
- **/email add** <email> <verifyEmail>: Add a new email address to your account.
|
||||
<br />Requires `authme.player.email.add`
|
||||
- **/email change** <oldEmail> <newEmail>: Change an email address of your account.
|
||||
<br />Requires `authme.player.email.change`
|
||||
- **/email recover** <email>: Recover your account using an Email address by sending a mail containing a new password.
|
||||
<br />Requires `authme.player.email.recover`
|
||||
- **/email code** <code>: Recover your account by submitting a code delivered to your email.
|
||||
<br />Requires `authme.player.email.recover`
|
||||
- **/email setpassword** <password>: Set a new password after successfully recovering your account.
|
||||
<br />Requires `authme.player.email.recover`
|
||||
- **/email help** [query]: View detailed help for /email commands.
|
||||
- **/login** <password>: Command to log in using AuthMeReloaded.
|
||||
<br />Requires `authme.player.login`
|
||||
- **/login help** [query]: View detailed help for /login commands.
|
||||
- **/logout**: Command to logout using AuthMeReloaded.
|
||||
<br />Requires `authme.player.logout`
|
||||
- **/logout help** [query]: View detailed help for /logout commands.
|
||||
- **/register** [password] [verifyPassword]: Command to register using AuthMeReloaded.
|
||||
<br />Requires `authme.player.register`
|
||||
- **/register help** [query]: View detailed help for /register commands.
|
||||
- **/unregister** <password>: Command to unregister using AuthMeReloaded.
|
||||
<br />Requires `authme.player.unregister`
|
||||
- **/unregister help** [query]: View detailed help for /unregister commands.
|
||||
- **/changepassword** <oldPassword> <newPassword>: Command to change your password using AuthMeReloaded.
|
||||
<br />Requires `authme.player.changepassword`
|
||||
- **/changepassword help** [query]: View detailed help for /changepassword commands.
|
||||
- **/totp**: Performs actions related to two-factor authentication.
|
||||
- **/totp code** <code>: Processes the two-factor authentication code during login.
|
||||
- **/totp add**: Enables two-factor authentication for your account.
|
||||
<br />Requires `authme.player.totpadd`
|
||||
- **/totp confirm** <code>: Saves the generated TOTP secret after confirmation.
|
||||
<br />Requires `authme.player.totpadd`
|
||||
- **/totp remove** <code>: Disables two-factor authentication for your account.
|
||||
<br />Requires `authme.player.totpremove`
|
||||
- **/totp help** [query]: View detailed help for /totp commands.
|
||||
- **/captcha** <captcha>: Captcha command for AuthMeReloaded.
|
||||
<br />Requires `authme.player.captcha`
|
||||
- **/captcha help** [query]: View detailed help for /captcha commands.
|
||||
- **/verification** <code>: Command to complete the verification process for AuthMeReloaded.
|
||||
<br />Requires `authme.player.security.verificationcode`
|
||||
- **/verification help** [query]: View detailed help for /verification commands.
|
||||
|
||||
|
||||
---
|
||||
|
||||
This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sun Apr 04 21:31:42 CEST 2021
|
||||
584
docs/config.md
584
docs/config.md
@ -1,584 +0,0 @@
|
||||
<!-- AUTO-GENERATED FILE! Do not edit this directly -->
|
||||
<!-- File auto-generated on Thu Jul 28 18:11:22 CEST 2022. See docs/config/config.tpl.md -->
|
||||
|
||||
## AuthMe Configuration
|
||||
The first time you run AuthMe it will create a config.yml file in the plugins/AuthMe folder,
|
||||
with which you can configure various settings. The following is the initial contents of
|
||||
the generated config.yml file.
|
||||
|
||||
```yml
|
||||
DataSource:
|
||||
# What type of database do you want to use?
|
||||
# Valid values: SQLITE, MARIADB, MYSQL, POSTGRESQL
|
||||
backend: SQLITE
|
||||
# Enable the database caching system, should be disabled on bungeecord environments
|
||||
# or when a website integration is being used.
|
||||
caching: true
|
||||
# Database host address
|
||||
mySQLHost: 127.0.0.1
|
||||
# Database port
|
||||
mySQLPort: '3306'
|
||||
# Connect to MySQL database over SSL
|
||||
mySQLUseSSL: true
|
||||
# Verification of server's certificate.
|
||||
# We would not recommend to set this option to false.
|
||||
# Set this option to false at your own risk if and only if you know what you're doing
|
||||
mySQLCheckServerCertificate: true
|
||||
# Authorize client to retrieve RSA server public key.
|
||||
# Advanced option, ignore if you don't know what it means.
|
||||
mySQLAllowPublicKeyRetrieval: true
|
||||
# Username to connect to the MySQL database
|
||||
mySQLUsername: authme
|
||||
# Password to connect to the MySQL database
|
||||
mySQLPassword: '12345'
|
||||
# Database Name, use with converters or as SQLITE database name
|
||||
mySQLDatabase: authme
|
||||
# Table of the database
|
||||
mySQLTablename: authme
|
||||
# Column of IDs to sort data
|
||||
mySQLColumnId: id
|
||||
# Column for storing or checking players nickname
|
||||
mySQLColumnName: username
|
||||
# Column for storing or checking players RealName
|
||||
mySQLRealName: realname
|
||||
# Column for storing players passwords
|
||||
mySQLColumnPassword: password
|
||||
# Column for storing players passwords salts
|
||||
mySQLColumnSalt: ''
|
||||
# Column for storing players emails
|
||||
mySQLColumnEmail: email
|
||||
# Column for storing if a player is logged in or not
|
||||
mySQLColumnLogged: isLogged
|
||||
# Column for storing if a player has a valid session or not
|
||||
mySQLColumnHasSession: hasSession
|
||||
# Column for storing a player's TOTP key (for two-factor authentication)
|
||||
mySQLtotpKey: totp
|
||||
# Column for storing the player's last IP
|
||||
mySQLColumnIp: ip
|
||||
# Column for storing players lastlogins
|
||||
mySQLColumnLastLogin: lastlogin
|
||||
# Column storing the registration date
|
||||
mySQLColumnRegisterDate: regdate
|
||||
# Column for storing the IP address at the time of registration
|
||||
mySQLColumnRegisterIp: regip
|
||||
# Column for storing player LastLocation - X
|
||||
mySQLlastlocX: x
|
||||
# Column for storing player LastLocation - Y
|
||||
mySQLlastlocY: y
|
||||
# Column for storing player LastLocation - Z
|
||||
mySQLlastlocZ: z
|
||||
# Column for storing player LastLocation - World Name
|
||||
mySQLlastlocWorld: world
|
||||
# Column for storing player LastLocation - Yaw
|
||||
mySQLlastlocYaw: yaw
|
||||
# Column for storing player LastLocation - Pitch
|
||||
mySQLlastlocPitch: pitch
|
||||
# Column for storing players uuids (optional)
|
||||
mySQLPlayerUUID: ''
|
||||
# Overrides the size of the DB Connection Pool, default = 10
|
||||
poolSize: 10
|
||||
# The maximum lifetime of a connection in the pool, default = 1800 seconds
|
||||
# You should set this at least 30 seconds less than mysql server wait_timeout
|
||||
maxLifetime: 1800
|
||||
ExternalBoardOptions:
|
||||
# Column for storing players groups
|
||||
mySQLColumnGroup: ''
|
||||
# -1 means disabled. If you want that only activated players
|
||||
# can log into your server, you can set here the group number
|
||||
# of unactivated users, needed for some forum/CMS support
|
||||
nonActivedUserGroup: -1
|
||||
# Other MySQL columns where we need to put the username (case-sensitive)
|
||||
mySQLOtherUsernameColumns: []
|
||||
# How much log2 rounds needed in BCrypt (do not change if you do not know what it does)
|
||||
bCryptLog2Round: 12
|
||||
# phpBB table prefix defined during the phpBB installation process
|
||||
phpbbTablePrefix: phpbb_
|
||||
# phpBB activated group ID; 2 is the default registered group defined by phpBB
|
||||
phpbbActivatedGroupId: 2
|
||||
# IP Board table prefix defined during the IP Board installation process
|
||||
IPBTablePrefix: ipb_
|
||||
# IP Board default group ID; 3 is the default registered group defined by IP Board
|
||||
IPBActivatedGroupId: 3
|
||||
# Xenforo table prefix defined during the Xenforo installation process
|
||||
XFTablePrefix: xf_
|
||||
# XenForo default group ID; 2 is the default registered group defined by Xenforo
|
||||
XFActivatedGroupId: 2
|
||||
# Wordpress prefix defined during WordPress installation
|
||||
wordpressTablePrefix: wp_
|
||||
settings:
|
||||
sessions:
|
||||
# Do you want to enable the session feature?
|
||||
# If enabled, when a player authenticates successfully,
|
||||
# his IP and his nickname is saved.
|
||||
# The next time the player joins the server, if his IP
|
||||
# is the same as last time and the timeout hasn't
|
||||
# expired, he will not need to authenticate.
|
||||
enabled: false
|
||||
# After how many minutes should a session expire?
|
||||
# A player's session ends after the timeout or if his IP has changed
|
||||
timeout: 10
|
||||
# Message language, available languages:
|
||||
# https://github.com/AuthMe/AuthMeReloaded/blob/master/docs/translations.md
|
||||
messagesLanguage: en
|
||||
# Forces authme to hook into Vault instead of a specific permission handler system.
|
||||
forceVaultHook: false
|
||||
# Log level: INFO, FINE, DEBUG. Use INFO for general messages,
|
||||
# FINE for some additional detailed ones (like password failed),
|
||||
# and DEBUG for debugging
|
||||
logLevel: FINE
|
||||
# By default we schedule async tasks when talking to the database. If you want
|
||||
# typical communication with the database to happen synchronously, set this to false
|
||||
useAsyncTasks: true
|
||||
# The name of the server, used in some placeholders.
|
||||
serverName: Your Minecraft Server
|
||||
restrictions:
|
||||
# Can not authenticated players chat?
|
||||
# Keep in mind that this feature also blocks all commands not
|
||||
# listed in the list below.
|
||||
allowChat: false
|
||||
# Hide the chat log from players who are not authenticated?
|
||||
hideChat: false
|
||||
# Allowed commands for unauthenticated players
|
||||
allowCommands:
|
||||
- /login
|
||||
- /log
|
||||
- /l
|
||||
- /register
|
||||
- /reg
|
||||
- /email
|
||||
- /captcha
|
||||
- /2fa
|
||||
- /totp
|
||||
# Max number of allowed registrations per IP
|
||||
# The value 0 means an unlimited number of registrations!
|
||||
maxRegPerIp: 1
|
||||
# Minimum allowed username length
|
||||
minNicknameLength: 3
|
||||
# Maximum allowed username length
|
||||
maxNicknameLength: 16
|
||||
# When this setting is enabled, online players can't be kicked out
|
||||
# due to "Logged in from another Location"
|
||||
# This setting will prevent potential security exploits.
|
||||
ForceSingleSession: true
|
||||
ForceSpawnLocOnJoin:
|
||||
# If enabled, every player that spawn in one of the world listed in
|
||||
# "ForceSpawnLocOnJoin.worlds" will be teleported to the spawnpoint after successful
|
||||
# authentication. The quit location of the player will be overwritten.
|
||||
# This is different from "teleportUnAuthedToSpawn" that teleport player
|
||||
# to the spawnpoint on join.
|
||||
enabled: false
|
||||
# WorldNames where we need to force the spawn location
|
||||
# Case-sensitive!
|
||||
worlds:
|
||||
- world
|
||||
- world_nether
|
||||
- world_the_end
|
||||
# This option will save the quit location of the players.
|
||||
SaveQuitLocation: false
|
||||
# To activate the restricted user feature you need
|
||||
# to enable this option and configure the AllowedRestrictedUser field.
|
||||
AllowRestrictedUser: false
|
||||
# The restricted user feature will kick players listed below
|
||||
# if they don't match the defined IP address. Names are case-insensitive.
|
||||
# You can use * as wildcard (127.0.0.*), or regex with a "regex:" prefix regex:127\.0\.0\..*
|
||||
# Example:
|
||||
# AllowedRestrictedUser:
|
||||
# - playername;127.0.0.1
|
||||
# - playername;regex:127\.0\.0\..*
|
||||
AllowedRestrictedUser: []
|
||||
# Ban unknown IPs trying to log in with a restricted username?
|
||||
banUnsafedIP: false
|
||||
# Should unregistered players be kicked immediately?
|
||||
kickNonRegistered: false
|
||||
# Should players be kicked on wrong password?
|
||||
kickOnWrongPassword: true
|
||||
# Should not logged in players be teleported to the spawn?
|
||||
# After the authentication they will be teleported back to
|
||||
# their normal position.
|
||||
teleportUnAuthedToSpawn: false
|
||||
# Can unregistered players walk around?
|
||||
allowMovement: false
|
||||
# After how many seconds should players who fail to login or register
|
||||
# be kicked? Set to 0 to disable.
|
||||
timeout: 30
|
||||
# Regex pattern of allowed characters in the player name.
|
||||
allowedNicknameCharacters: '[a-zA-Z0-9_]*'
|
||||
# How far can unregistered players walk?
|
||||
# Set to 0 for unlimited radius
|
||||
allowedMovementRadius: 100
|
||||
# Should we protect the player inventory before logging in? Requires ProtocolLib.
|
||||
ProtectInventoryBeforeLogIn: true
|
||||
# Should we deny the tabcomplete feature before logging in? Requires ProtocolLib.
|
||||
DenyTabCompleteBeforeLogin: false
|
||||
# Should we display all other accounts from a player when he joins?
|
||||
# permission: /authme.admin.accounts
|
||||
displayOtherAccounts: true
|
||||
# Spawn priority; values: authme, essentials, cmi, multiverse, default
|
||||
spawnPriority: authme,essentials,cmi,multiverse,default
|
||||
# Maximum Login authorized by IP
|
||||
maxLoginPerIp: 0
|
||||
# Maximum Join authorized by IP
|
||||
maxJoinPerIp: 0
|
||||
# AuthMe will NEVER teleport players if set to true!
|
||||
noTeleport: false
|
||||
# Regex syntax for allowed chars in passwords. The default [!-~] allows all visible ASCII
|
||||
# characters, which is what we recommend. See also http://asciitable.com
|
||||
# You can test your regex with https://regex101.com
|
||||
allowedPasswordCharacters: '[!-~]*'
|
||||
GameMode:
|
||||
# Force survival gamemode when player joins?
|
||||
ForceSurvivalMode: false
|
||||
unrestrictions:
|
||||
# Below you can list all account names that AuthMe will ignore
|
||||
# for registration or login. Configure it at your own risk!!
|
||||
# This option adds compatibility with BuildCraft and some other mods.
|
||||
# It is case-insensitive! Example:
|
||||
# UnrestrictedName:
|
||||
# - 'npcPlayer'
|
||||
# - 'npcPlayer2'
|
||||
UnrestrictedName: []
|
||||
# Below you can list all inventories names that AuthMe will ignore
|
||||
# for registration or login. Configure it at your own risk!!
|
||||
# This option adds compatibility with some mods.
|
||||
# It is case-insensitive! Example:
|
||||
# UnrestrictedInventories:
|
||||
# - 'myCustomInventory1'
|
||||
# - 'myCustomInventory2'
|
||||
UnrestrictedInventories: []
|
||||
security:
|
||||
# Minimum length of password
|
||||
minPasswordLength: 5
|
||||
# Maximum length of password
|
||||
passwordMaxLength: 30
|
||||
# Possible values: SHA256, BCRYPT, BCRYPT2Y, PBKDF2, SALTEDSHA512,
|
||||
# MYBB, IPB3, PHPBB, PHPFUSION, SMF, XENFORO, XAUTH, JOOMLA, WBB3, WBB4, MD5VB,
|
||||
# PBKDF2DJANGO, WORDPRESS, ROYALAUTH, ARGON2, CUSTOM (for developers only). See full list at
|
||||
# https://github.com/AuthMe/AuthMeReloaded/blob/master/docs/hash_algorithms.md
|
||||
# If you use ARGON2, check that you have the argon2 c library on your system
|
||||
passwordHash: SHA256
|
||||
# If a password check fails, AuthMe will also try to check with the following hash methods.
|
||||
# Use this setting when you change from one hash method to another.
|
||||
# AuthMe will update the password to the new hash. Example:
|
||||
# legacyHashes:
|
||||
# - 'SHA1'
|
||||
legacyHashes: []
|
||||
# Salt length for the SALTED2MD5 MD5(MD5(password)+salt)
|
||||
doubleMD5SaltLength: 8
|
||||
# Number of rounds to use if passwordHash is set to PBKDF2. Default is 10000
|
||||
pbkdf2Rounds: 10000
|
||||
# Prevent unsafe passwords from being used; put them in lowercase!
|
||||
# You should always set 'help' as unsafePassword due to possible conflicts.
|
||||
# unsafePasswords:
|
||||
# - '123456'
|
||||
# - 'password'
|
||||
# - 'help'
|
||||
unsafePasswords:
|
||||
- '123456'
|
||||
- password
|
||||
- qwerty
|
||||
- '12345'
|
||||
- '54321'
|
||||
- '123456789'
|
||||
- help
|
||||
registration:
|
||||
# Enable registration on the server?
|
||||
enabled: true
|
||||
# Send every X seconds a message to a player to
|
||||
# remind him that he has to login/register
|
||||
messageInterval: 5
|
||||
# Only registered and logged in players can play.
|
||||
# See restrictions for exceptions
|
||||
force: true
|
||||
# Type of registration: PASSWORD or EMAIL
|
||||
# PASSWORD = account is registered with a password supplied by the user;
|
||||
# EMAIL = password is generated and sent to the email provided by the user.
|
||||
# More info at https://github.com/AuthMe/AuthMeReloaded/wiki/Registration
|
||||
type: PASSWORD
|
||||
# Second argument the /register command should take: NONE = no 2nd argument
|
||||
# CONFIRMATION = must repeat first argument (pass or email)
|
||||
# EMAIL_OPTIONAL = for password register: 2nd argument can be empty or have email address
|
||||
# EMAIL_MANDATORY = for password register: 2nd argument MUST be an email address
|
||||
secondArg: CONFIRMATION
|
||||
# Do we force kick a player after a successful registration?
|
||||
# Do not use with login feature below
|
||||
forceKickAfterRegister: false
|
||||
# Does AuthMe need to enforce a /login after a successful registration?
|
||||
forceLoginAfterRegister: false
|
||||
# Broadcast the welcome message to the server or only to the player?
|
||||
# set true for server or false for player
|
||||
broadcastWelcomeMessage: false
|
||||
# Should we delay the join message and display it once the player has logged in?
|
||||
delayJoinMessage: false
|
||||
# The custom join message that will be sent after a successful login,
|
||||
# keep empty to use the original one.
|
||||
# Available variables:
|
||||
# {PLAYERNAME}: the player name (no colors)
|
||||
# {DISPLAYNAME}: the player display name (with colors)
|
||||
# {DISPLAYNAMENOCOLOR}: the player display name (without colors)
|
||||
customJoinMessage: ''
|
||||
# Should we remove the leave messages of unlogged users?
|
||||
removeUnloggedLeaveMessage: false
|
||||
# Should we remove join messages altogether?
|
||||
removeJoinMessage: false
|
||||
# Should we remove leave messages altogether?
|
||||
removeLeaveMessage: false
|
||||
# Do we need to add potion effect Blinding before login/register?
|
||||
applyBlindEffect: false
|
||||
# Do we need to prevent people to login with another case?
|
||||
# If Xephi is registered, then Xephi can login, but not XEPHI/xephi/XePhI
|
||||
preventOtherCase: true
|
||||
GroupOptions:
|
||||
# Enables switching a player to defined permission groups before they log in.
|
||||
# See below for a detailed explanation.
|
||||
enablePermissionCheck: false
|
||||
# This is a very important option: if a registered player joins the server
|
||||
# AuthMe will switch him to unLoggedInGroup. This should prevent all major exploits.
|
||||
# You can set up your permission plugin with this special group to have no permissions,
|
||||
# or only permission to chat (or permission to send private messages etc.).
|
||||
# The better way is to set up this group with few permissions, so if a player
|
||||
# tries to exploit an account they can do only what you've defined for the group.
|
||||
# After login, the player will be moved to his correct permissions group!
|
||||
# Please note that the group name is case-sensitive, so 'admin' is different from 'Admin'
|
||||
# Otherwise your group will be wiped and the player will join in the default group []!
|
||||
# Example: registeredPlayerGroup: 'NotLogged'
|
||||
registeredPlayerGroup: ''
|
||||
# Similar to above, unregistered players can be set to the following
|
||||
# permissions group
|
||||
unregisteredPlayerGroup: ''
|
||||
Email:
|
||||
# Email SMTP server host
|
||||
mailSMTP: smtp.gmail.com
|
||||
# Email SMTP server port
|
||||
mailPort: 465
|
||||
# Only affects port 25: enable TLS/STARTTLS?
|
||||
useTls: true
|
||||
# Email account which sends the mails
|
||||
mailAccount: ''
|
||||
# Email account password
|
||||
mailPassword: ''
|
||||
# Email address, fill when mailAccount is not the email address of the account
|
||||
mailAddress: ''
|
||||
# Custom sender name, replacing the mailAccount name in the email
|
||||
mailSenderName: ''
|
||||
# Recovery password length
|
||||
RecoveryPasswordLength: 8
|
||||
# Mail Subject
|
||||
mailSubject: Your new AuthMe password
|
||||
# Like maxRegPerIP but with email
|
||||
maxRegPerEmail: 1
|
||||
# Recall players to add an email?
|
||||
recallPlayers: false
|
||||
# Delay in minute for the recall scheduler
|
||||
delayRecall: 5
|
||||
# Blacklist these domains for emails
|
||||
emailBlacklisted:
|
||||
- 10minutemail.com
|
||||
# Whitelist ONLY these domains for emails
|
||||
emailWhitelisted: []
|
||||
# Send the new password drawn in an image?
|
||||
generateImage: false
|
||||
# The OAuth2 token
|
||||
emailOauth2Token: ''
|
||||
Hooks:
|
||||
# Do we need to hook with multiverse for spawn checking?
|
||||
multiverse: true
|
||||
# Do we need to hook with BungeeCord?
|
||||
bungeecord: false
|
||||
# Send player to this BungeeCord server after register/login
|
||||
sendPlayerTo: ''
|
||||
# Do we need to disable Essentials SocialSpy on join?
|
||||
disableSocialSpy: false
|
||||
# Do we need to force /motd Essentials command on join?
|
||||
useEssentialsMotd: false
|
||||
Protection:
|
||||
# Enable some servers protection (country based login, antibot)
|
||||
enableProtection: false
|
||||
# Apply the protection also to registered usernames
|
||||
enableProtectionRegistered: true
|
||||
geoIpDatabase:
|
||||
# The MaxMind clientId used to download the GeoIp database,
|
||||
# get one at https://www.maxmind.com/en/accounts/current/license-key
|
||||
# The EssentialsX project has a very useful tutorial on how to generate
|
||||
# the license key: https://github.com/EssentialsX/Wiki/blob/master/GeoIP.md
|
||||
clientId: ''
|
||||
# The MaxMind licenseKey used to download the GeoIp database.
|
||||
licenseKey: ''
|
||||
# Countries allowed to join the server and register. For country codes, see
|
||||
# https://dev.maxmind.com/geoip/legacy/codes/iso3166/
|
||||
# Use "LOCALHOST" for local addresses.
|
||||
# PLEASE USE QUOTES!
|
||||
countries:
|
||||
- US
|
||||
- GB
|
||||
- LOCALHOST
|
||||
# Countries not allowed to join the server and register
|
||||
# PLEASE USE QUOTES!
|
||||
countriesBlacklist:
|
||||
- A1
|
||||
# Do we need to enable automatic antibot system?
|
||||
enableAntiBot: true
|
||||
# The interval in seconds
|
||||
antiBotInterval: 5
|
||||
# Max number of players allowed to login in the interval
|
||||
# before the AntiBot system is enabled automatically
|
||||
antiBotSensibility: 10
|
||||
# Duration in minutes of the antibot automatic system
|
||||
antiBotDuration: 10
|
||||
# Delay in seconds before the antibot activation
|
||||
antiBotDelay: 60
|
||||
quickCommands:
|
||||
# Kicks the player that issued a command before the defined time after the join process
|
||||
denyCommandsBeforeMilliseconds: 1000
|
||||
Purge:
|
||||
# If enabled, AuthMe automatically purges old, unused accounts
|
||||
useAutoPurge: false
|
||||
# Number of days after which an account should be purged
|
||||
daysBeforeRemovePlayer: 60
|
||||
# Do we need to remove the player.dat file during purge process?
|
||||
removePlayerDat: false
|
||||
# Do we need to remove the Essentials/userdata/player.yml file during purge process?
|
||||
removeEssentialsFile: false
|
||||
# World in which the players.dat are stored
|
||||
defaultWorld: world
|
||||
# Remove LimitedCreative/inventories/player.yml, player_creative.yml files during purge?
|
||||
removeLimitedCreativesInventories: false
|
||||
# Do we need to remove the AntiXRayData/PlayerData/player file during purge process?
|
||||
removeAntiXRayFile: false
|
||||
# Do we need to remove permissions?
|
||||
removePermissions: false
|
||||
Security:
|
||||
SQLProblem:
|
||||
# Stop the server if we can't contact the sql database
|
||||
# Take care with this, if you set this to false,
|
||||
# AuthMe will automatically disable and the server won't be protected!
|
||||
stopServer: true
|
||||
console:
|
||||
# Copy AuthMe log output in a separate file as well?
|
||||
logConsole: true
|
||||
captcha:
|
||||
# Enable captcha when a player uses wrong password too many times
|
||||
useCaptcha: false
|
||||
# Max allowed tries before a captcha is required
|
||||
maxLoginTry: 5
|
||||
# Captcha length
|
||||
captchaLength: 5
|
||||
# Minutes after which login attempts count is reset for a player
|
||||
captchaCountReset: 60
|
||||
# Require captcha before a player may register?
|
||||
requireForRegistration: false
|
||||
tempban:
|
||||
# Tempban a user's IP address if they enter the wrong password too many times
|
||||
enableTempban: false
|
||||
# How many times a user can attempt to login before their IP being tempbanned
|
||||
maxLoginTries: 10
|
||||
# The length of time a IP address will be tempbanned in minutes
|
||||
# Default: 480 minutes, or 8 hours
|
||||
tempbanLength: 480
|
||||
# How many minutes before resetting the count for failed logins by IP and username
|
||||
# Default: 480 minutes (8 hours)
|
||||
minutesBeforeCounterReset: 480
|
||||
# The command to execute instead of using the internal ban system, empty if disabled.
|
||||
# Available placeholders: %player%, %ip%
|
||||
customCommand: ''
|
||||
recoveryCode:
|
||||
# Number of characters a recovery code should have (0 to disable)
|
||||
length: 8
|
||||
# How many hours is a recovery code valid for?
|
||||
validForHours: 4
|
||||
# Max number of tries to enter recovery code
|
||||
maxTries: 3
|
||||
# How long a player has after password recovery to change their password
|
||||
# without logging in. This is in minutes.
|
||||
# Default: 2 minutes
|
||||
passwordChangeTimeout: 2
|
||||
emailRecovery:
|
||||
# Seconds a user has to wait for before a password recovery mail may be sent again
|
||||
# This prevents an attacker from abusing AuthMe's email feature.
|
||||
cooldown: 60
|
||||
privacy:
|
||||
# The mail shown using /email show will be partially hidden
|
||||
# E.g. (if enabled)
|
||||
# original email: my.email@example.com
|
||||
# hidden email: my.***@***mple.com
|
||||
enableEmailMasking: false
|
||||
# Minutes after which a verification code will expire
|
||||
verificationCodeExpiration: 10
|
||||
# Before a user logs in, various properties are temporarily removed from the player,
|
||||
# such as OP status, ability to fly, and walk/fly speed.
|
||||
# Once the user is logged in, we add back the properties we previously saved.
|
||||
# In this section, you may define how these properties should be handled.
|
||||
# Read more at https://github.com/AuthMe/AuthMeReloaded/wiki/Limbo-players
|
||||
limbo:
|
||||
persistence:
|
||||
# Besides storing the data in memory, you can define if/how the data should be persisted
|
||||
# on disk. This is useful in case of a server crash, so next time the server starts we can
|
||||
# properly restore things like OP status, ability to fly, and walk/fly speed.
|
||||
# DISABLED: no disk storage,
|
||||
# INDIVIDUAL_FILES: each player data in its own file,
|
||||
# DISTRIBUTED_FILES: distributes players into different files based on their UUID, see below
|
||||
type: INDIVIDUAL_FILES
|
||||
# This setting only affects DISTRIBUTED_FILES persistence. The distributed file
|
||||
# persistence attempts to reduce the number of files by distributing players into various
|
||||
# buckets based on their UUID. This setting defines into how many files the players should
|
||||
# be distributed. Possible values: ONE, FOUR, EIGHT, SIXTEEN, THIRTY_TWO, SIXTY_FOUR,
|
||||
# ONE_TWENTY for 128, TWO_FIFTY for 256.
|
||||
# For example, if you expect 100 non-logged in players, setting to SIXTEEN will average
|
||||
# 6.25 players per file (100 / 16).
|
||||
# Note: if you change this setting all data will be migrated. If you have a lot of data,
|
||||
# change this setting only on server restart, not with /authme reload.
|
||||
distributionSize: SIXTEEN
|
||||
# Whether the player is allowed to fly: RESTORE, ENABLE, DISABLE, NOTHING.
|
||||
# RESTORE sets back the old property from the player. NOTHING will prevent AuthMe
|
||||
# from modifying the 'allow flight' property on the player.
|
||||
restoreAllowFlight: RESTORE
|
||||
# Restore fly speed: RESTORE, DEFAULT, MAX_RESTORE, RESTORE_NO_ZERO.
|
||||
# RESTORE: restore the speed the player had;
|
||||
# DEFAULT: always set to default speed;
|
||||
# MAX_RESTORE: take the maximum of the player's current speed and the previous one
|
||||
# RESTORE_NO_ZERO: Like 'restore' but sets speed to default if the player's speed was 0
|
||||
restoreFlySpeed: RESTORE_NO_ZERO
|
||||
# Restore walk speed: RESTORE, DEFAULT, MAX_RESTORE, RESTORE_NO_ZERO.
|
||||
# See above for a description of the values.
|
||||
restoreWalkSpeed: RESTORE_NO_ZERO
|
||||
BackupSystem:
|
||||
# General configuration for backups: if false, no backups are possible
|
||||
ActivateBackup: false
|
||||
# Create backup at every start of server
|
||||
OnServerStart: false
|
||||
# Create backup at every stop of server
|
||||
OnServerStop: true
|
||||
# Windows only: MySQL installation path
|
||||
MysqlWindowsPath: C:\Program Files\MySQL\MySQL Server 5.1\
|
||||
# Converter settings: see https://github.com/AuthMe/AuthMeReloaded/wiki/Converters
|
||||
Converter:
|
||||
Rakamak:
|
||||
# Rakamak file name
|
||||
fileName: users.rak
|
||||
# Rakamak use IP?
|
||||
useIP: false
|
||||
# Rakamak IP file name
|
||||
ipFileName: UsersIp.rak
|
||||
CrazyLogin:
|
||||
# CrazyLogin database file name
|
||||
fileName: accounts.db
|
||||
loginSecurity:
|
||||
# LoginSecurity: convert from SQLite; if false we use MySQL
|
||||
useSqlite: true
|
||||
mySql:
|
||||
# LoginSecurity MySQL: database host
|
||||
host: ''
|
||||
# LoginSecurity MySQL: database name
|
||||
database: ''
|
||||
# LoginSecurity MySQL: database user
|
||||
user: ''
|
||||
# LoginSecurity MySQL: password for database user
|
||||
password: ''
|
||||
|
||||
```
|
||||
|
||||
To change settings on a running server, save your changes to config.yml and use
|
||||
`/authme reload`.
|
||||
|
||||
---
|
||||
|
||||
This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Thu Jul 28 18:11:22 CEST 2022
|
||||
@ -1,83 +0,0 @@
|
||||
<!-- AUTO-GENERATED FILE! Do not edit this directly -->
|
||||
<!-- File auto-generated on Sun Apr 04 21:31:44 CEST 2021. See docs/hashmethods/hash_algorithms.tpl.md -->
|
||||
|
||||
## Hash Algorithms
|
||||
AuthMe supports the following hash algorithms for storing your passwords safely.
|
||||
|
||||
|
||||
Algorithm | Recommendation | Hash length | ASCII | | Salt type | Length | Separate?
|
||||
--------- | -------------- | ----------- | ----- | --- | --------- | ------ | ---------
|
||||
ARGON2 | Recommended | 96 | | | Text | 16 |
|
||||
BCRYPT | Recommended | 60 | | | Text | 22 |
|
||||
BCRYPT2Y | Recommended | 60 | | | Text | 22 |
|
||||
CMW | Do not use | 32 | | | None | |
|
||||
CRAZYCRYPT1 | Do not use | 128 | | | Username | |
|
||||
IPB3 | Acceptable | 32 | | | Text | 5 | Y
|
||||
IPB4 | Does not work | 60 | | | Text | 22 | Y
|
||||
JOOMLA | Acceptable | 65 | | | Text | 32 |
|
||||
MD5VB | Acceptable | 56 | | | Text | 16 |
|
||||
MYBB | Acceptable | 32 | | | Text | 8 | Y
|
||||
PBKDF2 | Recommended | 165 | | | Text | 16 |
|
||||
PBKDF2DJANGO | Acceptable | 77 | Y | | Text | 12 |
|
||||
PHPBB | Acceptable | 60 | | | Text | 22 |
|
||||
PHPFUSION | Do not use | 64 | Y | | | | Y
|
||||
ROYALAUTH | Do not use | 128 | | | None | |
|
||||
SALTED2MD5 | Acceptable | 32 | | | Text | | Y
|
||||
SALTEDSHA512 | Recommended | 128 | | | | | Y
|
||||
SHA256 | Recommended | 86 | | | Text | 16 |
|
||||
SMF | Do not use | 40 | | | Username | | Y
|
||||
TWO_FACTOR | Does not work | 16 | | | None | |
|
||||
WBB3 | Acceptable | 40 | | | Text | 40 | Y
|
||||
WBB4 | Recommended | 60 | | | Text | 22 |
|
||||
WORDPRESS | Acceptable | 34 | | | Text | 9 |
|
||||
XAUTH | Recommended | 140 | | | Text | 12 |
|
||||
XFBCRYPT | Recommended | 60 | | | Text | 22 |
|
||||
CUSTOM | | | | | | | |
|
||||
|
||||
<!-- AUTO-GENERATED FILE! Do not edit this directly -->
|
||||
|
||||
### Columns
|
||||
#### Algorithm
|
||||
The algorithm is the hashing algorithm used to store passwords with. Default is SHA256 and is recommended.
|
||||
You can change the hashing algorithm in the config.yml: under `security`, locate `passwordHash`.
|
||||
|
||||
#### Recommendation
|
||||
The recommendation lists our usage recommendation in terms of how secure it is (not how _well_ the algorithm works!).
|
||||
- Recommended: The hash algorithm appears to be cryptographically secure and is one we recommend.
|
||||
- Acceptable: There are safer algorithms that can be chosen but using the algorithm is generally OK.
|
||||
- Deprecated: Cannot be used anymore actively or in the near future.
|
||||
- Do not use: Hash algorithm isn't sufficiently secure. Use only if required to hook into another system.
|
||||
- Does not work: The algorithm does not work properly; do not use.
|
||||
|
||||
#### Hash Length
|
||||
The length of the hashes the algorithm produces. Note that the hash length is not (primarily) indicative of
|
||||
whether an algorithm is secure or not.
|
||||
|
||||
#### ASCII
|
||||
If denoted with a **y**, means that the algorithm is restricted to ASCII characters only, i.e. it will simply ignore
|
||||
"special characters" such as `ÿ` or `Â`. Note that we do not recommend the use of "special characters" in passwords.
|
||||
|
||||
#### Salt Columns
|
||||
Before hashing, a _salt_ may be appended to the password to make the hash more secure. The following columns describe
|
||||
the salt the algorithm uses.
|
||||
<!-- AUTO-GENERATED FILE! Do not edit this directly -->
|
||||
|
||||
##### Salt Type
|
||||
We do not recommend the usage
|
||||
of any algorithm that doesn't use a randomly generated text as salt. This "salt type" column indicates what type of
|
||||
salt the algorithm uses:
|
||||
- Text: randomly generated text (see also the following column, "Length")
|
||||
- Username: the salt is constructed from the username (bad)
|
||||
- None: the algorithm uses no salt (bad)
|
||||
|
||||
##### Length
|
||||
If applicable (salt type is "Text"), indicates the length of the generated salt. The longer the better.
|
||||
If this column is empty when the salt type is "Text", it typically means the salt length can be defined in config.yml.
|
||||
|
||||
##### Separate
|
||||
If denoted with a **y**, it means that the salt is stored in a separate column in the database. This is neither good
|
||||
or bad.
|
||||
|
||||
---
|
||||
|
||||
This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sun Apr 04 21:31:44 CEST 2021
|
||||
@ -1,76 +0,0 @@
|
||||
<!-- AUTO-GENERATED FILE! Do not edit this directly -->
|
||||
<!-- File auto-generated on Sun Apr 04 21:31:44 CEST 2021. See docs/permissions/permission_nodes.tpl.md -->
|
||||
|
||||
## AuthMe Permission Nodes
|
||||
The following are the permission nodes that are currently supported by the latest dev builds.
|
||||
|
||||
- **authme.admin.*** – Give access to all admin commands.
|
||||
- **authme.admin.accounts** – Administrator command to see all accounts associated with a user.
|
||||
- **authme.admin.antibotmessages** – Permission to see Antibot messages.
|
||||
- **authme.admin.backup** – Allows to use the backup command.
|
||||
- **authme.admin.changemail** – Administrator command to set or change the email address of a user.
|
||||
- **authme.admin.changepassword** – Administrator command to change the password of a user.
|
||||
- **authme.admin.converter** – Administrator command to convert old or other data to AuthMe data.
|
||||
- **authme.admin.firstspawn** – Administrator command to teleport to the first AuthMe spawn.
|
||||
- **authme.admin.forcelogin** – Administrator command to force-login an existing user.
|
||||
- **authme.admin.getemail** – Administrator command to get the email address of a user, if set.
|
||||
- **authme.admin.getip** – Administrator command to get the last known IP of a user.
|
||||
- **authme.admin.lastlogin** – Administrator command to see the last login date and time of a user.
|
||||
- **authme.admin.purge** – Administrator command to purge old user data.
|
||||
- **authme.admin.purgebannedplayers** – Administrator command to purge all data associated with banned players.
|
||||
- **authme.admin.purgelastpos** – Administrator command to purge the last position of a user.
|
||||
- **authme.admin.purgeplayer** – Administrator command to purge a given player.
|
||||
- **authme.admin.register** – Administrator command to register a new user.
|
||||
- **authme.admin.reload** – Administrator command to reload the plugin configuration.
|
||||
- **authme.admin.seeotheraccounts** – Permission to see the other accounts of the players that log in.
|
||||
- **authme.admin.seerecent** – Administrator command to see the last recently logged in players.
|
||||
- **authme.admin.setfirstspawn** – Administrator command to set the first AuthMe spawn.
|
||||
- **authme.admin.setspawn** – Administrator command to set the AuthMe spawn.
|
||||
- **authme.admin.spawn** – Administrator command to teleport to the AuthMe spawn.
|
||||
- **authme.admin.switchantibot** – Administrator command to toggle the AntiBot protection status.
|
||||
- **authme.admin.totpdisable** – Administrator command to disable the two-factor auth of a user.
|
||||
- **authme.admin.totpviewstatus** – Administrator command to see whether a player has enabled two-factor authentication.
|
||||
- **authme.admin.unregister** – Administrator command to unregister an existing user.
|
||||
- **authme.admin.updatemessages** – Permission to use the update messages command.
|
||||
- **authme.allowchatbeforelogin** – Permission to send chat messages before being logged in.
|
||||
- **authme.allowmultipleaccounts** – Permission to be able to register multiple accounts.
|
||||
- **authme.bypassantibot** – Permission node to bypass AntiBot protection.
|
||||
- **authme.bypassbungeesend** – Permission node to bypass BungeeCord server teleportation.
|
||||
- **authme.bypasscountrycheck** – Permission to bypass the GeoIp country code check.
|
||||
- **authme.bypassforcesurvival** – Permission for users to bypass force-survival mode.
|
||||
- **authme.bypasspurge** – Permission to bypass the purging process.
|
||||
- **authme.debug.command** – General permission to use the /authme debug command.
|
||||
- **authme.debug.country** – Permission to use the country lookup section.
|
||||
- **authme.debug.db** – Permission to view data from the database.
|
||||
- **authme.debug.group** – Permission to view permission groups.
|
||||
- **authme.debug.limbo** – Permission to use the limbo data viewer.
|
||||
- **authme.debug.mail** – Permission to use the test email sender.
|
||||
- **authme.debug.mysqldef** – Permission to change nullable status of MySQL columns.
|
||||
- **authme.debug.perm** – Permission to use the permission checker.
|
||||
- **authme.debug.spawn** – Permission to view spawn information.
|
||||
- **authme.debug.stats** – Permission to use the stats section.
|
||||
- **authme.debug.valid** – Permission to use sample validation.
|
||||
- **authme.player.*** – Permission to use all player (non-admin) commands.
|
||||
- **authme.player.canbeforced** – Permission for users a login can be forced to.
|
||||
- **authme.player.captcha** – Command permission to use captcha.
|
||||
- **authme.player.changepassword** – Command permission to change the password.
|
||||
- **authme.player.email** – Grants all email permissions.
|
||||
- **authme.player.email.add** – Command permission to add an email address.
|
||||
- **authme.player.email.change** – Command permission to change the email address.
|
||||
- **authme.player.email.recover** – Command permission to recover an account using its email address.
|
||||
- **authme.player.email.see** – Command permission to see the own email address.
|
||||
- **authme.player.login** – Command permission to login.
|
||||
- **authme.player.logout** – Command permission to logout.
|
||||
- **authme.player.protection.quickcommandsprotection** – Permission that enables on join quick commands checks for the player.
|
||||
- **authme.player.register** – Command permission to register.
|
||||
- **authme.player.security.verificationcode** – Permission to use the email verification codes feature.
|
||||
- **authme.player.seeownaccounts** – Permission to use to see own other accounts.
|
||||
- **authme.player.totpadd** – Permission to enable two-factor authentication.
|
||||
- **authme.player.totpremove** – Permission to disable two-factor authentication.
|
||||
- **authme.player.unregister** – Command permission to unregister.
|
||||
- **authme.vip** – When the server is full and someone with this permission joins the server, someone will be kicked.
|
||||
|
||||
|
||||
---
|
||||
|
||||
This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sun Apr 04 21:31:44 CEST 2021
|
||||
@ -1,46 +0,0 @@
|
||||
<!-- AUTO-GENERATED FILE! Do not edit this directly -->
|
||||
<!-- File auto-generated on Sun Apr 04 21:31:44 CEST 2021. See docs/translations/translations.tpl.md -->
|
||||
|
||||
# AuthMe Translations
|
||||
The following translations are available in AuthMe. Set `messagesLanguage` to the language code
|
||||
in your config.yml to use the language, or use another language code to start a new translation.
|
||||
|
||||
Code | Language | Translated |
|
||||
---- | -------- | ---------: | ------
|
||||
[en](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_en.yml) | English | 100% | <img src="https://via.placeholder.com/100x7/66ff66?text=%20" alt="100" />
|
||||
[bg](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_bg.yml) | Bulgarian | 99% | <img src="https://via.placeholder.com/99x7/66ee55?text=%20" alt="99" />
|
||||
[br](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_br.yml) | Brazilian | 100% | <img src="https://via.placeholder.com/100x7/66ff66?text=%20" alt="100" />
|
||||
[cz](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_cz.yml) | Czech | 100% | <img src="https://via.placeholder.com/100x7/66ff66?text=%20" alt="100" />
|
||||
[de](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_de.yml) | German | 99% | <img src="https://via.placeholder.com/99x7/66ee55?text=%20" alt="99" />
|
||||
[eo](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_eo.yml) | Esperanto | 79% | <img src="https://via.placeholder.com/79x7/bb9900?text=%20" alt="79" />
|
||||
[es](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_es.yml) | Spanish | 99% | <img src="https://via.placeholder.com/99x7/66ee55?text=%20" alt="99" />
|
||||
[et](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_et.yml) | Estonian | 100% | <img src="https://via.placeholder.com/100x7/66ff66?text=%20" alt="100" />
|
||||
[eu](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_eu.yml) | Basque | 100% | <img src="https://via.placeholder.com/100x7/66ff66?text=%20" alt="100" />
|
||||
[fi](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_fi.yml) | Finnish | 45% | <img src="https://via.placeholder.com/45x7/aa5500?text=%20" alt="45" />
|
||||
[fr](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_fr.yml) | French | 100% | <img src="https://via.placeholder.com/100x7/66ff66?text=%20" alt="100" />
|
||||
[gl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_gl.yml) | Galician | 48% | <img src="https://via.placeholder.com/48x7/aa5500?text=%20" alt="48" />
|
||||
[hu](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_hu.yml) | Hungarian | 99% | <img src="https://via.placeholder.com/99x7/66ee55?text=%20" alt="99" />
|
||||
[id](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_id.yml) | Indonesian | 93% | <img src="https://via.placeholder.com/93x7/77dd44?text=%20" alt="93" />
|
||||
[it](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_it.yml) | Italian | 100% | <img src="https://via.placeholder.com/100x7/66ff66?text=%20" alt="100" />
|
||||
[ko](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_ko.yml) | Korean | 99% | <img src="https://via.placeholder.com/99x7/66ee55?text=%20" alt="99" />
|
||||
[lt](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_lt.yml) | Lithuanian | 36% | <img src="https://via.placeholder.com/36x7/aa4400?text=%20" alt="36" />
|
||||
[nl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_nl.yml) | Dutch | 100% | <img src="https://via.placeholder.com/100x7/66ff66?text=%20" alt="100" />
|
||||
[pl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_pl.yml) | Polish | 99% | <img src="https://via.placeholder.com/99x7/66ee55?text=%20" alt="99" />
|
||||
[pt](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_pt.yml) | Portuguese | 100% | <img src="https://via.placeholder.com/100x7/66ff66?text=%20" alt="100" />
|
||||
[ro](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_ro.yml) | Romanian | 100% | <img src="https://via.placeholder.com/100x7/66ff66?text=%20" alt="100" />
|
||||
[ru](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_ru.yml) | Russian | 100% | <img src="https://via.placeholder.com/100x7/66ff66?text=%20" alt="100" />
|
||||
[si](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_si.yml) | Slovenian | 99% | <img src="https://via.placeholder.com/99x7/66ee55?text=%20" alt="99" />
|
||||
[sk](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_sk.yml) | Slovakian | 79% | <img src="https://via.placeholder.com/79x7/bb9900?text=%20" alt="79" />
|
||||
[sr](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_sr.yml) | Serbian | 99% | <img src="https://via.placeholder.com/99x7/66ee55?text=%20" alt="99" />
|
||||
[tr](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_tr.yml) | Turkish | 100% | <img src="https://via.placeholder.com/100x7/66ff66?text=%20" alt="100" />
|
||||
[uk](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_uk.yml) | Ukrainian | 100% | <img src="https://via.placeholder.com/100x7/66ff66?text=%20" alt="100" />
|
||||
[vn](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_vn.yml) | Vietnamese | 77% | <img src="https://via.placeholder.com/77x7/bb9900?text=%20" alt="77" />
|
||||
[zhcn](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhcn.yml) | Chinese (China) | 100% | <img src="https://via.placeholder.com/100x7/66ff66?text=%20" alt="100" />
|
||||
[zhhk](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhhk.yml) | Chinese (Hong Kong) | 99% | <img src="https://via.placeholder.com/99x7/66ee55?text=%20" alt="99" />
|
||||
[zhmc](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhmc.yml) | Chinese (Macau) | 64% | <img src="https://via.placeholder.com/64x7/bb7700?text=%20" alt="64" />
|
||||
[zhtw](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhtw.yml) | Chinese (Taiwan) | 86% | <img src="https://via.placeholder.com/86x7/99bb22?text=%20" alt="86" />
|
||||
|
||||
|
||||
---
|
||||
|
||||
This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sun Apr 04 21:31:44 CEST 2021
|
||||
@ -1,564 +0,0 @@
|
||||
# =======================================================================================================
|
||||
# _____ __ .__ _____ __________ .__ .___ .___
|
||||
# / _ \ __ ___/ |_| |__ / \ ____\______ \ ____ | | _________ __| _/____ __| _/
|
||||
# / /_\ \| | \ __| | \ / \ / \_/ __ \| __/ __ \| | / _ \__ \ / __ _/ __ \ / __ |
|
||||
# / | | | /| | | Y / Y \ ___/| | \ ___/| |_( <_> / __ \/ /_/ \ ___// /_/ |
|
||||
# \____|__ |____/ |__| |___| \____|__ /\___ |____|_ /\___ |____/\____(____ \____ |\___ \____ |
|
||||
# \/ \/ \/ \/ \/ \/ \/ \/ \/ \/
|
||||
#
|
||||
# =======================================================================================================
|
||||
#
|
||||
# Authme Main Configuration File.
|
||||
#
|
||||
# =======================================================================================================
|
||||
|
||||
# Plugin infos (overwritten on start, just a simple way to find out your plugin version).
|
||||
authors: ${pluginAuthors}
|
||||
version: ${project.version}
|
||||
buildNumber: ${buildNumber}
|
||||
|
||||
# Set this setting to true when you have configured the plugin,
|
||||
# when false the server will be stopped with a warning message.
|
||||
enabled: false
|
||||
|
||||
# Database settings.
|
||||
data_source:
|
||||
# ===========================
|
||||
# Database general settings.
|
||||
# ===========================
|
||||
|
||||
# Database backend (sqlite, mysql).
|
||||
backend: sqlite
|
||||
# Enable database queries caching, should improve performance.
|
||||
caching: true
|
||||
|
||||
# ===========================
|
||||
# SqLite db parameters.
|
||||
# ===========================
|
||||
|
||||
sqlite:
|
||||
# The name of the database storage file.
|
||||
filename: 'authme.db'
|
||||
|
||||
# ===========================
|
||||
# MySql db parameters.
|
||||
# ===========================
|
||||
|
||||
mysql:
|
||||
# Connection parameters.
|
||||
host: '127.0.0.1'
|
||||
port: 3306
|
||||
username: 'change_me'
|
||||
password: 'change_me'
|
||||
database: 'my_minecraft_server'
|
||||
tablename: 'authme'
|
||||
|
||||
# Column names.
|
||||
column_names:
|
||||
id: id
|
||||
# Column for storing nicknames (ignore case nickname).
|
||||
name: username
|
||||
# Column for storing the realname (case sensitive nickname).
|
||||
real_name: realname
|
||||
# Column for storing passwords.
|
||||
password: password
|
||||
# Column for storing email addresses.
|
||||
email: email
|
||||
# Column for storing the authentication status (logged or not).
|
||||
login_status: isLogged
|
||||
# Column for storing player IPs.
|
||||
ip: ip
|
||||
# Column for storing lastlogins date and time.
|
||||
last_login_timestamp: lastlogin
|
||||
# Latest logout location of the players.
|
||||
last_location:
|
||||
world: world
|
||||
x: x
|
||||
y: y
|
||||
z: z
|
||||
# Enabled only if the bungeecord integration is activated.
|
||||
server: world
|
||||
|
||||
# Support for registrations via WebInterfaces/CSM.
|
||||
# Disable some backend caching parameters.
|
||||
disableAggressiveCaching: false
|
||||
|
||||
# Main settings
|
||||
settings:
|
||||
|
||||
# ===========================
|
||||
# Bungeecord integration
|
||||
# ===========================
|
||||
|
||||
bungeecord:
|
||||
# Enable bungeecord integration features
|
||||
enabled: true
|
||||
|
||||
# Server name (must be unique, please use the name in the bungeecord configuration).
|
||||
# Use 'auto' for auto configuration (requires the bungeecord module).
|
||||
serverName: LoginLobby1
|
||||
# Keep the auth status when the player moves between servers.
|
||||
# Required if you're using the bungeecord module.
|
||||
keepAuthBetweenServers: true
|
||||
|
||||
# Target server after login
|
||||
send_after_login:
|
||||
enabled: false
|
||||
message: ''
|
||||
delay: 5
|
||||
# Server name ("ServerName") or group ("G:GroupName")
|
||||
# Groups are avariable only when the bungeecord module is avariable.
|
||||
# If the server change fails the player will be kicked.
|
||||
target: Lobby1
|
||||
failKickMessage: 'Failed to connect to the lobby! Please try to join the server again!'
|
||||
# Target server after logout
|
||||
send_after_logout:
|
||||
enabled: false
|
||||
message: ''
|
||||
delay: 5
|
||||
# Server name ("ServerName") or group ("G:GroupName")
|
||||
# Groups are avariable only when the bungeecord module is avariable.
|
||||
# If the server change fails the player will be kicked.
|
||||
target: LoginLobby1
|
||||
failKickMessage: 'Failed to connect to the lobby! Please try to join the server again!'
|
||||
|
||||
# Variables:
|
||||
# %p playername
|
||||
bungee_commands:
|
||||
player_command_after_register:
|
||||
enabled: false
|
||||
cmd: ''
|
||||
console_command_after_register:
|
||||
enabled: false
|
||||
cmd: 'alert %p joined for the first time the network!'
|
||||
player_command_after_login:
|
||||
enabled: false
|
||||
cmd: 'glist'
|
||||
console_command_after_login:
|
||||
enabled: false
|
||||
cmd: 'alert %p logged in correctly!'
|
||||
player_command_after_join:
|
||||
enabled: false
|
||||
cmd: ''
|
||||
console_command_after_join:
|
||||
enabled: false
|
||||
cmd: 'alert %p joined the network!'
|
||||
player_command_first_join:
|
||||
enabled: false
|
||||
cmd: ''
|
||||
console_command_first_join:
|
||||
enabled: false
|
||||
cmd: 'alert %p joined for the first time the network!'
|
||||
|
||||
# ===========================
|
||||
# Sessions configuration.
|
||||
# ===========================
|
||||
|
||||
sessions:
|
||||
# Enable sessions.
|
||||
# When a player is authenticated, his IP and his nickname is saved.
|
||||
# The next time the player will join the server, if his IP is the same
|
||||
# of the last time, and the timeout time hasn't expired, he will be
|
||||
# automatically authenticated.
|
||||
enabled: false
|
||||
# Session timeout.
|
||||
# 0 for unlimited time (Very dangerous, use it at your own risk!)
|
||||
# Consider that if player's ip has changed but the timeout hasn't
|
||||
# expired, player will be kicked out of the sever!
|
||||
timeout: 10
|
||||
# When enabled a player's session will expire if someone tries to
|
||||
# login with a different IP Address.
|
||||
expire_on_ip_change: true
|
||||
|
||||
# ===========================
|
||||
# Registration settings.
|
||||
# ===========================
|
||||
|
||||
registration:
|
||||
# After how many time unregistered players should be kicked?
|
||||
# Set to 0 to disable. (default: 30)
|
||||
timeout: 30
|
||||
|
||||
nickname:
|
||||
min_length: 4
|
||||
max_lenght: 16
|
||||
# Regex syntax.
|
||||
allowed_characters: '[a-zA-Z0-9_]*'
|
||||
|
||||
password:
|
||||
# Enable double check of password on registration:
|
||||
# /register <password> <confirmPassword>
|
||||
double_check: true
|
||||
# Minimum password lenght.
|
||||
min_length: 5
|
||||
# Regex syntax.
|
||||
allowed_characters: '[\x21-\x7E]*'
|
||||
# Denied unsafe passwords.
|
||||
unsafePasswords:
|
||||
- '123456'
|
||||
- 'password'
|
||||
- 'qwerty'
|
||||
- '12345'
|
||||
- '54321'
|
||||
|
||||
# ===========================
|
||||
# Login settings.
|
||||
# ===========================
|
||||
|
||||
login:
|
||||
# After how many time unlogged players should be kicked?
|
||||
# Set to 0 to disable. (default: 30)
|
||||
timeout: 30
|
||||
|
||||
|
||||
|
||||
# ===========================
|
||||
# Encryption parameters.
|
||||
# ===========================
|
||||
|
||||
password_encryption:
|
||||
# The hashing algorithm.
|
||||
# Possible values: MD5, SHA1, SHA256, WHIRLPOOL, XAUTH, MD5VB, PHPBB, MYBB, IPB3,
|
||||
# PHPFUSION, SMF, XENFORO, SALTED2MD5, JOOMLA, BCRYPT, WBB3, SHA512, DOUBLEMD5,
|
||||
# PBKDF2, PBKDF2DJANGO, WORDPRESS, ROYALAUTH, CUSTOM (developpers only).
|
||||
encryption_algorithm: SHA256
|
||||
# The salt length for the SALTED2MD5 and MD5(MD5(password)+salt) algorithms.
|
||||
md5_salt_length: 8
|
||||
# If password check fails try all the other hash algorithm.
|
||||
# AuthMe will update the password to the new passwordHash.
|
||||
enable_convertion: false
|
||||
|
||||
# ===========================
|
||||
# Unlogged user restrictions.
|
||||
# ===========================
|
||||
|
||||
unlogged_restrictions:
|
||||
# Deny chat messages send for unlogged users.
|
||||
deny_chat: true
|
||||
# Hide chat to unlogged users.
|
||||
# Only player messages, plugins will be able to send messages to the player anyway.
|
||||
hide_chat: false
|
||||
|
||||
# Deny any command message not in the whitelist below.
|
||||
deny_commands: true
|
||||
command_whitelist:
|
||||
- /login
|
||||
- /register
|
||||
- /l
|
||||
- /reg
|
||||
- /email
|
||||
- /captcha
|
||||
|
||||
movements:
|
||||
# Restrict player movements.
|
||||
restrict: true
|
||||
# Allowed radius.
|
||||
allowed_radius: 0
|
||||
# Should unlogged players have speed = 0?
|
||||
# After the login the walking/flying speeed will be reset to the default value.
|
||||
removeSpeed: true
|
||||
|
||||
# End is there atm xD
|
||||
|
||||
# This option will save the quit location of the players.
|
||||
SaveQuitLocation: false
|
||||
# Should not logged in players be teleported to the spawn?
|
||||
# After the authentication, if SaveQuitLocation is enabled,
|
||||
# they will be teleported back to their normal position.
|
||||
teleportUnAuthedToSpawn: false
|
||||
|
||||
# If enabled, after the login, if the ForceSpawnOnTheseWorlds setting contains
|
||||
# the player's world, he will be teleported to the world spawnpoint.
|
||||
# The quit location of the player will be overwritten.
|
||||
# This is different from "teleportUnAuthedToSpawn" that teleports player
|
||||
# back to his quit location after the authentication.
|
||||
ForceSpawnLocOnJoinEnabled: false
|
||||
# WorldNames where we need to force the spawn location
|
||||
# Warning: This setting is Case Sensitive!
|
||||
ForceSpawnOnTheseWorlds:
|
||||
- world
|
||||
- world_nether
|
||||
- world_the_end
|
||||
|
||||
# this is very important options,
|
||||
# every time player join the server,
|
||||
# if they are registered, AuthMe will switch him
|
||||
# to unLoggedInGroup, this
|
||||
# should prevent all major exploit.
|
||||
# So you can set up on your Permission Plugin
|
||||
# this special group with 0 permissions, or permissions to chat,
|
||||
# or permission to
|
||||
# send private message or all other perms that you want,
|
||||
# the better way is to set up
|
||||
# this group with few permissions,
|
||||
# so if player try to exploit some account,
|
||||
# they can
|
||||
# do anything except what you set in perm Group.
|
||||
# After a correct logged-in player will be
|
||||
# moved to his correct permissions group!
|
||||
# Pay attention group name is case sensitive,
|
||||
# so Admin is different from admin,
|
||||
# otherwise your group will be wiped,
|
||||
# and player join in default group []!
|
||||
# Example unLoggedinGroup: NotLogged
|
||||
unLoggedinGroup: unLoggedinGroup
|
||||
|
||||
# ===========================
|
||||
# Address restrictions
|
||||
# ===========================
|
||||
|
||||
# Max number of registrations per IP (default: 1)
|
||||
maxRegPerIp: 1
|
||||
# Maximum allowed number of Logins per IP, 0 to disable (default: 0)
|
||||
maxLoginPerIp: 0
|
||||
# Maximum allowed number of Joins per IP, 0 to disable (default: 0)
|
||||
maxJoinPerIp: 0
|
||||
|
||||
# When this setting is enabled, online players can't be kicked out
|
||||
# due to "Logged in from another Location"
|
||||
# This setting will prevent potential security exploits.
|
||||
ForceSingleSession: true
|
||||
|
||||
# To activate the restricted user feature you need
|
||||
# to enable this option and configure the
|
||||
# AllowedRestrictedUser field.
|
||||
AllowRestrictedUser: false
|
||||
# The restricted user feature will kick players listed below
|
||||
# if they dont match of the defined ip address.
|
||||
# Example:
|
||||
# AllowedRestrictedUser:
|
||||
# - playername;127.0.0.1
|
||||
AllowedRestrictedUser:
|
||||
- playername;127.0.0.
|
||||
# Ban ip when the ip is not the ip registered in database
|
||||
banUnsafedIP: false
|
||||
|
||||
|
||||
|
||||
# ===============================
|
||||
# Other restrictions
|
||||
# ===============================
|
||||
|
||||
# Should we protect the player inventory before logging in?
|
||||
# Warning: Requires the latest version of ProtocolLib!
|
||||
ProtectInventoryBeforeLogIn: true
|
||||
|
||||
# Should unregistered players be kicked immediately?
|
||||
kickNonRegistered: false
|
||||
# Should players be kicked on wrong password?
|
||||
kickOnWrongPassword: false
|
||||
|
||||
# Should we display all other accounts of a player when he joins?
|
||||
# Required permission: authme.admin.accounts
|
||||
displayOtherAccounts: true
|
||||
|
||||
# ===============================
|
||||
# Restrictions compatibility
|
||||
# ===============================
|
||||
|
||||
# Spawn Priority. Avariable values : authme, essentials, multiverse, default
|
||||
spawnPriority: authme,essentials,multiverse,default
|
||||
# AuthMe will NEVER teleport players!
|
||||
noTeleport: false
|
||||
|
||||
GameMode:
|
||||
# Do you want to set player's gamemode to survival when he joins?
|
||||
# This enables also the settings below.
|
||||
ForceSurvivalMode: false
|
||||
# Do you want to reset player's inventory if player joins with creative mode?
|
||||
ResetInventoryIfCreative: false
|
||||
# Do you want to force the survival mode ONLY after the /login process?
|
||||
ForceOnlyAfterLogin: false
|
||||
|
||||
# sgdc3: Ok, our configuration is shit.... xD Today I will stop there
|
||||
|
||||
|
||||
registration:
|
||||
# enable registration on the server?
|
||||
enabled: true
|
||||
# Send every X seconds a message to a player to
|
||||
# remind him that he has to login/register
|
||||
messageInterval: 5
|
||||
# Only registered and logged in players can play.
|
||||
# See restrictions for exceptions
|
||||
force: true
|
||||
# Does we replace password registration by an Email registration method ?
|
||||
enableEmailRegistrationSystem: false
|
||||
# Enable double check of email when you register
|
||||
# when it's true, registration require that kind of command:
|
||||
# /register <email> <confirmEmail>
|
||||
doubleEmailCheck: false
|
||||
# Do we force kicking player after a successful registration ?
|
||||
# Do not use with login feature below
|
||||
forceKickAfterRegister: false
|
||||
# Does AuthMe need to enforce a /login after a successful registration ?
|
||||
forceLoginAfterRegister: false
|
||||
unrestrictions:
|
||||
# below you can list all your account name, that
|
||||
# AuthMe will ignore for registration or login, configure it
|
||||
# at your own risk!! Remember that if you are going to add
|
||||
# nickname with [], you have to delimit name with ' '.
|
||||
# this option add compatibility with BuildCraft and some
|
||||
# other mods.
|
||||
# It is CaseSensitive!
|
||||
UnrestrictedName: []
|
||||
# Message language, available : en, de, br, cz, pl, fr, ru, hu, sk, es, zhtw, fi, zhcn, lt, it, ko, pt
|
||||
messagesLanguage: en
|
||||
# Force these commands after /login, without any '/', use %p for replace with player name
|
||||
forceCommands: []
|
||||
# Force these commands after /login as a server console, without any '/', use %p for replace with player name
|
||||
forceCommandsAsConsole: []
|
||||
# Force these commands after /register, without any '/', use %p for replace with player name
|
||||
forceRegisterCommands: []
|
||||
# Force these commands after /register as a server console, without any '/', use %p for replace with player name
|
||||
forceRegisterCommandsAsConsole: []
|
||||
# Do we need to broadcast the welcome message to all server or only to the player? set true for server or false for player
|
||||
broadcastWelcomeMessage: false
|
||||
# Do we need to delay the join/leave message to be displayed only when the player is authenticated ?
|
||||
delayJoinMessage: false
|
||||
removeJoinMessage: false
|
||||
removeLeaveMessage: false
|
||||
# Do we need to add potion effect Blinding before login/register ?
|
||||
applyBlindEffect: false
|
||||
ExternalBoardOptions:
|
||||
# MySQL column for the salt , needed for some forum/cms support
|
||||
mySQLColumnSalt: ''
|
||||
# MySQL column for the group, needed for some forum/cms support
|
||||
mySQLColumnGroup: ''
|
||||
# -1 mean disabled. If u want that only
|
||||
# activated player can login in your server
|
||||
# u can put in this options the group number
|
||||
# of unactivated user, needed for some forum/cms support
|
||||
nonActivedUserGroup: -1
|
||||
# Other MySQL columns where we need to put the Username (case sensitive)
|
||||
mySQLOtherUsernameColumns: []
|
||||
# How much Log to Round needed in BCrypt(do not change it if you do not know what's your doing)
|
||||
bCryptLog2Round: 10
|
||||
# phpBB prefix defined during phpbb installation process
|
||||
phpbbTablePrefix: 'phpbb_'
|
||||
# phpBB activated group id , 2 is default registered group defined by phpbb
|
||||
phpbbActivatedGroupId: 2
|
||||
# WordPress prefix defined during WordPress installation process
|
||||
wordpressTablePrefix: 'wp_'
|
||||
permission:
|
||||
# Take care with this options, if you dont want
|
||||
# to use Vault and Group Switching of
|
||||
# AuthMe for unloggedIn players put true
|
||||
# below, default is false.
|
||||
EnablePermissionCheck: false
|
||||
BackupSystem:
|
||||
# Enable or Disable Automatic Backup
|
||||
ActivateBackup: false
|
||||
# set Backup at every start of Server
|
||||
OnServerStart: false
|
||||
# set Backup at every stop of Server
|
||||
OnServerStop: true
|
||||
# Windows only mysql installation Path
|
||||
MysqlWindowsPath: 'C:\\Program Files\\MySQL\\MySQL Server 5.1\\'
|
||||
Security:
|
||||
SQLProblem:
|
||||
# Stop the server if we can't contact the sql database
|
||||
# Take care with this, if you set that to false,
|
||||
# AuthMe automatically disable and the server is not protected!
|
||||
stopServer: true
|
||||
ReloadCommand:
|
||||
# /reload support
|
||||
useReloadCommandSupport: true
|
||||
console:
|
||||
# Remove spam console
|
||||
noConsoleSpam: false
|
||||
captcha:
|
||||
# Player need to put a captcha when he fails too lot the password
|
||||
useCaptcha: false
|
||||
# Max allowed tries before request a captcha
|
||||
maxLoginTry: 5
|
||||
# Captcha length
|
||||
captchaLength: 5
|
||||
Converter:
|
||||
Rakamak:
|
||||
# Rakamak file name
|
||||
fileName: users.rak
|
||||
# Rakamak use ip ?
|
||||
useIP: false
|
||||
# IP file name for rakamak
|
||||
ipFileName: UsersIp.rak
|
||||
CrazyLogin:
|
||||
# CrazyLogin database file
|
||||
fileName: accounts.db
|
||||
Email:
|
||||
# Email SMTP server host
|
||||
mailSMTP: smtp.gmail.com
|
||||
# Email SMTP server port
|
||||
mailPort: 465
|
||||
# Email account that send the mail
|
||||
mailAccount: ''
|
||||
# Email account password
|
||||
mailPassword: ''
|
||||
# Custom SenderName, that replace the mailAccount name in the email
|
||||
mailSenderName: ''
|
||||
# Random password length
|
||||
RecoveryPasswordLength: 8
|
||||
# Email subject of password get
|
||||
mailSubject: 'Your new AuthMe Password'
|
||||
# Email text here
|
||||
mailText: 'Dear <playername>, <br /><br /> This is your new AuthMe password for the server <br /><br /> <servername> : <br /><br /> <generatedpass><br /><br />Do not forget to change password after login! <br /> /changepassword <generatedpass> newPassword'
|
||||
# Like maxRegPerIp but with email
|
||||
maxRegPerEmail: 1
|
||||
# Recall players to add an email ?
|
||||
recallPlayers: false
|
||||
# Delay in minute for the recall scheduler
|
||||
delayRecall: 5
|
||||
# Blacklist these domains for emails
|
||||
emailBlacklisted:
|
||||
- 10minutemail.com
|
||||
# WhiteList only these domains for emails
|
||||
emailWhitelisted: []
|
||||
# Do we need to send new password draw in an image ?
|
||||
generateImage: false
|
||||
Hooks:
|
||||
# Do we need to hook with multiverse for spawn checking?
|
||||
multiverse: true
|
||||
# Do we need to hook with BungeeCord for get the real Player ip ?
|
||||
bungeecord: false
|
||||
# Do we need to disable Essentials SocialSpy on join ?
|
||||
disableSocialSpy: true
|
||||
# Do we need to force /motd Essentials command on join ?
|
||||
useEssentialsMotd: false
|
||||
# Do we need to cache custom Attributes ?
|
||||
customAttributes: false
|
||||
Purge:
|
||||
# On Enable , does AuthMe need to purge automatically old accounts unused ?
|
||||
useAutoPurge: false
|
||||
# Number of Days an account become Unused
|
||||
daysBeforeRemovePlayer: 60
|
||||
# Do we need to remove the player.dat file during purge process ?
|
||||
removePlayerDat: false
|
||||
# Do we need to remove the Essentials/users/player.yml file during purge process ?
|
||||
removeEssentialsFile: false
|
||||
# World where are players.dat stores
|
||||
defaultWorld: 'world'
|
||||
# Do we need to remove LimitedCreative/inventories/player.yml , player_creative.yml files during purge process ?
|
||||
removeLimitedCreativesInventories: false
|
||||
# Do we need to remove the AntiXRayData/PlayerData/player file during purge process ?
|
||||
removeAntiXRayFile: false
|
||||
# Do we need to remove permissions ?
|
||||
removePermissions: false
|
||||
Protection:
|
||||
# Enable some servers protection ( country based login, antibot )
|
||||
enableProtection: false
|
||||
# Countries allowed to join the server and register, see http://dev.bukkit.org/bukkit-plugins/authme-reloaded/pages/countries-codes/ for countries' codes
|
||||
countries:
|
||||
- US
|
||||
- GB
|
||||
# Countries blacklisted automatically ( without any needed to enable protection )
|
||||
countriesBlacklist:
|
||||
- A1
|
||||
# Do we need to enable automatic antibot system?
|
||||
enableAntiBot: false
|
||||
# Max number of player allowed to login in 5 secs before enable AntiBot system automatically
|
||||
antiBotSensibility: 5
|
||||
# Duration in minutes of the antibot automatic system
|
||||
antiBotDuration: 10
|
||||
VeryGames:
|
||||
# These features are only available on VeryGames Server Provider
|
||||
enableIpCheck: false
|
||||
@ -1,199 +0,0 @@
|
||||
name: ${pluginName}
|
||||
authors: [${pluginAuthors}]
|
||||
website: ${project.url}
|
||||
description: ${project.description}
|
||||
main: ${mainClass}
|
||||
version: ${project.version}-b${buildNumber}
|
||||
softdepend:
|
||||
- Vault
|
||||
- PermissionsBukkit
|
||||
- PermissionsEX
|
||||
- EssentialsGroupManager
|
||||
- Multiverse-Core
|
||||
- Essentials
|
||||
- EssentialsSpawn
|
||||
- ProtocolLib
|
||||
commands:
|
||||
authme:
|
||||
description: AuthMe admin commands
|
||||
usage: '/authme reload|register playername password|changepassword playername password|unregister playername|version|converter datatype'
|
||||
permission: authme.admin
|
||||
register:
|
||||
description: Register an account
|
||||
usage: /register password confirmpassword
|
||||
aliases: [reg]
|
||||
permission: authme.player.register
|
||||
login:
|
||||
description: Login into a account
|
||||
usage: /login password
|
||||
aliases: [l,log]
|
||||
permission: authme.player.login
|
||||
changepassword:
|
||||
description: Change password of a account
|
||||
usage: /changepassword oldPassword newPassword
|
||||
permission: authme.player.changepassword
|
||||
logout:
|
||||
description: Logout from the server
|
||||
usage: /logout
|
||||
permission: authme.player.logout
|
||||
unregister:
|
||||
description: unregister your account
|
||||
usage: /unregister password
|
||||
permission: authme.player.unregister
|
||||
email:
|
||||
description: Add Email or recover password
|
||||
usage: '/email add your@email.com your@email.com|change oldEmail newEmail|recovery your@email.com'
|
||||
permission: authme.player.email
|
||||
captcha:
|
||||
description: Captcha command
|
||||
usage: /captcha theCaptcha
|
||||
permission: authme.player.captcha
|
||||
permissions:
|
||||
authme.canbeforced:
|
||||
description: Allow the user to be forced-logged via API
|
||||
default: true
|
||||
authme.player:
|
||||
description: Gives access to all authme player commands
|
||||
default: true
|
||||
children:
|
||||
authme.player.login: true
|
||||
authme.player.logout: true
|
||||
authme.player.register: true
|
||||
authme.player.unregister: true
|
||||
authme.player.changepassword: true
|
||||
authme.player.captcha: true
|
||||
authme.player.email: true
|
||||
authme.player.register:
|
||||
description: Register your account
|
||||
default: false
|
||||
authme.player.unregister:
|
||||
description: Unregister your account
|
||||
default: false
|
||||
authme.player.login:
|
||||
description: Login into your account
|
||||
default: false
|
||||
authme.player.logout:
|
||||
description: Logout from your account
|
||||
default: false
|
||||
authme.player.changepassword:
|
||||
description: Change password of your account
|
||||
default: false
|
||||
authme.player.email:
|
||||
description: Gives access to player's email commands
|
||||
default: false
|
||||
children:
|
||||
authme.player.email.add: true
|
||||
authme.player.email.change: true
|
||||
authme.player.email.recover: true
|
||||
authme.player.email.add:
|
||||
description: Add an email to your account
|
||||
default: false
|
||||
authme.player.email.change:
|
||||
description: Change email of your account
|
||||
default: false
|
||||
authme.player.email.recover:
|
||||
description: Recover your account
|
||||
default: false
|
||||
authme.player.captcha:
|
||||
description: Captcha command
|
||||
default: false
|
||||
authme.admin:
|
||||
description: Gives access to all authme admin commands
|
||||
default: op
|
||||
children:
|
||||
authme.admin.forcelogin: true
|
||||
authme.admin.forcelogout: true
|
||||
authme.admin.register: true
|
||||
authme.admin.unregister: true
|
||||
authme.admin.changemail: true
|
||||
authme.admin.changepassword: true
|
||||
authme.admin.lastlogin: true
|
||||
authme.admin.accounts: true
|
||||
authme.admin.getemail: true
|
||||
authme.admin.getip: true
|
||||
authme.admin.setspawn: true
|
||||
authme.admin.spawn: true
|
||||
authme.admin.setfirstspawn: true
|
||||
authme.admin.firstspawn: true
|
||||
authme.admin.purge: true
|
||||
authme.admin.purgebannedplayers: true
|
||||
authme.admin.purgelastpos: true
|
||||
authme.admin.converter: true
|
||||
authme.admin.reload: true
|
||||
authme.admin.switchantibot: true
|
||||
authme.admin.seeotheraccounts: true
|
||||
authme.admin.register:
|
||||
description: Register an account
|
||||
default: false
|
||||
authme.admin.unregister:
|
||||
description: Unregister an account
|
||||
default: false
|
||||
authme.admin.forcelogin:
|
||||
description: Force login for that player
|
||||
default: false
|
||||
authme.admin.forcelogout:
|
||||
description: Force logout for that player
|
||||
default: false
|
||||
authme.admin.changepassword:
|
||||
description: Change the password of an account
|
||||
default: false
|
||||
authme.admin.getemail:
|
||||
description: Get last email about a player
|
||||
default: false
|
||||
authme.admin.changeemail:
|
||||
description: Change a player email
|
||||
default: false
|
||||
authme.admin.accounts:
|
||||
description: Display Players Accounts
|
||||
default: false
|
||||
authme.admin.seeotheraccounts:
|
||||
description: Display other accounts about a player when he logs in
|
||||
default: false
|
||||
authme.admin.lastlogin:
|
||||
description: Get last login date about a player
|
||||
default: false
|
||||
authme.admin.getip:
|
||||
description: Get IP from a player (fake and real)
|
||||
default: false
|
||||
authme.admin.setspawn:
|
||||
description: Set the AuthMe spawn point
|
||||
default: false
|
||||
authme.admin.spawn:
|
||||
description: Teleport to AuthMe spawn point
|
||||
default: false
|
||||
authme.admin.setfirstspawn:
|
||||
description: Set the AuthMe First Spawn Point
|
||||
default: false
|
||||
authme.admin.firstspawn:
|
||||
description: Teleport to AuthMe First Spawn Point
|
||||
default: false
|
||||
authme.admin.switchantibot:
|
||||
description: Switch AntiBot mode on/off
|
||||
default: false
|
||||
authme.admin.purge:
|
||||
description: Database purge command
|
||||
default: false
|
||||
authme.admin.purgebannedplayers:
|
||||
description: Purge banned players
|
||||
default: false
|
||||
authme.admin.purgelastpos:
|
||||
description: Purge last position of a player/players
|
||||
default: false
|
||||
authme.admin.converter:
|
||||
description: Allow the /authme converter command
|
||||
default: false
|
||||
authme.admin.reload:
|
||||
description: Reload the plugin
|
||||
default: false
|
||||
authme.vip:
|
||||
description: Allow vip slot when the server is full
|
||||
default: false
|
||||
authme.bypassantibot:
|
||||
description: Bypass the AntiBot check
|
||||
default: false
|
||||
authme.allowmultipleaccounts:
|
||||
description: Allow more accounts for same ip
|
||||
default: false
|
||||
authme.bypassforcesurvival:
|
||||
description: Bypass all ForceSurvival features
|
||||
default: false
|
||||
File diff suppressed because one or more lines are too long
@ -1,148 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*****************************************************************************
|
||||
* AuthMe website integration logic *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* Allows interaction with the AuthMe database (registration, password *
|
||||
* verification). Don't forget to update the AUTHME_TABLE value and your *
|
||||
* database credentials in getAuthmeMySqli(). *
|
||||
* *
|
||||
* Source: https://github.com/AuthMe/AuthMeReloaded/ *
|
||||
*****************************************************************************/
|
||||
abstract class AuthMeController {
|
||||
|
||||
const AUTHME_TABLE = 'authme';
|
||||
|
||||
/**
|
||||
* Entry point function to check supplied credentials against the AuthMe database.
|
||||
*
|
||||
* @param string $username the username
|
||||
* @param string $password the password
|
||||
* @return bool true iff the data is correct, false otherwise
|
||||
*/
|
||||
function checkPassword($username, $password) {
|
||||
if (is_scalar($username) && is_scalar($password)) {
|
||||
$hash = $this->getHashFromDatabase($username);
|
||||
if ($hash) {
|
||||
return $this->isValidPassword($password, $hash);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the user exists in the database or not.
|
||||
*
|
||||
* @param string $username the username to check
|
||||
* @return bool true if the user exists; false otherwise
|
||||
*/
|
||||
function isUserRegistered($username) {
|
||||
$mysqli = $this->getAuthmeMySqli();
|
||||
if ($mysqli !== null) {
|
||||
$stmt = $mysqli->prepare('SELECT 1 FROM ' . self::AUTHME_TABLE . ' WHERE username = ?');
|
||||
$stmt->bind_param('s', $username);
|
||||
$stmt->execute();
|
||||
return $stmt->fetch();
|
||||
}
|
||||
|
||||
// Defensive default to true; we actually don't know
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a player with the given username.
|
||||
*
|
||||
* @param string $username the username to register
|
||||
* @param string $password the password to associate to the user
|
||||
* @param string $email the email (may be empty)
|
||||
* @return bool whether or not the registration was successful
|
||||
*/
|
||||
function register($username, $password, $email) {
|
||||
$email = $email ? $email : 'your@email.com';
|
||||
$mysqli = $this->getAuthmeMySqli();
|
||||
if ($mysqli !== null) {
|
||||
$hash = $this->hash($password);
|
||||
$stmt = $mysqli->prepare('INSERT INTO ' . self::AUTHME_TABLE . ' (username, realname, password, email, ip) '
|
||||
. 'VALUES (?, ?, ?, ?, ?)');
|
||||
$username_low = strtolower($username);
|
||||
$stmt->bind_param('sssss', $username_low, $username, $hash, $email, $_SERVER['REMOTE_ADDR']);
|
||||
return $stmt->execute();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Changes password for player.
|
||||
*
|
||||
* @param string $username the username
|
||||
* @param string $password the password
|
||||
* @return bool true whether or not password change was successful
|
||||
*/
|
||||
function changePassword($username, $password) {
|
||||
$mysqli = $this->getAuthmeMySqli();
|
||||
if ($mysqli !== null) {
|
||||
$hash = $this->hash($password);
|
||||
$stmt = $mysqli->prepare('UPDATE ' . self::AUTHME_TABLE . ' SET password=? '
|
||||
. 'WHERE username=?');
|
||||
$username_low = strtolower($username);
|
||||
$stmt->bind_param('ss', $hash, $username_low);
|
||||
return $stmt->execute();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hashes the given password.
|
||||
*
|
||||
* @param $password string the clear-text password to hash
|
||||
* @return string the resulting hash
|
||||
*/
|
||||
protected abstract function hash($password);
|
||||
|
||||
/**
|
||||
* Checks whether the given password matches the hash.
|
||||
*
|
||||
* @param $password string the clear-text password
|
||||
* @param $hash string the password hash
|
||||
* @return boolean true if the password matches, false otherwise
|
||||
*/
|
||||
protected abstract function isValidPassword($password, $hash);
|
||||
|
||||
/**
|
||||
* Returns a connection to the database.
|
||||
*
|
||||
* @return mysqli|null the mysqli object or null upon error
|
||||
*/
|
||||
private function getAuthmeMySqli() {
|
||||
// CHANGE YOUR DATABASE DETAILS HERE BELOW: host, user, password, database name
|
||||
$mysqli = new mysqli('localhost', 'root', '', 'authme');
|
||||
if (mysqli_connect_error()) {
|
||||
printf('Could not connect to AuthMe database. Errno: %d, error: "%s"',
|
||||
mysqli_connect_errno(), mysqli_connect_error());
|
||||
return null;
|
||||
}
|
||||
return $mysqli;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the hash associated with the given user from the database.
|
||||
*
|
||||
* @param string $username the username whose hash should be retrieved
|
||||
* @return string|null the hash, or null if unavailable (e.g. username doesn't exist)
|
||||
*/
|
||||
private function getHashFromDatabase($username) {
|
||||
$mysqli = $this->getAuthmeMySqli();
|
||||
if ($mysqli !== null) {
|
||||
$stmt = $mysqli->prepare('SELECT password FROM ' . self::AUTHME_TABLE . ' WHERE username = ?');
|
||||
$stmt->bind_param('s', $username);
|
||||
$stmt->execute();
|
||||
$stmt->bind_result($password);
|
||||
if ($stmt->fetch()) {
|
||||
return $password;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,20 +0,0 @@
|
||||
<?php
|
||||
|
||||
/***********************************************************
|
||||
* AuthMe website integration logic for BCrypt *
|
||||
* ------------------------------------------------------- *
|
||||
* See AuthMeController for details. *
|
||||
* *
|
||||
* Source: https://github.com/AuthMe/AuthMeReloaded/ *
|
||||
***********************************************************/
|
||||
class Bcrypt extends AuthMeController {
|
||||
|
||||
protected function hash($password) {
|
||||
return password_hash($password, PASSWORD_BCRYPT);
|
||||
}
|
||||
|
||||
protected function isValidPassword($password, $hash) {
|
||||
return password_verify($password, $hash);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,53 +0,0 @@
|
||||
<?php
|
||||
|
||||
/***********************************************************
|
||||
* AuthMe website integration logic for PBKDF2 *
|
||||
* ------------------------------------------------------- *
|
||||
* See AuthMeController for details. *
|
||||
* *
|
||||
* Source: https://github.com/AuthMe/AuthMeReloaded/ *
|
||||
***********************************************************/
|
||||
class Pbkdf2 extends AuthMeController {
|
||||
|
||||
/** @var string[] range of characters for salt generation */
|
||||
private $CHARS;
|
||||
|
||||
const SALT_LENGTH = 16;
|
||||
const NUMBER_OF_ITERATIONS = 10000;
|
||||
|
||||
public function __construct() {
|
||||
$this->CHARS = self::initCharRange();
|
||||
}
|
||||
|
||||
protected function isValidPassword($password, $hash) {
|
||||
// hash := pbkdf2_sha256$iterations$salt$hash
|
||||
$parts = explode('$', $hash);
|
||||
return count($parts) === 4 && $hash === $this->computeHash($parts[1], $parts[2], $password);
|
||||
}
|
||||
|
||||
protected function hash($password) {
|
||||
$salt = $this->generateSalt();
|
||||
return $this->computeHash(self::NUMBER_OF_ITERATIONS, $salt, $password);
|
||||
}
|
||||
|
||||
private function computeHash($iterations, $salt, $password) {
|
||||
return 'pbkdf2_sha256$' . self::NUMBER_OF_ITERATIONS . '$' . $salt
|
||||
. '$' . hash_pbkdf2('sha256', $password, $salt, self::NUMBER_OF_ITERATIONS, 64, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string randomly generated salt
|
||||
*/
|
||||
private function generateSalt() {
|
||||
$maxCharIndex = count($this->CHARS) - 1;
|
||||
$salt = '';
|
||||
for ($i = 0; $i < self::SALT_LENGTH; ++$i) {
|
||||
$salt .= $this->CHARS[mt_rand(0, $maxCharIndex)];
|
||||
}
|
||||
return $salt;
|
||||
}
|
||||
|
||||
private static function initCharRange() {
|
||||
return array_merge(range('0', '9'), range('a', 'f'));
|
||||
}
|
||||
}
|
||||
@ -1,48 +0,0 @@
|
||||
<?php
|
||||
|
||||
/***********************************************************
|
||||
* AuthMe website integration logic for SHA256 *
|
||||
* ------------------------------------------------------- *
|
||||
* See AuthMeController for details. *
|
||||
* *
|
||||
* Source: https://github.com/AuthMe/AuthMeReloaded/ *
|
||||
***********************************************************/
|
||||
class Sha256 extends AuthMeController {
|
||||
|
||||
/** @var string[] range of characters for salt generation */
|
||||
private $CHARS;
|
||||
|
||||
const SALT_LENGTH = 16;
|
||||
|
||||
public function __construct() {
|
||||
$this->CHARS = self::initCharRange();
|
||||
}
|
||||
|
||||
protected function isValidPassword($password, $hash) {
|
||||
// $SHA$salt$hash, where hash := sha256(sha256(password) . salt)
|
||||
$parts = explode('$', $hash);
|
||||
return count($parts) === 4 && $parts[3] === hash('sha256', hash('sha256', $password) . $parts[2]);
|
||||
}
|
||||
|
||||
protected function hash($password) {
|
||||
$salt = $this->generateSalt();
|
||||
return '$SHA$' . $salt . '$' . hash('sha256', hash('sha256', $password) . $salt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string randomly generated salt
|
||||
*/
|
||||
private function generateSalt() {
|
||||
$maxCharIndex = count($this->CHARS) - 1;
|
||||
$salt = '';
|
||||
for ($i = 0; $i < self::SALT_LENGTH; ++$i) {
|
||||
$salt .= $this->CHARS[mt_rand(0, $maxCharIndex)];
|
||||
}
|
||||
return $salt;
|
||||
}
|
||||
|
||||
private static function initCharRange() {
|
||||
return array_merge(range('0', '9'), range('a', 'f'));
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,102 +0,0 @@
|
||||
<!--
|
||||
This is a demo page for AuthMe website integration.
|
||||
See AuthMeController.php and the extending classes for the PHP code you need.
|
||||
-->
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>AuthMe Integration Sample</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
</head>
|
||||
<body>
|
||||
<?php
|
||||
error_reporting(E_ALL);
|
||||
|
||||
require 'AuthMeController.php';
|
||||
|
||||
// Change this to the file of the hash encryption you need, e.g. Bcrypt.php or Sha256.php
|
||||
require 'Sha256.php';
|
||||
// The class name must correspond to the file you have in require above! e.g. require 'Sha256.php'; and new Sha256();
|
||||
$authme_controller = new Sha256();
|
||||
|
||||
$action = get_from_post_or_empty('action');
|
||||
$user = get_from_post_or_empty('username');
|
||||
$pass = get_from_post_or_empty('password');
|
||||
$email = get_from_post_or_empty('email');
|
||||
|
||||
$was_successful = false;
|
||||
if ($action && $user && $pass) {
|
||||
if ($action === 'Log in') {
|
||||
$was_successful = process_login($user, $pass, $authme_controller);
|
||||
} else if ($action === 'Register') {
|
||||
$was_successful = process_register($user, $pass, $email, $authme_controller);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$was_successful) {
|
||||
echo '<h1>Login sample</h1>
|
||||
This is a demo form for AuthMe website integration. Enter your AuthMe login details
|
||||
into the following form to test it.
|
||||
<form method="post">
|
||||
<table>
|
||||
<tr><td>Name</td><td><input type="text" value="' . htmlspecialchars($user) . '" name="username" /></td></tr>
|
||||
<tr><td>Email</td><td><input type="text" value="' . htmlspecialchars($email) . '" name="email" /></td></tr>
|
||||
<tr><td>Pass</td><td><input type="password" value="' . htmlspecialchars($pass) . '" name="password" /></td></tr>
|
||||
<tr>
|
||||
<td><input type="submit" name="action" value="Log in" /></td>
|
||||
<td><input type="submit" name="action" value="Register" /></td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>';
|
||||
}
|
||||
|
||||
function get_from_post_or_empty($index_name) {
|
||||
return trim(
|
||||
filter_input(INPUT_POST, $index_name, FILTER_UNSAFE_RAW, FILTER_REQUIRE_SCALAR | FILTER_FLAG_STRIP_LOW)
|
||||
?: '');
|
||||
}
|
||||
|
||||
|
||||
// Login logic
|
||||
function process_login($user, $pass, AuthMeController $controller) {
|
||||
if ($controller->checkPassword($user, $pass)) {
|
||||
printf('<h1>Hello, %s!</h1>', htmlspecialchars($user));
|
||||
echo 'Successful login. Nice to have you back!'
|
||||
. '<br /><a href="index.php">Back to form</a>';
|
||||
return true;
|
||||
} else {
|
||||
echo '<h1>Error</h1> Invalid username or password.';
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Register logic
|
||||
function process_register($user, $pass, $email, AuthMeController $controller) {
|
||||
if ($controller->isUserRegistered($user)) {
|
||||
echo '<h1>Error</h1> This user already exists.';
|
||||
} else if (!is_email_valid($email)) {
|
||||
echo '<h1>Error</h1> The supplied email is invalid.';
|
||||
} else {
|
||||
// Note that we don't validate the password or username at all in this demo...
|
||||
$register_success = $controller->register($user, $pass, $email);
|
||||
if ($register_success) {
|
||||
printf('<h1>Welcome, %s!</h1>Thanks for registering', htmlspecialchars($user));
|
||||
echo '<br /><a href="index.php">Back to form</a>';
|
||||
return true;
|
||||
} else {
|
||||
echo '<h1>Error</h1>Unfortunately, there was an error during the registration.';
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function is_email_valid($email) {
|
||||
return trim($email) === ''
|
||||
? true // accept no email
|
||||
: filter_var($email, FILTER_VALIDATE_EMAIL);
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@ -1,357 +0,0 @@
|
||||
package fr.xephi.authme;
|
||||
|
||||
import ch.jalu.injector.Injector;
|
||||
import ch.jalu.injector.InjectorBuilder;
|
||||
import fr.xephi.authme.api.v3.AuthMeApi;
|
||||
import fr.xephi.authme.command.CommandHandler;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.initialization.DataFolder;
|
||||
import fr.xephi.authme.initialization.DataSourceProvider;
|
||||
import fr.xephi.authme.initialization.OnShutdownPlayerSaver;
|
||||
import fr.xephi.authme.initialization.OnStartupTasks;
|
||||
import fr.xephi.authme.initialization.SettingsProvider;
|
||||
import fr.xephi.authme.initialization.TaskCloser;
|
||||
import fr.xephi.authme.listener.BlockListener;
|
||||
import fr.xephi.authme.listener.EntityListener;
|
||||
import fr.xephi.authme.listener.PlayerListener;
|
||||
import fr.xephi.authme.listener.PlayerListener111;
|
||||
import fr.xephi.authme.listener.PlayerListener19;
|
||||
import fr.xephi.authme.listener.PlayerListener19Spigot;
|
||||
import fr.xephi.authme.listener.ServerListener;
|
||||
import fr.xephi.authme.mail.EmailService;
|
||||
import fr.xephi.authme.output.ConsoleLoggerFactory;
|
||||
import fr.xephi.authme.security.crypts.Sha256;
|
||||
import fr.xephi.authme.service.BackupService;
|
||||
import fr.xephi.authme.service.BukkitService;
|
||||
import fr.xephi.authme.service.MigrationService;
|
||||
import fr.xephi.authme.service.bungeecord.BungeeReceiver;
|
||||
import fr.xephi.authme.service.yaml.YamlParseException;
|
||||
import fr.xephi.authme.settings.Settings;
|
||||
import fr.xephi.authme.settings.SettingsWarner;
|
||||
import fr.xephi.authme.settings.properties.EmailSettings;
|
||||
import fr.xephi.authme.settings.properties.SecuritySettings;
|
||||
import fr.xephi.authme.task.CleanupTask;
|
||||
import fr.xephi.authme.task.purge.PurgeService;
|
||||
import fr.xephi.authme.util.ExceptionUtils;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.bukkit.scheduler.BukkitScheduler;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static fr.xephi.authme.service.BukkitService.TICKS_PER_MINUTE;
|
||||
import static fr.xephi.authme.util.Utils.isClassLoaded;
|
||||
|
||||
/**
|
||||
* The AuthMe main class.
|
||||
*/
|
||||
public class AuthMe extends JavaPlugin {
|
||||
|
||||
// Constants
|
||||
private static final String PLUGIN_NAME = "AuthMeReloaded";
|
||||
private static final String LOG_FILENAME = "authme.log";
|
||||
private static final int CLEANUP_INTERVAL = 5 * TICKS_PER_MINUTE;
|
||||
|
||||
// Version and build number values
|
||||
private static String pluginVersion = "5.6.0";
|
||||
private static String pluginBuildNumber = "Custom";
|
||||
|
||||
// Private instances
|
||||
private EmailService emailService;
|
||||
private CommandHandler commandHandler;
|
||||
private Settings settings;
|
||||
private DataSource database;
|
||||
private BukkitService bukkitService;
|
||||
private Injector injector;
|
||||
private BackupService backupService;
|
||||
private ConsoleLogger logger;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public AuthMe() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the plugin's name.
|
||||
*
|
||||
* @return The plugin's name.
|
||||
*/
|
||||
public static String getPluginName() {
|
||||
return PLUGIN_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the plugin's version.
|
||||
*
|
||||
* @return The plugin's version.
|
||||
*/
|
||||
public static String getPluginVersion() {
|
||||
return pluginVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the plugin's build number.
|
||||
*
|
||||
* @return The plugin's build number.
|
||||
*/
|
||||
public static String getPluginBuildNumber() {
|
||||
return pluginBuildNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method called when the server enables the plugin.
|
||||
*/
|
||||
@Override
|
||||
public void onEnable() {
|
||||
// Load the plugin version data from the plugin description file
|
||||
loadPluginInfo(getDescription().getVersion());
|
||||
|
||||
// Set the Logger instance and log file path
|
||||
ConsoleLogger.initialize(getLogger(), new File(getDataFolder(), LOG_FILENAME));
|
||||
logger = ConsoleLoggerFactory.get(AuthMe.class);
|
||||
|
||||
// Check server version
|
||||
if (!isClassLoaded("org.spigotmc.event.player.PlayerSpawnLocationEvent")
|
||||
|| !isClassLoaded("org.bukkit.event.player.PlayerInteractAtEntityEvent")) {
|
||||
logger.warning("You are running an unsupported server version (" + getServerNameVersionSafe() + "). "
|
||||
+ "AuthMe requires Spigot 1.8.X or later!");
|
||||
stopOrUnload();
|
||||
return;
|
||||
}
|
||||
|
||||
// Prevent running AuthMeBridge due to major exploit issues
|
||||
if (getServer().getPluginManager().isPluginEnabled("AuthMeBridge")) {
|
||||
logger.warning("Detected AuthMeBridge, support for it has been dropped as it was "
|
||||
+ "causing exploit issues, please use AuthMeBungee instead! Aborting!");
|
||||
stopOrUnload();
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize the plugin
|
||||
try {
|
||||
initialize();
|
||||
} catch (Throwable th) {
|
||||
YamlParseException yamlParseException = ExceptionUtils.findThrowableInCause(YamlParseException.class, th);
|
||||
if (yamlParseException == null) {
|
||||
logger.logException("Aborting initialization of AuthMe:", th);
|
||||
th.printStackTrace();
|
||||
} else {
|
||||
logger.logException("File '" + yamlParseException.getFile() + "' contains invalid YAML. "
|
||||
+ "Please run its contents through https://yamllint.com", yamlParseException);
|
||||
}
|
||||
stopOrUnload();
|
||||
return;
|
||||
}
|
||||
|
||||
// Show settings warnings
|
||||
injector.getSingleton(SettingsWarner.class).logWarningsForMisconfigurations();
|
||||
|
||||
// Schedule clean up task
|
||||
CleanupTask cleanupTask = injector.getSingleton(CleanupTask.class);
|
||||
cleanupTask.runTaskTimerAsynchronously(this, CLEANUP_INTERVAL, CLEANUP_INTERVAL);
|
||||
|
||||
// Do a backup on start
|
||||
backupService.doBackup(BackupService.BackupCause.START);
|
||||
|
||||
// Set up Metrics
|
||||
OnStartupTasks.sendMetrics(this, settings);
|
||||
|
||||
// Successful message
|
||||
logger.info("AuthMeReloaded successfully enabled!");
|
||||
|
||||
// Purge on start if enabled
|
||||
PurgeService purgeService = injector.getSingleton(PurgeService.class);
|
||||
purgeService.runAutoPurge();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the version and build number of the plugin from the description file.
|
||||
*
|
||||
* @param versionRaw the version as given by the plugin description file
|
||||
*/
|
||||
private static void loadPluginInfo(String versionRaw) {
|
||||
int index = versionRaw.lastIndexOf("-");
|
||||
if (index != -1) {
|
||||
pluginVersion = versionRaw.substring(0, index);
|
||||
pluginBuildNumber = versionRaw.substring(index + 1);
|
||||
if (pluginBuildNumber.startsWith("b")) {
|
||||
pluginBuildNumber = pluginBuildNumber.substring(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the plugin and all the services.
|
||||
*/
|
||||
private void initialize() {
|
||||
// Create plugin folder
|
||||
getDataFolder().mkdir();
|
||||
|
||||
// Create injector, provide elements from the Bukkit environment and register providers
|
||||
injector = new InjectorBuilder()
|
||||
.addDefaultHandlers("fr.xephi.authme")
|
||||
.create();
|
||||
injector.register(AuthMe.class, this);
|
||||
injector.register(Server.class, getServer());
|
||||
injector.register(PluginManager.class, getServer().getPluginManager());
|
||||
injector.register(BukkitScheduler.class, getServer().getScheduler());
|
||||
injector.provide(DataFolder.class, getDataFolder());
|
||||
injector.registerProvider(Settings.class, SettingsProvider.class);
|
||||
injector.registerProvider(DataSource.class, DataSourceProvider.class);
|
||||
|
||||
// Get settings and set up logger
|
||||
settings = injector.getSingleton(Settings.class);
|
||||
ConsoleLoggerFactory.reloadSettings(settings);
|
||||
OnStartupTasks.setupConsoleFilter(getLogger());
|
||||
|
||||
// Set all service fields on the AuthMe class
|
||||
instantiateServices(injector);
|
||||
|
||||
// Convert deprecated PLAINTEXT hash entries
|
||||
MigrationService.changePlainTextToSha256(settings, database, new Sha256());
|
||||
|
||||
// If the server is empty (fresh start) just set all the players as unlogged
|
||||
if (bukkitService.getOnlinePlayers().isEmpty()) {
|
||||
database.purgeLogged();
|
||||
}
|
||||
|
||||
// Register event listeners
|
||||
registerEventListeners(injector);
|
||||
|
||||
// Start Email recall task if needed
|
||||
OnStartupTasks onStartupTasks = injector.newInstance(OnStartupTasks.class);
|
||||
onStartupTasks.scheduleRecallEmailTask();
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates all services.
|
||||
*
|
||||
* @param injector the injector
|
||||
*/
|
||||
void instantiateServices(Injector injector) {
|
||||
database = injector.getSingleton(DataSource.class);
|
||||
bukkitService = injector.getSingleton(BukkitService.class);
|
||||
commandHandler = injector.getSingleton(CommandHandler.class);
|
||||
emailService = injector.getSingleton(EmailService.class);
|
||||
backupService = injector.getSingleton(BackupService.class);
|
||||
|
||||
// Trigger instantiation (class not used elsewhere)
|
||||
injector.getSingleton(BungeeReceiver.class);
|
||||
|
||||
// Trigger construction of API classes; they will keep track of the singleton
|
||||
injector.getSingleton(AuthMeApi.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers all event listeners.
|
||||
*
|
||||
* @param injector the injector
|
||||
*/
|
||||
void registerEventListeners(Injector injector) {
|
||||
// Get the plugin manager instance
|
||||
PluginManager pluginManager = getServer().getPluginManager();
|
||||
|
||||
// Register event listeners
|
||||
pluginManager.registerEvents(injector.getSingleton(PlayerListener.class), this);
|
||||
pluginManager.registerEvents(injector.getSingleton(BlockListener.class), this);
|
||||
pluginManager.registerEvents(injector.getSingleton(EntityListener.class), this);
|
||||
pluginManager.registerEvents(injector.getSingleton(ServerListener.class), this);
|
||||
|
||||
// Try to register 1.9 player listeners
|
||||
if (isClassLoaded("org.bukkit.event.player.PlayerSwapHandItemsEvent")) {
|
||||
pluginManager.registerEvents(injector.getSingleton(PlayerListener19.class), this);
|
||||
}
|
||||
|
||||
// Try to register 1.9 spigot player listeners
|
||||
if (isClassLoaded("org.spigotmc.event.player.PlayerSpawnLocationEvent")) {
|
||||
pluginManager.registerEvents(injector.getSingleton(PlayerListener19Spigot.class), this);
|
||||
}
|
||||
|
||||
// Register listener for 1.11 events if available
|
||||
if (isClassLoaded("org.bukkit.event.entity.EntityAirChangeEvent")) {
|
||||
pluginManager.registerEvents(injector.getSingleton(PlayerListener111.class), this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the server or disables the plugin, as defined in the configuration.
|
||||
*/
|
||||
public void stopOrUnload() {
|
||||
if (settings == null || settings.getProperty(SecuritySettings.STOP_SERVER_ON_PROBLEM)) {
|
||||
getLogger().warning("THE SERVER IS GOING TO SHUT DOWN AS DEFINED IN THE CONFIGURATION!");
|
||||
setEnabled(false);
|
||||
getServer().shutdown();
|
||||
} else {
|
||||
setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
// onDisable is also called when we prematurely abort, so any field may be null
|
||||
OnShutdownPlayerSaver onShutdownPlayerSaver = injector == null
|
||||
? null
|
||||
: injector.createIfHasDependencies(OnShutdownPlayerSaver.class);
|
||||
if (onShutdownPlayerSaver != null) {
|
||||
onShutdownPlayerSaver.saveAllPlayers();
|
||||
}
|
||||
if (settings.getProperty(EmailSettings.SHUTDOWN_MAIL)){
|
||||
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy'年'MM'月'dd'日' HH:mm:ss");
|
||||
Date date = new Date(System.currentTimeMillis());
|
||||
emailService.sendShutDown(settings.getProperty(EmailSettings.SHUTDOWN_MAIL_ADDRESS),dateFormat.format(date));
|
||||
}
|
||||
|
||||
// Do backup on stop if enabled
|
||||
if (backupService != null) {
|
||||
backupService.doBackup(BackupService.BackupCause.STOP);
|
||||
}
|
||||
|
||||
// Wait for tasks and close data source
|
||||
new TaskCloser(this, database).run();
|
||||
|
||||
// Disabled correctly
|
||||
Consumer<String> infoLogMethod = logger == null ? getLogger()::info : logger::info;
|
||||
infoLogMethod.accept("AuthMe " + this.getDescription().getVersion() + " disabled!");
|
||||
ConsoleLogger.closeFileWriter();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Bukkit commands.
|
||||
*
|
||||
* @param sender The command sender (Bukkit).
|
||||
* @param cmd The command (Bukkit).
|
||||
* @param commandLabel The command label (Bukkit).
|
||||
* @param args The command arguments (Bukkit).
|
||||
* @return True if the command was executed, false otherwise.
|
||||
*/
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command cmd,
|
||||
@NotNull String commandLabel, String[] args) {
|
||||
// Make sure the command handler has been initialized
|
||||
if (commandHandler == null) {
|
||||
getLogger().severe("AuthMe command handler is not available");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Handle the command
|
||||
return commandHandler.processCommand(sender, commandLabel, args);
|
||||
}
|
||||
|
||||
private String getServerNameVersionSafe() {
|
||||
try {
|
||||
Server server = getServer();
|
||||
return server.getName() + " v. " + server.getVersion();
|
||||
} catch (Throwable ignore) {
|
||||
return "-";
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,287 +0,0 @@
|
||||
package fr.xephi.authme;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
import fr.xephi.authme.output.LogLevel;
|
||||
import fr.xephi.authme.settings.Settings;
|
||||
import fr.xephi.authme.settings.properties.PluginSettings;
|
||||
import fr.xephi.authme.settings.properties.SecuritySettings;
|
||||
import fr.xephi.authme.util.ExceptionUtils;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.MessageFormat;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeFormatterBuilder;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* AuthMe logger.
|
||||
*/
|
||||
public final class ConsoleLogger {
|
||||
|
||||
private static final String NEW_LINE = System.getProperty("line.separator");
|
||||
/** Formatter which formats dates to something like "[08-16 21:18:46]" for any given LocalDateTime. */
|
||||
private static final DateTimeFormatter DATE_FORMAT = new DateTimeFormatterBuilder()
|
||||
.appendLiteral('[')
|
||||
.appendPattern("MM-dd HH:mm:ss")
|
||||
.appendLiteral(']')
|
||||
.toFormatter();
|
||||
|
||||
// Outside references
|
||||
private static File logFile;
|
||||
private static Logger logger;
|
||||
|
||||
// Shared state
|
||||
private static OutputStreamWriter fileWriter;
|
||||
|
||||
// Individual state
|
||||
private final String name;
|
||||
private LogLevel logLevel = LogLevel.INFO;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param name the name of this logger (the fully qualified class name using it)
|
||||
*/
|
||||
public ConsoleLogger(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
// --------
|
||||
// Configurations
|
||||
// --------
|
||||
|
||||
public static void initialize(Logger logger, File logFile) {
|
||||
ConsoleLogger.logger = logger;
|
||||
ConsoleLogger.logFile = logFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets logging settings which are shared by all logger instances.
|
||||
*
|
||||
* @param settings the settings to read from
|
||||
*/
|
||||
public static void initializeSharedSettings(Settings settings) {
|
||||
boolean useLogging = settings.getProperty(SecuritySettings.USE_LOGGING);
|
||||
if (useLogging) {
|
||||
initializeFileWriter();
|
||||
} else {
|
||||
closeFileWriter();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets logging settings which are individual to all loggers.
|
||||
*
|
||||
* @param settings the settings to read from
|
||||
*/
|
||||
public void initializeSettings(Settings settings) {
|
||||
this.logLevel = settings.getProperty(PluginSettings.LOG_LEVEL);
|
||||
}
|
||||
|
||||
public LogLevel getLogLevel() {
|
||||
return logLevel;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
// --------
|
||||
// Logging methods
|
||||
// --------
|
||||
|
||||
/**
|
||||
* Log a WARN message.
|
||||
*
|
||||
* @param message The message to log
|
||||
*/
|
||||
public void warning(String message) {
|
||||
logger.warning(message);
|
||||
writeLog("[WARN] " + message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a Throwable with the provided message on WARNING level
|
||||
* and save the stack trace to the log file.
|
||||
*
|
||||
* @param message The message to accompany the exception
|
||||
* @param th The Throwable to log
|
||||
*/
|
||||
public void logException(String message, Throwable th) {
|
||||
warning(message + " " + ExceptionUtils.formatException(th));
|
||||
writeLog(Throwables.getStackTraceAsString(th));
|
||||
}
|
||||
|
||||
/**
|
||||
* Log an INFO message.
|
||||
*
|
||||
* @param message The message to log
|
||||
*/
|
||||
public void info(String message) {
|
||||
logger.info(message);
|
||||
writeLog("[INFO] " + message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a FINE message if enabled.
|
||||
* <p>
|
||||
* Implementation note: this logs a message on INFO level because
|
||||
* levels below INFO are disabled by Bukkit/Spigot.
|
||||
*
|
||||
* @param message The message to log
|
||||
*/
|
||||
public void fine(String message) {
|
||||
if (logLevel.includes(LogLevel.FINE)) {
|
||||
logger.info(message);
|
||||
writeLog("[FINE] " + message);
|
||||
}
|
||||
}
|
||||
|
||||
// --------
|
||||
// Debug log methods
|
||||
// --------
|
||||
|
||||
/**
|
||||
* Log a DEBUG message if enabled.
|
||||
* <p>
|
||||
* Implementation note: this logs a message on INFO level and prefixes it with "DEBUG" because
|
||||
* levels below INFO are disabled by Bukkit/Spigot.
|
||||
*
|
||||
* @param message The message to log
|
||||
*/
|
||||
public void debug(String message) {
|
||||
if (logLevel.includes(LogLevel.DEBUG)) {
|
||||
logAndWriteWithDebugPrefix(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log the DEBUG message.
|
||||
*
|
||||
* @param message the message
|
||||
* @param param1 parameter to replace in the message
|
||||
*/
|
||||
// Avoids array creation if DEBUG level is disabled
|
||||
public void debug(String message, Object param1) {
|
||||
if (logLevel.includes(LogLevel.DEBUG)) {
|
||||
debug(message, new Object[]{param1});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log the DEBUG message.
|
||||
*
|
||||
* @param message the message
|
||||
* @param param1 first param to replace in message
|
||||
* @param param2 second param to replace in message
|
||||
*/
|
||||
// Avoids array creation if DEBUG level is disabled
|
||||
public void debug(String message, Object param1, Object param2) {
|
||||
if (logLevel.includes(LogLevel.DEBUG)) {
|
||||
debug(message, new Object[]{param1, param2});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log the DEBUG message.
|
||||
*
|
||||
* @param message the message
|
||||
* @param params the params to replace in the message
|
||||
*/
|
||||
public void debug(String message, Object... params) {
|
||||
if (logLevel.includes(LogLevel.DEBUG)) {
|
||||
logAndWriteWithDebugPrefix(MessageFormat.format(message, params));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log the DEBUG message from the supplier if enabled.
|
||||
*
|
||||
* @param msgSupplier the message supplier
|
||||
*/
|
||||
public void debug(Supplier<String> msgSupplier) {
|
||||
if (logLevel.includes(LogLevel.DEBUG)) {
|
||||
logAndWriteWithDebugPrefix(msgSupplier.get());
|
||||
}
|
||||
}
|
||||
|
||||
private void logAndWriteWithDebugPrefix(String message) {
|
||||
String debugMessage = "[DEBUG] " + message;
|
||||
logger.info(debugMessage);
|
||||
writeLog(debugMessage);
|
||||
}
|
||||
|
||||
// --------
|
||||
// Helpers
|
||||
// --------
|
||||
|
||||
/**
|
||||
* Closes the file writer.
|
||||
*/
|
||||
public static void closeFileWriter() {
|
||||
if (fileWriter != null) {
|
||||
try {
|
||||
fileWriter.flush();
|
||||
} catch (IOException ignored) {
|
||||
} finally {
|
||||
closeSafely(fileWriter);
|
||||
fileWriter = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a message into the log file with a TimeStamp if enabled.
|
||||
*
|
||||
* @param message The message to write to the log
|
||||
*/
|
||||
private static void writeLog(String message) {
|
||||
if (fileWriter != null) {
|
||||
String dateTime = DATE_FORMAT.format(LocalDateTime.now());
|
||||
try {
|
||||
fileWriter.write(dateTime);
|
||||
fileWriter.write(": ");
|
||||
fileWriter.write(message);
|
||||
fileWriter.write(NEW_LINE);
|
||||
fileWriter.flush();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void closeSafely(Closeable closeable) {
|
||||
if (closeable != null) {
|
||||
try {
|
||||
closeable.close();
|
||||
} catch (IOException e) {
|
||||
logger.log(Level.SEVERE, "Failed to close resource", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the {@link #fileWriter} field if it is null, handling any exceptions that might
|
||||
* arise during its creation.
|
||||
*/
|
||||
private static void initializeFileWriter() {
|
||||
if (fileWriter == null) {
|
||||
FileOutputStream fos = null;
|
||||
try {
|
||||
fos = new FileOutputStream(logFile, true);
|
||||
fileWriter = new OutputStreamWriter(fos, StandardCharsets.UTF_8);
|
||||
} catch (Exception e) {
|
||||
closeSafely(fos);
|
||||
logger.log(Level.SEVERE, "Failed to create writer to AuthMe log file", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,370 +0,0 @@
|
||||
package fr.xephi.authme.api.v3;
|
||||
|
||||
import fr.xephi.authme.AuthMe;
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
import fr.xephi.authme.data.auth.PlayerCache;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.process.Management;
|
||||
import fr.xephi.authme.process.register.executors.ApiPasswordRegisterParams;
|
||||
import fr.xephi.authme.process.register.executors.RegistrationMethod;
|
||||
import fr.xephi.authme.security.PasswordSecurity;
|
||||
import fr.xephi.authme.security.crypts.HashedPassword;
|
||||
import fr.xephi.authme.service.GeoIpService;
|
||||
import fr.xephi.authme.service.ValidationService;
|
||||
import fr.xephi.authme.util.PlayerUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* The current API of AuthMe.
|
||||
*
|
||||
* Recommended method of retrieving the AuthMeApi object:
|
||||
* <code>
|
||||
* AuthMeApi authmeApi = AuthMeApi.getInstance();
|
||||
* </code>
|
||||
*/
|
||||
public class AuthMeApi {
|
||||
|
||||
private static AuthMeApi singleton;
|
||||
private final AuthMe plugin;
|
||||
private final DataSource dataSource;
|
||||
private final PasswordSecurity passwordSecurity;
|
||||
private final Management management;
|
||||
private final ValidationService validationService;
|
||||
private final PlayerCache playerCache;
|
||||
private final GeoIpService geoIpService;
|
||||
|
||||
/*
|
||||
* Constructor for AuthMeApi.
|
||||
*/
|
||||
@Inject
|
||||
AuthMeApi(AuthMe plugin, DataSource dataSource, PlayerCache playerCache, PasswordSecurity passwordSecurity,
|
||||
Management management, ValidationService validationService, GeoIpService geoIpService) {
|
||||
this.plugin = plugin;
|
||||
this.dataSource = dataSource;
|
||||
this.passwordSecurity = passwordSecurity;
|
||||
this.management = management;
|
||||
this.validationService = validationService;
|
||||
this.playerCache = playerCache;
|
||||
this.geoIpService = geoIpService;
|
||||
AuthMeApi.singleton = this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the AuthMeApi object for AuthMe.
|
||||
*
|
||||
* @return The AuthMeApi object, or null if the AuthMe plugin is not enabled or not fully initialized yet
|
||||
*/
|
||||
public static AuthMeApi getInstance() {
|
||||
return singleton;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the plugin instance.
|
||||
*
|
||||
* @return The AuthMe instance
|
||||
*/
|
||||
public AuthMe getPlugin() {
|
||||
return plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gather the version number of the plugin.
|
||||
* This can be used to determine whether certain AuthMeApi features are available or not.
|
||||
*
|
||||
* @return Plugin version identifier as a string.
|
||||
*/
|
||||
public String getPluginVersion() {
|
||||
return AuthMe.getPluginVersion();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the given player is authenticated.
|
||||
*
|
||||
* @param player The player to verify
|
||||
* @return true if the player is authenticated
|
||||
*/
|
||||
public boolean isAuthenticated(Player player) {
|
||||
return playerCache.isAuthenticated(player.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the given player is an NPC.
|
||||
*
|
||||
* @param player The player to verify
|
||||
* @return true if the player is an npc
|
||||
*/
|
||||
public boolean isNpc(Player player) {
|
||||
return PlayerUtils.isNpc(player);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the given player is unrestricted. For such players, AuthMe will not require
|
||||
* them to authenticate.
|
||||
*
|
||||
* @param player The player to verify
|
||||
* @return true if the player is unrestricted
|
||||
* @see fr.xephi.authme.settings.properties.RestrictionSettings#UNRESTRICTED_NAMES
|
||||
*/
|
||||
public boolean isUnrestricted(Player player) {
|
||||
return validationService.isUnrestricted(player.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last location of an online player.
|
||||
*
|
||||
* @param player The player to process
|
||||
* @return The location of the player
|
||||
*/
|
||||
public Location getLastLocation(Player player) {
|
||||
PlayerAuth auth = playerCache.getAuth(player.getName());
|
||||
if (auth != null) {
|
||||
return new Location(Bukkit.getWorld(auth.getWorld()),
|
||||
auth.getQuitLocX(), auth.getQuitLocY(), auth.getQuitLocZ(), auth.getYaw(), auth.getPitch());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the AuthMe info of the given player's name, or empty optional if the player doesn't exist.
|
||||
*
|
||||
* @param playerName The player name to look up
|
||||
* @return AuthMe player info, or empty optional if the player doesn't exist
|
||||
*/
|
||||
public Optional<AuthMePlayer> getPlayerInfo(String playerName) {
|
||||
PlayerAuth auth = playerCache.getAuth(playerName);
|
||||
if (auth == null) {
|
||||
auth = dataSource.getAuth(playerName);
|
||||
}
|
||||
return AuthMePlayerImpl.fromPlayerAuth(auth);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last ip address of a player.
|
||||
*
|
||||
* @param playerName The name of the player to process
|
||||
* @return The last ip address of the player
|
||||
*/
|
||||
public String getLastIp(String playerName) {
|
||||
PlayerAuth auth = playerCache.getAuth(playerName);
|
||||
if (auth == null) {
|
||||
auth = dataSource.getAuth(playerName);
|
||||
}
|
||||
if (auth != null) {
|
||||
return auth.getLastIp();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user names by ip.
|
||||
*
|
||||
* @param address The ip address to process
|
||||
* @return The list of user names related to the ip address
|
||||
*/
|
||||
public List<String> getNamesByIp(String address) {
|
||||
return dataSource.getAllAuthsByIp(address);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last (AuthMe) login date of a player.
|
||||
*
|
||||
* @param playerName The name of the player to process
|
||||
* @return The date of the last login, or null if the player doesn't exist or has never logged in
|
||||
* @deprecated Use Java 8's Instant method {@link #getLastLoginTime(String)}
|
||||
*/
|
||||
@Deprecated
|
||||
public Date getLastLogin(String playerName) {
|
||||
Long lastLogin = getLastLoginMillis(playerName);
|
||||
return lastLogin == null ? null : new Date(lastLogin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last (AuthMe) login timestamp of a player.
|
||||
*
|
||||
* @param playerName The name of the player to process
|
||||
*
|
||||
* @return The timestamp of the last login, or null if the player doesn't exist or has never logged in
|
||||
*/
|
||||
public Instant getLastLoginTime(String playerName) {
|
||||
Long lastLogin = getLastLoginMillis(playerName);
|
||||
return lastLogin == null ? null : Instant.ofEpochMilli(lastLogin);
|
||||
}
|
||||
|
||||
private Long getLastLoginMillis(String playerName) {
|
||||
PlayerAuth auth = playerCache.getAuth(playerName);
|
||||
if (auth == null) {
|
||||
auth = dataSource.getAuth(playerName);
|
||||
}
|
||||
if (auth != null) {
|
||||
return auth.getLastLogin();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the player is registered.
|
||||
*
|
||||
* @param playerName The player name to check
|
||||
* @return true if player is registered, false otherwise
|
||||
*/
|
||||
public boolean isRegistered(String playerName) {
|
||||
String player = playerName.toLowerCase(Locale.ROOT);
|
||||
return dataSource.isAuthAvailable(player);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the password for the given player.
|
||||
*
|
||||
* @param playerName The player to check the password for
|
||||
* @param passwordToCheck The password to check
|
||||
* @return true if the password is correct, false otherwise
|
||||
*/
|
||||
public boolean checkPassword(String playerName, String passwordToCheck) {
|
||||
return passwordSecurity.comparePassword(passwordToCheck, playerName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an OFFLINE/ONLINE player with the given password.
|
||||
*
|
||||
* @param playerName The player to register
|
||||
* @param password The password to register the player with
|
||||
*
|
||||
* @return true if the player was registered successfully
|
||||
*/
|
||||
public boolean registerPlayer(String playerName, String password) {
|
||||
String name = playerName.toLowerCase(Locale.ROOT);
|
||||
if (isRegistered(name)) {
|
||||
return false;
|
||||
}
|
||||
HashedPassword result = passwordSecurity.computeHash(password, name);
|
||||
PlayerAuth auth = PlayerAuth.builder()
|
||||
.name(name)
|
||||
.password(result)
|
||||
.realName(playerName)
|
||||
.registrationDate(System.currentTimeMillis())
|
||||
.build();
|
||||
return dataSource.saveAuth(auth);
|
||||
}
|
||||
|
||||
/**
|
||||
* Force a player to login, i.e. the player is logged in without needing his password.
|
||||
*
|
||||
* @param player The player to log in
|
||||
*/
|
||||
public void forceLogin(Player player) {
|
||||
management.forceLogin(player);
|
||||
}
|
||||
|
||||
/**
|
||||
* Force a player to logout.
|
||||
*
|
||||
* @param player The player to log out
|
||||
*/
|
||||
public void forceLogout(Player player) {
|
||||
management.performLogout(player);
|
||||
}
|
||||
|
||||
/**
|
||||
* Force an ONLINE player to register.
|
||||
*
|
||||
* @param player The player to register
|
||||
* @param password The password to use
|
||||
* @param autoLogin Should the player be authenticated automatically after the registration?
|
||||
*/
|
||||
public void forceRegister(Player player, String password, boolean autoLogin) {
|
||||
management.performRegister(RegistrationMethod.API_REGISTRATION,
|
||||
ApiPasswordRegisterParams.of(player, password, autoLogin));
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an ONLINE player with the given password.
|
||||
*
|
||||
* @param player The player to register
|
||||
* @param password The password to use
|
||||
*/
|
||||
public void forceRegister(Player player, String password) {
|
||||
forceRegister(player, password, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister a player from AuthMe.
|
||||
*
|
||||
* @param player The player to unregister
|
||||
*/
|
||||
public void forceUnregister(Player player) {
|
||||
management.performUnregisterByAdmin(null, player.getName(), player);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister a player from AuthMe by name.
|
||||
*
|
||||
* @param name the name of the player (case-insensitive)
|
||||
*/
|
||||
public void forceUnregister(String name) {
|
||||
management.performUnregisterByAdmin(null, name, Bukkit.getPlayer(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Change a user's password
|
||||
*
|
||||
* @param name the user name
|
||||
* @param newPassword the new password
|
||||
*/
|
||||
public void changePassword(String name, String newPassword) {
|
||||
management.performPasswordChangeAsAdmin(null, name, newPassword);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the registered names (lowercase)
|
||||
*
|
||||
* @return registered names
|
||||
*/
|
||||
public List<String> getRegisteredNames() {
|
||||
List<String> registeredNames = new ArrayList<>();
|
||||
dataSource.getAllAuths().forEach(auth -> registeredNames.add(auth.getNickname()));
|
||||
return registeredNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the registered real-names (original case)
|
||||
*
|
||||
* @return registered real-names
|
||||
*/
|
||||
public List<String> getRegisteredRealNames() {
|
||||
List<String> registeredNames = new ArrayList<>();
|
||||
dataSource.getAllAuths().forEach(auth -> registeredNames.add(auth.getRealName()));
|
||||
return registeredNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the country code of the given IP address.
|
||||
*
|
||||
* @param ip textual IP address to lookup.
|
||||
*
|
||||
* @return two-character ISO 3166-1 alpha code for the country.
|
||||
*/
|
||||
public String getCountryCode(String ip) {
|
||||
return geoIpService.getCountryCode(ip);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the country name of the given IP address.
|
||||
*
|
||||
* @param ip textual IP address to lookup.
|
||||
*
|
||||
* @return The name of the country.
|
||||
*/
|
||||
public String getCountryName(String ip) {
|
||||
return geoIpService.getCountryName(ip);
|
||||
}
|
||||
}
|
||||
@ -1,65 +0,0 @@
|
||||
package fr.xephi.authme.api.v3;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Read-only player info exposed in the AuthMe API. The data in this object is copied from the
|
||||
* database and not updated afterwards. As such, it may become outdated if the player data changes
|
||||
* in AuthMe.
|
||||
*
|
||||
* @see AuthMeApi#getPlayerInfo
|
||||
*/
|
||||
public interface AuthMePlayer {
|
||||
|
||||
/**
|
||||
* @return the case-sensitive name of the player, e.g. "thePlayer3030" - never null
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Returns the UUID of the player as given by the server (may be offline UUID or not).
|
||||
* The UUID is not present if AuthMe is configured not to store the UUID or if the data is not
|
||||
* present (e.g. older record).
|
||||
*
|
||||
* @return player uuid, or empty optional if not available
|
||||
*/
|
||||
Optional<UUID> getUuid();
|
||||
|
||||
/**
|
||||
* Returns the email address associated with this player, or an empty optional if not available.
|
||||
*
|
||||
* @return player's email or empty optional
|
||||
*/
|
||||
Optional<String> getEmail();
|
||||
|
||||
/**
|
||||
* @return the registration date of the player's account - never null
|
||||
*/
|
||||
Instant getRegistrationDate();
|
||||
|
||||
/**
|
||||
* Returns the IP address with which the player's account was registered. Returns an empty optional
|
||||
* for older accounts, or if the account was registered by someone else (e.g. by an admin).
|
||||
*
|
||||
* @return the ip address used during the registration of the account, or empty optional
|
||||
*/
|
||||
Optional<String> getRegistrationIpAddress();
|
||||
|
||||
/**
|
||||
* Returns the last login date of the player. An empty optional is returned if the player never logged in.
|
||||
*
|
||||
* @return date the player last logged in successfully, or empty optional if not applicable
|
||||
*/
|
||||
Optional<Instant> getLastLoginDate();
|
||||
|
||||
/**
|
||||
* Returns the IP address the player last logged in with successfully. Returns an empty optional if the
|
||||
* player never logged in.
|
||||
*
|
||||
* @return ip address the player last logged in with successfully, or empty optional if not applicable
|
||||
*/
|
||||
Optional<String> getLastLoginIpAddress();
|
||||
|
||||
}
|
||||
@ -1,93 +0,0 @@
|
||||
package fr.xephi.authme.api.v3;
|
||||
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Implementation of {@link AuthMePlayer}. This implementation is not part of the API and
|
||||
* may have breaking changes in subsequent releases.
|
||||
*/
|
||||
class AuthMePlayerImpl implements AuthMePlayer {
|
||||
|
||||
private String name;
|
||||
private UUID uuid;
|
||||
private String email;
|
||||
|
||||
private Instant registrationDate;
|
||||
private String registrationIpAddress;
|
||||
|
||||
private Instant lastLoginDate;
|
||||
private String lastLoginIpAddress;
|
||||
|
||||
AuthMePlayerImpl() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps the given player auth to an AuthMePlayer instance. Returns an empty optional if
|
||||
* the player auth is null.
|
||||
*
|
||||
* @param playerAuth the player auth or null
|
||||
* @return the mapped player auth, or empty optional if the argument was null
|
||||
*/
|
||||
static Optional<AuthMePlayer> fromPlayerAuth(PlayerAuth playerAuth) {
|
||||
if (playerAuth == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
AuthMePlayerImpl authMeUser = new AuthMePlayerImpl();
|
||||
authMeUser.name = playerAuth.getRealName();
|
||||
authMeUser.uuid = playerAuth.getUuid();
|
||||
authMeUser.email = nullIfDefault(playerAuth.getEmail(), PlayerAuth.DB_EMAIL_DEFAULT);
|
||||
Long lastLoginMillis = nullIfDefault(playerAuth.getLastLogin(), PlayerAuth.DB_LAST_LOGIN_DEFAULT);
|
||||
authMeUser.registrationDate = toInstant(playerAuth.getRegistrationDate());
|
||||
authMeUser.registrationIpAddress = playerAuth.getRegistrationIp();
|
||||
authMeUser.lastLoginDate = toInstant(lastLoginMillis);
|
||||
authMeUser.lastLoginIpAddress = nullIfDefault(playerAuth.getLastIp(), PlayerAuth.DB_LAST_IP_DEFAULT);
|
||||
return Optional.of(authMeUser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Optional<UUID> getUuid() {
|
||||
return Optional.ofNullable(uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<String> getEmail() {
|
||||
return Optional.ofNullable(email);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Instant getRegistrationDate() {
|
||||
return registrationDate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<String> getRegistrationIpAddress() {
|
||||
return Optional.ofNullable(registrationIpAddress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Instant> getLastLoginDate() {
|
||||
return Optional.ofNullable( lastLoginDate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<String> getLastLoginIpAddress() {
|
||||
return Optional.ofNullable(lastLoginIpAddress);
|
||||
}
|
||||
|
||||
private static Instant toInstant(Long epochMillis) {
|
||||
return epochMillis == null ? null : Instant.ofEpochMilli(epochMillis);
|
||||
}
|
||||
|
||||
private static <T> T nullIfDefault(T value, T defaultValue) {
|
||||
return defaultValue.equals(value) ? null : value;
|
||||
}
|
||||
}
|
||||
@ -1,61 +0,0 @@
|
||||
package fr.xephi.authme.command;
|
||||
|
||||
/**
|
||||
* Wrapper for the description of a command argument.
|
||||
*/
|
||||
public class CommandArgumentDescription {
|
||||
|
||||
/**
|
||||
* Argument name (one-word description of the argument).
|
||||
*/
|
||||
private final String name;
|
||||
/**
|
||||
* Argument description.
|
||||
*/
|
||||
private final String description;
|
||||
/**
|
||||
* Defines whether the argument is optional.
|
||||
*/
|
||||
private final boolean isOptional;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param name The argument name.
|
||||
* @param description The argument description.
|
||||
* @param isOptional True if the argument is optional, false otherwise.
|
||||
*/
|
||||
public CommandArgumentDescription(String name, String description, boolean isOptional) {
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.isOptional = isOptional;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the argument name.
|
||||
*
|
||||
* @return Argument name.
|
||||
*/
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the argument description.
|
||||
*
|
||||
* @return Argument description.
|
||||
*/
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the argument is optional.
|
||||
*
|
||||
* @return True if the argument is optional, false otherwise.
|
||||
*/
|
||||
public boolean isOptional() {
|
||||
return isOptional;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,294 +0,0 @@
|
||||
package fr.xephi.authme.command;
|
||||
|
||||
import fr.xephi.authme.permission.PermissionNode;
|
||||
import fr.xephi.authme.util.StringUtils;
|
||||
import fr.xephi.authme.util.Utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
/**
|
||||
* Command description – defines which labels ("names") will lead to a command and points to the
|
||||
* {@link ExecutableCommand} implementation that executes the logic of the command.
|
||||
* <p>
|
||||
* CommandDescription instances are built hierarchically: they have one parent, or {@code null} for base commands
|
||||
* (main commands such as {@code /authme}), and may have multiple children extending the mapping of the parent: e.g. if
|
||||
* {@code /authme} has a child whose label is {@code "register"}, then {@code /authme register} is the command that
|
||||
* the child defines.
|
||||
*/
|
||||
@SuppressWarnings("checkstyle:FinalClass") // Justification: class is mocked in multiple tests
|
||||
public class CommandDescription {
|
||||
|
||||
/**
|
||||
* Defines the labels to execute the command. For example, if labels are "register" and "r" and the parent is
|
||||
* the command for "/authme", then both "/authme register" and "/authme r" will be handled by this command.
|
||||
*/
|
||||
private List<String> labels;
|
||||
/**
|
||||
* Short description of the command.
|
||||
*/
|
||||
private String description;
|
||||
/**
|
||||
* Detailed description of what the command does.
|
||||
*/
|
||||
private String detailedDescription;
|
||||
/**
|
||||
* The class implementing the command described by this object.
|
||||
*/
|
||||
private Class<? extends ExecutableCommand> executableCommand;
|
||||
/**
|
||||
* The parent command.
|
||||
*/
|
||||
private CommandDescription parent;
|
||||
/**
|
||||
* The child commands that extend this command.
|
||||
*/
|
||||
private List<CommandDescription> children = new ArrayList<>();
|
||||
/**
|
||||
* The arguments the command takes.
|
||||
*/
|
||||
private List<CommandArgumentDescription> arguments;
|
||||
/**
|
||||
* Permission node required to execute this command.
|
||||
*/
|
||||
private PermissionNode permission;
|
||||
|
||||
/**
|
||||
* Private constructor.
|
||||
* <p>
|
||||
* Note for developers: Instances should be created with {@link CommandBuilder#register()} to be properly
|
||||
* registered in the command tree.
|
||||
*
|
||||
* @param labels command labels
|
||||
* @param description description of the command
|
||||
* @param detailedDescription detailed command description
|
||||
* @param executableCommand class of the command implementation
|
||||
* @param parent parent command
|
||||
* @param arguments command arguments
|
||||
* @param permission permission node required to execute this command
|
||||
*/
|
||||
private CommandDescription(List<String> labels, String description, String detailedDescription,
|
||||
Class<? extends ExecutableCommand> executableCommand, CommandDescription parent,
|
||||
List<CommandArgumentDescription> arguments, PermissionNode permission) {
|
||||
this.labels = labels;
|
||||
this.description = description;
|
||||
this.detailedDescription = detailedDescription;
|
||||
this.executableCommand = executableCommand;
|
||||
this.parent = parent;
|
||||
this.arguments = arguments;
|
||||
this.permission = permission;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all relative labels of this command. For example, if this object describes {@code /authme register} and
|
||||
* {@code /authme r}, then it will return a list with {@code register} and {@code r}. The parent label
|
||||
* {@code authme} is not returned.
|
||||
*
|
||||
* @return All labels of the command description.
|
||||
*/
|
||||
public List<String> getLabels() {
|
||||
return labels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether this command description has the given label.
|
||||
*
|
||||
* @param commandLabel The label to check for.
|
||||
*
|
||||
* @return {@code true} if this command contains the given label, {@code false} otherwise.
|
||||
*/
|
||||
public boolean hasLabel(String commandLabel) {
|
||||
for (String label : labels) {
|
||||
if (label.equalsIgnoreCase(commandLabel)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link ExecutableCommand} class implementing this command.
|
||||
*
|
||||
* @return The executable command class
|
||||
*/
|
||||
public Class<? extends ExecutableCommand> getExecutableCommand() {
|
||||
return executableCommand;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the parent.
|
||||
*
|
||||
* @return The parent command, or null for base commands
|
||||
*/
|
||||
public CommandDescription getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of labels necessary to get to this command. This corresponds to the number of parents + 1.
|
||||
*
|
||||
* @return The number of labels, e.g. for "/authme abc def" the label count is 3
|
||||
*/
|
||||
public int getLabelCount() {
|
||||
if (parent == null) {
|
||||
return 1;
|
||||
}
|
||||
return parent.getLabelCount() + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all command children.
|
||||
*
|
||||
* @return Command children.
|
||||
*/
|
||||
public List<CommandDescription> getChildren() {
|
||||
return children;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all arguments the command takes.
|
||||
*
|
||||
* @return Command arguments.
|
||||
*/
|
||||
public List<CommandArgumentDescription> getArguments() {
|
||||
return arguments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a short description of the command.
|
||||
*
|
||||
* @return Command description.
|
||||
*/
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a detailed description of the command.
|
||||
*
|
||||
* @return Detailed description.
|
||||
*/
|
||||
public String getDetailedDescription() {
|
||||
return detailedDescription;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the permission node required to execute the command.
|
||||
*
|
||||
* @return The permission node, or null if none are required to execute the command.
|
||||
*/
|
||||
public PermissionNode getPermission() {
|
||||
return permission;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a builder instance to create a new command description.
|
||||
*
|
||||
* @return The builder
|
||||
*/
|
||||
public static CommandBuilder builder() {
|
||||
return new CommandBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for initializing CommandDescription objects.
|
||||
*/
|
||||
public static final class CommandBuilder {
|
||||
private List<String> labels;
|
||||
private String description;
|
||||
private String detailedDescription;
|
||||
private Class<? extends ExecutableCommand> executableCommand;
|
||||
private CommandDescription parent;
|
||||
private List<CommandArgumentDescription> arguments = new ArrayList<>();
|
||||
private PermissionNode permission;
|
||||
|
||||
/**
|
||||
* Build a CommandDescription and register it onto the parent if available.
|
||||
*
|
||||
* @return The generated CommandDescription object
|
||||
*/
|
||||
public CommandDescription register() {
|
||||
CommandDescription command = build();
|
||||
|
||||
if (command.parent != null) {
|
||||
command.parent.children.add(command);
|
||||
}
|
||||
return command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a CommandDescription (without registering it on the parent).
|
||||
*
|
||||
* @return The generated CommandDescription object
|
||||
*/
|
||||
public CommandDescription build() {
|
||||
checkArgument(!Utils.isCollectionEmpty(labels), "Labels may not be empty");
|
||||
checkArgument(!StringUtils.isBlank(description), "Description may not be empty");
|
||||
checkArgument(!StringUtils.isBlank(detailedDescription), "Detailed description may not be empty");
|
||||
checkArgument(executableCommand != null, "Executable command must be set");
|
||||
// parents and permissions may be null; arguments may be empty
|
||||
|
||||
return new CommandDescription(labels, description, detailedDescription, executableCommand,
|
||||
parent, arguments, permission);
|
||||
}
|
||||
|
||||
public CommandBuilder labels(List<String> labels) {
|
||||
this.labels = labels;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandBuilder labels(String... labels) {
|
||||
return labels(asList(labels));
|
||||
}
|
||||
|
||||
public CommandBuilder description(String description) {
|
||||
this.description = description;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandBuilder detailedDescription(String detailedDescription) {
|
||||
this.detailedDescription = detailedDescription;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandBuilder executableCommand(Class<? extends ExecutableCommand> executableCommand) {
|
||||
this.executableCommand = executableCommand;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandBuilder parent(CommandDescription parent) {
|
||||
this.parent = parent;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an argument that the command description requires. This method can be called multiples times to add
|
||||
* multiple arguments.
|
||||
*
|
||||
* @param label The label of the argument (single word name of the argument)
|
||||
* @param description The description of the argument
|
||||
* @param isOptional True if the argument is optional, false if it is mandatory
|
||||
*
|
||||
* @return The builder
|
||||
*/
|
||||
public CommandBuilder withArgument(String label, String description, boolean isOptional) {
|
||||
arguments.add(new CommandArgumentDescription(label, description, isOptional));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a permission node that a user must have to execute the command.
|
||||
*
|
||||
* @param permission The PermissionNode to add
|
||||
* @return The builder
|
||||
*/
|
||||
public CommandBuilder permission(PermissionNode permission) {
|
||||
this.permission = permission;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,186 +0,0 @@
|
||||
package fr.xephi.authme.command;
|
||||
|
||||
import ch.jalu.injector.factory.Factory;
|
||||
import fr.xephi.authme.AuthMe;
|
||||
import fr.xephi.authme.command.help.HelpProvider;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.message.Messages;
|
||||
import fr.xephi.authme.permission.PermissionsManager;
|
||||
import fr.xephi.authme.util.StringUtils;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* The AuthMe command handler, responsible for invoking the correct {@link ExecutableCommand} based on incoming
|
||||
* command labels or for displaying a help message for unknown command labels.
|
||||
*/
|
||||
public class CommandHandler {
|
||||
|
||||
/**
|
||||
* The threshold for suggesting a similar command. If the difference is below this value, we will
|
||||
* ask the player whether he meant the similar command.
|
||||
*/
|
||||
private static final double SUGGEST_COMMAND_THRESHOLD = 0.75;
|
||||
|
||||
private final CommandMapper commandMapper;
|
||||
private final PermissionsManager permissionsManager;
|
||||
private final Messages messages;
|
||||
private final HelpProvider helpProvider;
|
||||
|
||||
/**
|
||||
* Map with ExecutableCommand children. The key is the type of the value.
|
||||
*/
|
||||
private Map<Class<? extends ExecutableCommand>, ExecutableCommand> commands = new HashMap<>();
|
||||
|
||||
@Inject
|
||||
CommandHandler(Factory<ExecutableCommand> commandFactory, CommandMapper commandMapper,
|
||||
PermissionsManager permissionsManager, Messages messages, HelpProvider helpProvider) {
|
||||
this.commandMapper = commandMapper;
|
||||
this.permissionsManager = permissionsManager;
|
||||
this.messages = messages;
|
||||
this.helpProvider = helpProvider;
|
||||
initializeCommands(commandFactory, commandMapper.getCommandClasses());
|
||||
}
|
||||
|
||||
/**
|
||||
* Map a command that was invoked to the proper {@link CommandDescription} or return a useful error
|
||||
* message upon failure.
|
||||
*
|
||||
* @param sender The command sender.
|
||||
* @param bukkitCommandLabel The command label (Bukkit).
|
||||
* @param bukkitArgs The command arguments (Bukkit).
|
||||
*
|
||||
* @return True if the command was executed, false otherwise.
|
||||
*/
|
||||
public boolean processCommand(CommandSender sender, String bukkitCommandLabel, String[] bukkitArgs) {
|
||||
// Add the Bukkit command label to the front so we get a list like [authme, register, bobby, mysecret]
|
||||
List<String> parts = skipEmptyArguments(bukkitArgs);
|
||||
parts.add(0, bukkitCommandLabel);
|
||||
|
||||
FoundCommandResult result = commandMapper.mapPartsToCommand(sender, parts);
|
||||
handleCommandResult(sender, result);
|
||||
return !FoundResultStatus.MISSING_BASE_COMMAND.equals(result.getResultStatus());
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the given {@link FoundCommandResult} for the provided command sender.
|
||||
*
|
||||
* @param sender the command sender who executed the command
|
||||
* @param result the command mapping result
|
||||
*/
|
||||
private void handleCommandResult(CommandSender sender, FoundCommandResult result) {
|
||||
switch (result.getResultStatus()) {
|
||||
case SUCCESS:
|
||||
executeCommand(sender, result);
|
||||
break;
|
||||
case MISSING_BASE_COMMAND:
|
||||
sender.sendMessage(ChatColor.DARK_RED + "Failed to parse " + AuthMe.getPluginName() + " command!");
|
||||
break;
|
||||
case INCORRECT_ARGUMENTS:
|
||||
sendImproperArgumentsMessage(sender, result);
|
||||
break;
|
||||
case UNKNOWN_LABEL:
|
||||
sendUnknownCommandMessage(sender, result);
|
||||
break;
|
||||
case NO_PERMISSION:
|
||||
messages.send(sender, MessageKey.NO_PERMISSION);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Unknown result status '" + result.getResultStatus() + "'");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize all required ExecutableCommand objects.
|
||||
*
|
||||
* @param commandFactory factory to create command objects
|
||||
* @param commandClasses the classes to instantiate
|
||||
*/
|
||||
private void initializeCommands(Factory<ExecutableCommand> commandFactory,
|
||||
Set<Class<? extends ExecutableCommand>> commandClasses) {
|
||||
for (Class<? extends ExecutableCommand> clazz : commandClasses) {
|
||||
commands.put(clazz, commandFactory.newInstance(clazz));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the command for the given command sender.
|
||||
*
|
||||
* @param sender The sender which initiated the command
|
||||
* @param result The mapped result
|
||||
*/
|
||||
private void executeCommand(CommandSender sender, FoundCommandResult result) {
|
||||
ExecutableCommand executableCommand = commands.get(result.getCommandDescription().getExecutableCommand());
|
||||
List<String> arguments = result.getArguments();
|
||||
executableCommand.executeCommand(sender, arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip all entries of the given array that are simply whitespace.
|
||||
*
|
||||
* @param args The array to process
|
||||
* @return List of the items that are not empty
|
||||
*/
|
||||
private static List<String> skipEmptyArguments(String[] args) {
|
||||
List<String> cleanArguments = new ArrayList<>();
|
||||
for (String argument : args) {
|
||||
if (!StringUtils.isBlank(argument)) {
|
||||
cleanArguments.add(argument);
|
||||
}
|
||||
}
|
||||
return cleanArguments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show an "unknown command" message to the user and suggest an existing command if its similarity is within
|
||||
* the defined threshold.
|
||||
*
|
||||
* @param sender The command sender
|
||||
* @param result The command that was found during the mapping process
|
||||
*/
|
||||
private static void sendUnknownCommandMessage(CommandSender sender, FoundCommandResult result) {
|
||||
sender.sendMessage(ChatColor.DARK_RED + "Unknown command!");
|
||||
|
||||
// Show a command suggestion if available and the difference isn't too big
|
||||
if (result.getDifference() <= SUGGEST_COMMAND_THRESHOLD && result.getCommandDescription() != null) {
|
||||
sender.sendMessage(ChatColor.YELLOW + "Did you mean " + ChatColor.GOLD
|
||||
+ CommandUtils.constructCommandPath(result.getCommandDescription()) + ChatColor.YELLOW + "?");
|
||||
}
|
||||
|
||||
sender.sendMessage(ChatColor.YELLOW + "Use the command " + ChatColor.GOLD + "/" + result.getLabels().get(0)
|
||||
+ " help" + ChatColor.YELLOW + " to view help.");
|
||||
}
|
||||
|
||||
private void sendImproperArgumentsMessage(CommandSender sender, FoundCommandResult result) {
|
||||
CommandDescription command = result.getCommandDescription();
|
||||
if (!permissionsManager.hasPermission(sender, command.getPermission())) {
|
||||
messages.send(sender, MessageKey.NO_PERMISSION);
|
||||
return;
|
||||
}
|
||||
|
||||
ExecutableCommand executableCommand = commands.get(command.getExecutableCommand());
|
||||
MessageKey usageMessage = executableCommand.getArgumentsMismatchMessage();
|
||||
if (usageMessage == null) {
|
||||
showHelpForCommand(sender, result);
|
||||
} else {
|
||||
messages.send(sender, usageMessage);
|
||||
}
|
||||
}
|
||||
|
||||
private void showHelpForCommand(CommandSender sender, FoundCommandResult result) {
|
||||
sender.sendMessage(ChatColor.DARK_RED + "Incorrect command arguments!");
|
||||
helpProvider.outputHelp(sender, result, HelpProvider.SHOW_ARGUMENTS);
|
||||
|
||||
List<String> labels = result.getLabels();
|
||||
String childLabel = labels.size() >= 2 ? labels.get(1) : "";
|
||||
sender.sendMessage(ChatColor.GOLD + "Detailed help: " + ChatColor.WHITE
|
||||
+ "/" + labels.get(0) + " help " + childLabel);
|
||||
}
|
||||
}
|
||||
@ -1,663 +0,0 @@
|
||||
package fr.xephi.authme.command;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import fr.xephi.authme.command.executable.HelpCommand;
|
||||
import fr.xephi.authme.command.executable.authme.AccountsCommand;
|
||||
import fr.xephi.authme.command.executable.authme.AuthMeCommand;
|
||||
import fr.xephi.authme.command.executable.authme.BackupCommand;
|
||||
import fr.xephi.authme.command.executable.authme.ChangePasswordAdminCommand;
|
||||
import fr.xephi.authme.command.executable.authme.ConverterCommand;
|
||||
import fr.xephi.authme.command.executable.authme.FirstSpawnCommand;
|
||||
import fr.xephi.authme.command.executable.authme.ForceLoginCommand;
|
||||
import fr.xephi.authme.command.executable.authme.GetEmailCommand;
|
||||
import fr.xephi.authme.command.executable.authme.GetIpCommand;
|
||||
import fr.xephi.authme.command.executable.authme.LastLoginCommand;
|
||||
import fr.xephi.authme.command.executable.authme.PurgeBannedPlayersCommand;
|
||||
import fr.xephi.authme.command.executable.authme.PurgeCommand;
|
||||
import fr.xephi.authme.command.executable.authme.PurgeLastPositionCommand;
|
||||
import fr.xephi.authme.command.executable.authme.PurgePlayerCommand;
|
||||
import fr.xephi.authme.command.executable.authme.RecentPlayersCommand;
|
||||
import fr.xephi.authme.command.executable.authme.RegisterAdminCommand;
|
||||
import fr.xephi.authme.command.executable.authme.ReloadCommand;
|
||||
import fr.xephi.authme.command.executable.authme.SetEmailCommand;
|
||||
import fr.xephi.authme.command.executable.authme.SetFirstSpawnCommand;
|
||||
import fr.xephi.authme.command.executable.authme.SetSpawnCommand;
|
||||
import fr.xephi.authme.command.executable.authme.SpawnCommand;
|
||||
import fr.xephi.authme.command.executable.authme.SwitchAntiBotCommand;
|
||||
import fr.xephi.authme.command.executable.authme.TotpDisableAdminCommand;
|
||||
import fr.xephi.authme.command.executable.authme.TotpViewStatusCommand;
|
||||
import fr.xephi.authme.command.executable.authme.UnregisterAdminCommand;
|
||||
import fr.xephi.authme.command.executable.authme.UpdateHelpMessagesCommand;
|
||||
import fr.xephi.authme.command.executable.authme.VersionCommand;
|
||||
import fr.xephi.authme.command.executable.authme.WhitelistCommand;
|
||||
import fr.xephi.authme.command.executable.authme.debug.DebugCommand;
|
||||
import fr.xephi.authme.command.executable.captcha.CaptchaCommand;
|
||||
import fr.xephi.authme.command.executable.changepassword.ChangePasswordCommand;
|
||||
import fr.xephi.authme.command.executable.email.AddEmailCommand;
|
||||
import fr.xephi.authme.command.executable.email.ChangeEmailCommand;
|
||||
import fr.xephi.authme.command.executable.email.EmailBaseCommand;
|
||||
import fr.xephi.authme.command.executable.email.EmailSetPasswordCommand;
|
||||
import fr.xephi.authme.command.executable.email.ProcessCodeCommand;
|
||||
import fr.xephi.authme.command.executable.email.RecoverEmailCommand;
|
||||
import fr.xephi.authme.command.executable.email.ShowEmailCommand;
|
||||
import fr.xephi.authme.command.executable.login.LoginCommand;
|
||||
import fr.xephi.authme.command.executable.logout.LogoutCommand;
|
||||
import fr.xephi.authme.command.executable.register.RegisterCommand;
|
||||
import fr.xephi.authme.command.executable.totp.AddTotpCommand;
|
||||
import fr.xephi.authme.command.executable.totp.ConfirmTotpCommand;
|
||||
import fr.xephi.authme.command.executable.totp.RemoveTotpCommand;
|
||||
import fr.xephi.authme.command.executable.totp.TotpBaseCommand;
|
||||
import fr.xephi.authme.command.executable.totp.TotpCodeCommand;
|
||||
import fr.xephi.authme.command.executable.unregister.UnregisterCommand;
|
||||
import fr.xephi.authme.command.executable.verification.VerificationCommand;
|
||||
import fr.xephi.authme.permission.AdminPermission;
|
||||
import fr.xephi.authme.permission.DebugSectionPermissions;
|
||||
import fr.xephi.authme.permission.PlayerPermission;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Initializes all available AuthMe commands.
|
||||
*/
|
||||
public class CommandInitializer {
|
||||
|
||||
private static final boolean OPTIONAL = true;
|
||||
private static final boolean MANDATORY = false;
|
||||
|
||||
private List<CommandDescription> commands;
|
||||
|
||||
public CommandInitializer() {
|
||||
buildCommands();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the description of all AuthMe commands.
|
||||
*
|
||||
* @return the command descriptions
|
||||
*/
|
||||
public List<CommandDescription> getCommands() {
|
||||
return commands;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the command description objects for all available AuthMe commands.
|
||||
*/
|
||||
private void buildCommands() {
|
||||
// Register /authme and /email commands
|
||||
CommandDescription authMeBase = buildAuthMeBaseCommand();
|
||||
CommandDescription emailBase = buildEmailBaseCommand();
|
||||
|
||||
// Register the base login command
|
||||
CommandDescription loginBase = CommandDescription.builder()
|
||||
.parent(null)
|
||||
.labels("login", "l", "log")
|
||||
.description("Login command")
|
||||
.detailedDescription("Command to log in using AuthMeReloaded.")
|
||||
.withArgument("password", "Login password", MANDATORY)
|
||||
.permission(PlayerPermission.LOGIN)
|
||||
.executableCommand(LoginCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the base logout command
|
||||
CommandDescription logoutBase = CommandDescription.builder()
|
||||
.parent(null)
|
||||
.labels("logout")
|
||||
.description("Logout command")
|
||||
.detailedDescription("Command to logout using AuthMeReloaded.")
|
||||
.permission(PlayerPermission.LOGOUT)
|
||||
.executableCommand(LogoutCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the base register command
|
||||
CommandDescription registerBase = CommandDescription.builder()
|
||||
.parent(null)
|
||||
.labels("register", "reg")
|
||||
.description("Register an account")
|
||||
.detailedDescription("Command to register using AuthMeReloaded.")
|
||||
.withArgument("password", "Password", OPTIONAL)
|
||||
.withArgument("verifyPassword", "Verify password", OPTIONAL)
|
||||
.permission(PlayerPermission.REGISTER)
|
||||
.executableCommand(RegisterCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the base unregister command
|
||||
CommandDescription unregisterBase = CommandDescription.builder()
|
||||
.parent(null)
|
||||
.labels("unregister", "unreg")
|
||||
.description("Unregister an account")
|
||||
.detailedDescription("Command to unregister using AuthMeReloaded.")
|
||||
.withArgument("password", "Password", MANDATORY)
|
||||
.permission(PlayerPermission.UNREGISTER)
|
||||
.executableCommand(UnregisterCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the base changepassword command
|
||||
CommandDescription changePasswordBase = CommandDescription.builder()
|
||||
.parent(null)
|
||||
.labels("changepassword", "changepass", "cp")
|
||||
.description("Change password of an account")
|
||||
.detailedDescription("Command to change your password using AuthMeReloaded.")
|
||||
.withArgument("oldPassword", "Old password", MANDATORY)
|
||||
.withArgument("newPassword", "New password", MANDATORY)
|
||||
.permission(PlayerPermission.CHANGE_PASSWORD)
|
||||
.executableCommand(ChangePasswordCommand.class)
|
||||
.register();
|
||||
|
||||
// Create totp base command
|
||||
CommandDescription totpBase = buildTotpBaseCommand();
|
||||
|
||||
// Register the base captcha command
|
||||
CommandDescription captchaBase = CommandDescription.builder()
|
||||
.parent(null)
|
||||
.labels("captcha")
|
||||
.description("Captcha command")
|
||||
.detailedDescription("Captcha command for AuthMeReloaded.")
|
||||
.withArgument("captcha", "The Captcha", MANDATORY)
|
||||
.permission(PlayerPermission.CAPTCHA)
|
||||
.executableCommand(CaptchaCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the base verification code command
|
||||
CommandDescription verificationBase = CommandDescription.builder()
|
||||
.parent(null)
|
||||
.labels("verification")
|
||||
.description("Verification command")
|
||||
.detailedDescription("Command to complete the verification process for AuthMeReloaded.")
|
||||
.withArgument("code", "The code", MANDATORY)
|
||||
.permission(PlayerPermission.VERIFICATION_CODE)
|
||||
.executableCommand(VerificationCommand.class)
|
||||
.register();
|
||||
|
||||
List<CommandDescription> baseCommands = ImmutableList.of(authMeBase, emailBase, loginBase, logoutBase,
|
||||
registerBase, unregisterBase, changePasswordBase, totpBase, captchaBase, verificationBase);
|
||||
|
||||
setHelpOnAllBases(baseCommands);
|
||||
commands = baseCommands;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a command description object for {@code /authme} including its children.
|
||||
*
|
||||
* @return the authme base command description
|
||||
*/
|
||||
private CommandDescription buildAuthMeBaseCommand() {
|
||||
// Register the base AuthMe Reloaded command
|
||||
CommandDescription authmeBase = CommandDescription.builder()
|
||||
.labels("authme")
|
||||
.description("AuthMe op commands")
|
||||
.detailedDescription("The main AuthMeReloaded command. The root for all admin commands.")
|
||||
.executableCommand(AuthMeCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the register command
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("register", "reg", "r")
|
||||
.description("Register a player")
|
||||
.detailedDescription("Register the specified player with the specified password.")
|
||||
.withArgument("player", "Player name", MANDATORY)
|
||||
.withArgument("password", "Password", MANDATORY)
|
||||
.permission(AdminPermission.REGISTER)
|
||||
.executableCommand(RegisterAdminCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the unregister command
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("unregister", "unreg", "unr")
|
||||
.description("Unregister a player")
|
||||
.detailedDescription("Unregister the specified player.")
|
||||
.withArgument("player", "Player name", MANDATORY)
|
||||
.permission(AdminPermission.UNREGISTER)
|
||||
.executableCommand(UnregisterAdminCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the forcelogin command
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("forcelogin", "login")
|
||||
.description("Enforce login player")
|
||||
.detailedDescription("Enforce the specified player to login.")
|
||||
.withArgument("player", "Online player name", OPTIONAL)
|
||||
.permission(AdminPermission.FORCE_LOGIN)
|
||||
.executableCommand(ForceLoginCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the changepassword command
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("password", "changepassword", "changepass", "cp")
|
||||
.description("Change a player's password")
|
||||
.detailedDescription("Change the password of a player.")
|
||||
.withArgument("player", "Player name", MANDATORY)
|
||||
.withArgument("pwd", "New password", MANDATORY)
|
||||
.permission(AdminPermission.CHANGE_PASSWORD)
|
||||
.executableCommand(ChangePasswordAdminCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the last login command
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("lastlogin", "ll")
|
||||
.description("Player's last login")
|
||||
.detailedDescription("View the date of the specified players last login.")
|
||||
.withArgument("player", "Player name", OPTIONAL)
|
||||
.permission(AdminPermission.LAST_LOGIN)
|
||||
.executableCommand(LastLoginCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the accounts command
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("accounts", "account")
|
||||
.description("Display player accounts")
|
||||
.detailedDescription("Display all accounts of a player by his player name or IP.")
|
||||
.withArgument("player", "Player name or IP", OPTIONAL)
|
||||
.permission(AdminPermission.ACCOUNTS)
|
||||
.executableCommand(AccountsCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the getemail command
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("email", "mail", "getemail", "getmail")
|
||||
.description("Display player's email")
|
||||
.detailedDescription("Display the email address of the specified player if set.")
|
||||
.withArgument("player", "Player name", OPTIONAL)
|
||||
.permission(AdminPermission.GET_EMAIL)
|
||||
.executableCommand(GetEmailCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the setemail command
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("setemail", "setmail", "chgemail", "chgmail")
|
||||
.description("Change player's email")
|
||||
.detailedDescription("Change the email address of the specified player.")
|
||||
.withArgument("player", "Player name", MANDATORY)
|
||||
.withArgument("email", "Player email", MANDATORY)
|
||||
.permission(AdminPermission.CHANGE_EMAIL)
|
||||
.executableCommand(SetEmailCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the getip command
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("getip", "ip")
|
||||
.description("Get player's IP")
|
||||
.detailedDescription("Get the IP address of the specified online player.")
|
||||
.withArgument("player", "Player name", MANDATORY)
|
||||
.permission(AdminPermission.GET_IP)
|
||||
.executableCommand(GetIpCommand.class)
|
||||
.register();
|
||||
|
||||
// Register totp command
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("totp", "2fa")
|
||||
.description("See if a player has enabled TOTP")
|
||||
.detailedDescription("Returns whether the specified player has enabled two-factor authentication.")
|
||||
.withArgument("player", "Player name", MANDATORY)
|
||||
.permission(AdminPermission.VIEW_TOTP_STATUS)
|
||||
.executableCommand(TotpViewStatusCommand.class)
|
||||
.register();
|
||||
|
||||
// Register disable totp command
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("disabletotp", "disable2fa", "deletetotp", "delete2fa")
|
||||
.description("Delete TOTP token of a player")
|
||||
.detailedDescription("Disable two-factor authentication for a player.")
|
||||
.withArgument("player", "Player name", MANDATORY)
|
||||
.permission(AdminPermission.DISABLE_TOTP)
|
||||
.executableCommand(TotpDisableAdminCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the spawn command
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("spawn", "home")
|
||||
.description("Teleport to spawn")
|
||||
.detailedDescription("Teleport to the spawn.")
|
||||
.permission(AdminPermission.SPAWN)
|
||||
.executableCommand(SpawnCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the setspawn command
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("setspawn", "chgspawn")
|
||||
.description("Change the spawn")
|
||||
.detailedDescription("Change the player's spawn to your current position.")
|
||||
.permission(AdminPermission.SET_SPAWN)
|
||||
.executableCommand(SetSpawnCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the firstspawn command
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("firstspawn", "firsthome")
|
||||
.description("Teleport to first spawn")
|
||||
.detailedDescription("Teleport to the first spawn.")
|
||||
.permission(AdminPermission.FIRST_SPAWN)
|
||||
.executableCommand(FirstSpawnCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the setfirstspawn command
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("setfirstspawn", "chgfirstspawn")
|
||||
.description("Change the first spawn")
|
||||
.detailedDescription("Change the first player's spawn to your current position.")
|
||||
.permission(AdminPermission.SET_FIRST_SPAWN)
|
||||
.executableCommand(SetFirstSpawnCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the purge command
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("purge", "delete")
|
||||
.description("Purge old data")
|
||||
.detailedDescription("Purge old AuthMeReloaded data longer than the specified number of days ago.")
|
||||
.withArgument("days", "Number of days", MANDATORY)
|
||||
.permission(AdminPermission.PURGE)
|
||||
.executableCommand(PurgeCommand.class)
|
||||
.register();
|
||||
|
||||
// Purge player command
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("purgeplayer")
|
||||
.description("Purges the data of one player")
|
||||
.detailedDescription("Purges data of the given player.")
|
||||
.withArgument("player", "The player to purge", MANDATORY)
|
||||
.withArgument("options", "'force' to run without checking if player is registered", OPTIONAL)
|
||||
.permission(AdminPermission.PURGE_PLAYER)
|
||||
.executableCommand(PurgePlayerCommand.class)
|
||||
.register();
|
||||
|
||||
// Backup command
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("backup")
|
||||
.description("Perform a backup")
|
||||
.detailedDescription("Creates a backup of the registered users.")
|
||||
.permission(AdminPermission.BACKUP)
|
||||
.executableCommand(BackupCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the purgelastposition command
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("resetpos", "purgelastposition", "purgelastpos", "resetposition",
|
||||
"resetlastposition", "resetlastpos")
|
||||
.description("Purge player's last position")
|
||||
.detailedDescription("Purge the last know position of the specified player or all of them.")
|
||||
.withArgument("player/*", "Player name or * for all players", MANDATORY)
|
||||
.permission(AdminPermission.PURGE_LAST_POSITION)
|
||||
.executableCommand(PurgeLastPositionCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the purgebannedplayers command
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("purgebannedplayers", "purgebannedplayer", "deletebannedplayers", "deletebannedplayer")
|
||||
.description("Purge banned players data")
|
||||
.detailedDescription("Purge all AuthMeReloaded data for banned players.")
|
||||
.permission(AdminPermission.PURGE_BANNED_PLAYERS)
|
||||
.executableCommand(PurgeBannedPlayersCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the switchantibot command
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("switchantibot", "toggleantibot", "antibot")
|
||||
.description("Switch AntiBot mode")
|
||||
.detailedDescription("Switch or toggle the AntiBot mode to the specified state.")
|
||||
.withArgument("mode", "ON / OFF", OPTIONAL)
|
||||
.permission(AdminPermission.SWITCH_ANTIBOT)
|
||||
.executableCommand(SwitchAntiBotCommand.class)
|
||||
.register();
|
||||
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("whitelist")
|
||||
.description("Switch whitelist mode")
|
||||
.detailedDescription("Switch or toggle the whitelist mode to the specified state.")
|
||||
.withArgument("mode", "ON / OFF", OPTIONAL)
|
||||
.permission(AdminPermission.SWITCH_ANTIBOT)
|
||||
.executableCommand(WhitelistCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the reload command
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("reload", "rld")
|
||||
.description("Reload plugin")
|
||||
.detailedDescription("Reload the AuthMeReloaded plugin.")
|
||||
.permission(AdminPermission.RELOAD)
|
||||
.executableCommand(ReloadCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the version command
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("version", "ver", "v", "about", "info")
|
||||
.description("Version info")
|
||||
.detailedDescription("Show detailed information about the installed AuthMeReloaded version, the "
|
||||
+ "developers, contributors, and license.")
|
||||
.executableCommand(VersionCommand.class)
|
||||
.register();
|
||||
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("converter", "convert", "conv")
|
||||
.description("Converter command")
|
||||
.detailedDescription("Converter command for AuthMeReloaded.")
|
||||
.withArgument("job", "Conversion job: xauth / crazylogin / rakamak / "
|
||||
+ "royalauth / vauth / sqliteToSql / mysqlToSqlite / loginsecurity", OPTIONAL)
|
||||
.permission(AdminPermission.CONVERTER)
|
||||
.executableCommand(ConverterCommand.class)
|
||||
.register();
|
||||
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("messages", "msg")
|
||||
.description("Add missing help messages")
|
||||
.detailedDescription("Adds missing texts to the current help messages file.")
|
||||
.permission(AdminPermission.UPDATE_MESSAGES)
|
||||
.executableCommand(UpdateHelpMessagesCommand.class)
|
||||
.register();
|
||||
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("recent")
|
||||
.description("See players who have recently logged in")
|
||||
.detailedDescription("Shows the last players that have logged in.")
|
||||
.permission(AdminPermission.SEE_RECENT_PLAYERS)
|
||||
.executableCommand(RecentPlayersCommand.class)
|
||||
.register();
|
||||
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("debug", "dbg")
|
||||
.description("Debug features")
|
||||
.detailedDescription("Allows various operations for debugging.")
|
||||
.withArgument("child", "The child to execute", OPTIONAL)
|
||||
.withArgument("arg", "argument (depends on debug section)", OPTIONAL)
|
||||
.withArgument("arg", "argument (depends on debug section)", OPTIONAL)
|
||||
.permission(DebugSectionPermissions.DEBUG_COMMAND)
|
||||
.executableCommand(DebugCommand.class)
|
||||
.register();
|
||||
|
||||
return authmeBase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a command description for {@code /email} including its children.
|
||||
*
|
||||
* @return the email base command description
|
||||
*/
|
||||
private CommandDescription buildEmailBaseCommand() {
|
||||
// Register the base Email command
|
||||
CommandDescription emailBase = CommandDescription.builder()
|
||||
.parent(null)
|
||||
.labels("email")
|
||||
.description("Add email or recover password")
|
||||
.detailedDescription("The AuthMeReloaded email command base.")
|
||||
.executableCommand(EmailBaseCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the show command
|
||||
CommandDescription.builder()
|
||||
.parent(emailBase)
|
||||
.labels("show", "myemail")
|
||||
.description("Show Email")
|
||||
.detailedDescription("Show your current email address.")
|
||||
.permission(PlayerPermission.SEE_EMAIL)
|
||||
.executableCommand(ShowEmailCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the add command
|
||||
CommandDescription.builder()
|
||||
.parent(emailBase)
|
||||
.labels("add", "addemail", "addmail")
|
||||
.description("Add Email")
|
||||
.detailedDescription("Add a new email address to your account.")
|
||||
.withArgument("email", "Email address", MANDATORY)
|
||||
.withArgument("verifyEmail", "Email address verification", MANDATORY)
|
||||
.permission(PlayerPermission.ADD_EMAIL)
|
||||
.executableCommand(AddEmailCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the change command
|
||||
CommandDescription.builder()
|
||||
.parent(emailBase)
|
||||
.labels("change", "changeemail", "changemail")
|
||||
.description("Change Email")
|
||||
.detailedDescription("Change an email address of your account.")
|
||||
.withArgument("oldEmail", "Old email address", MANDATORY)
|
||||
.withArgument("newEmail", "New email address", MANDATORY)
|
||||
.permission(PlayerPermission.CHANGE_EMAIL)
|
||||
.executableCommand(ChangeEmailCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the recover command
|
||||
CommandDescription.builder()
|
||||
.parent(emailBase)
|
||||
.labels("recover", "recovery", "recoveremail", "recovermail")
|
||||
.description("Recover password using email")
|
||||
.detailedDescription("Recover your account using an Email address by sending a mail containing "
|
||||
+ "a new password.")
|
||||
.withArgument("email", "Email address", MANDATORY)
|
||||
.permission(PlayerPermission.RECOVER_EMAIL)
|
||||
.executableCommand(RecoverEmailCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the process recovery code command
|
||||
CommandDescription.builder()
|
||||
.parent(emailBase)
|
||||
.labels("code")
|
||||
.description("Submit code to recover password")
|
||||
.detailedDescription("Recover your account by submitting a code delivered to your email.")
|
||||
.withArgument("code", "Recovery code", MANDATORY)
|
||||
.permission(PlayerPermission.RECOVER_EMAIL)
|
||||
.executableCommand(ProcessCodeCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the change password after recovery command
|
||||
CommandDescription.builder()
|
||||
.parent(emailBase)
|
||||
.labels("setpassword")
|
||||
.description("Set new password after recovery")
|
||||
.detailedDescription("Set a new password after successfully recovering your account.")
|
||||
.withArgument("password", "New password", MANDATORY)
|
||||
.permission(PlayerPermission.RECOVER_EMAIL)
|
||||
.executableCommand(EmailSetPasswordCommand.class)
|
||||
.register();
|
||||
|
||||
return emailBase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a command description object for {@code /totp} including its children.
|
||||
*
|
||||
* @return the totp base command description
|
||||
*/
|
||||
private CommandDescription buildTotpBaseCommand() {
|
||||
// Register the base totp command
|
||||
CommandDescription totpBase = CommandDescription.builder()
|
||||
.parent(null)
|
||||
.labels("totp", "2fa")
|
||||
.description("TOTP commands")
|
||||
.detailedDescription("Performs actions related to two-factor authentication.")
|
||||
.executableCommand(TotpBaseCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the base totp code
|
||||
CommandDescription.builder()
|
||||
.parent(totpBase)
|
||||
.labels("code", "c")
|
||||
.description("Command for logging in")
|
||||
.detailedDescription("Processes the two-factor authentication code during login.")
|
||||
.withArgument("code", "The TOTP code to use to log in", MANDATORY)
|
||||
.executableCommand(TotpCodeCommand.class)
|
||||
.register();
|
||||
|
||||
// Register totp add
|
||||
CommandDescription.builder()
|
||||
.parent(totpBase)
|
||||
.labels("add")
|
||||
.description("Enables TOTP")
|
||||
.detailedDescription("Enables two-factor authentication for your account.")
|
||||
.permission(PlayerPermission.ENABLE_TWO_FACTOR_AUTH)
|
||||
.executableCommand(AddTotpCommand.class)
|
||||
.register();
|
||||
|
||||
// Register totp confirm
|
||||
CommandDescription.builder()
|
||||
.parent(totpBase)
|
||||
.labels("confirm")
|
||||
.description("Enables TOTP after successful code")
|
||||
.detailedDescription("Saves the generated TOTP secret after confirmation.")
|
||||
.withArgument("code", "Code from the given secret from /totp add", MANDATORY)
|
||||
.permission(PlayerPermission.ENABLE_TWO_FACTOR_AUTH)
|
||||
.executableCommand(ConfirmTotpCommand.class)
|
||||
.register();
|
||||
|
||||
// Register totp remove
|
||||
CommandDescription.builder()
|
||||
.parent(totpBase)
|
||||
.labels("remove")
|
||||
.description("Removes TOTP")
|
||||
.detailedDescription("Disables two-factor authentication for your account.")
|
||||
.withArgument("code", "Current 2FA code", MANDATORY)
|
||||
.permission(PlayerPermission.DISABLE_TWO_FACTOR_AUTH)
|
||||
.executableCommand(RemoveTotpCommand.class)
|
||||
.register();
|
||||
|
||||
return totpBase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the help command on all base commands, e.g. to register /authme help or /register help.
|
||||
*
|
||||
* @param commands the list of base commands to register a help child command on
|
||||
*/
|
||||
private void setHelpOnAllBases(Collection<CommandDescription> commands) {
|
||||
final List<String> helpCommandLabels = Arrays.asList("help", "hlp", "h", "sos", "?");
|
||||
|
||||
for (CommandDescription base : commands) {
|
||||
CommandDescription.builder()
|
||||
.parent(base)
|
||||
.labels(helpCommandLabels)
|
||||
.description("View help")
|
||||
.detailedDescription("View detailed help for /" + base.getLabels().get(0) + " commands.")
|
||||
.withArgument("query", "The command or query to view help for.", OPTIONAL)
|
||||
.executableCommand(HelpCommand.class)
|
||||
.register();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,207 +0,0 @@
|
||||
package fr.xephi.authme.command;
|
||||
|
||||
import fr.xephi.authme.command.executable.HelpCommand;
|
||||
import fr.xephi.authme.permission.PermissionsManager;
|
||||
import fr.xephi.authme.util.StringUtils;
|
||||
import fr.xephi.authme.util.Utils;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
import static fr.xephi.authme.command.FoundResultStatus.INCORRECT_ARGUMENTS;
|
||||
import static fr.xephi.authme.command.FoundResultStatus.MISSING_BASE_COMMAND;
|
||||
import static fr.xephi.authme.command.FoundResultStatus.UNKNOWN_LABEL;
|
||||
|
||||
/**
|
||||
* Maps incoming command parts to the correct {@link CommandDescription}.
|
||||
*/
|
||||
public class CommandMapper {
|
||||
|
||||
/**
|
||||
* The class of the help command, to which the base label should also be passed in the arguments.
|
||||
*/
|
||||
private static final Class<? extends ExecutableCommand> HELP_COMMAND_CLASS = HelpCommand.class;
|
||||
|
||||
private final Collection<CommandDescription> baseCommands;
|
||||
private final PermissionsManager permissionsManager;
|
||||
|
||||
@Inject
|
||||
public CommandMapper(CommandInitializer commandInitializer, PermissionsManager permissionsManager) {
|
||||
this.baseCommands = commandInitializer.getCommands();
|
||||
this.permissionsManager = permissionsManager;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Map incoming command parts to a command. This processes all parts and distinguishes the labels from arguments.
|
||||
*
|
||||
* @param sender The command sender (null if none applicable)
|
||||
* @param parts The parts to map to commands and arguments
|
||||
* @return The generated {@link FoundCommandResult}
|
||||
*/
|
||||
public FoundCommandResult mapPartsToCommand(CommandSender sender, List<String> parts) {
|
||||
if (Utils.isCollectionEmpty(parts)) {
|
||||
return new FoundCommandResult(null, parts, null, 0.0, MISSING_BASE_COMMAND);
|
||||
}
|
||||
|
||||
CommandDescription base = getBaseCommand(parts.get(0));
|
||||
if (base == null) {
|
||||
return new FoundCommandResult(null, parts, null, 0.0, MISSING_BASE_COMMAND);
|
||||
}
|
||||
|
||||
// Prefer labels: /register help goes to "Help command", not "Register command" with argument 'help'
|
||||
List<String> remainingParts = parts.subList(1, parts.size());
|
||||
CommandDescription childCommand = getSuitableChild(base, remainingParts);
|
||||
if (childCommand != null) {
|
||||
FoundResultStatus status = getPermissionAwareStatus(sender, childCommand);
|
||||
FoundCommandResult result = new FoundCommandResult(
|
||||
childCommand, parts.subList(0, 2), parts.subList(2, parts.size()), 0.0, status);
|
||||
return transformResultForHelp(result);
|
||||
} else if (hasSuitableArgumentCount(base, remainingParts.size())) {
|
||||
FoundResultStatus status = getPermissionAwareStatus(sender, base);
|
||||
return new FoundCommandResult(base, parts.subList(0, 1), parts.subList(1, parts.size()), 0.0, status);
|
||||
}
|
||||
|
||||
return getCommandWithSmallestDifference(base, parts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all {@link ExecutableCommand} classes referenced in {@link CommandDescription} objects.
|
||||
*
|
||||
* @return all classes
|
||||
* @see CommandInitializer#getCommands
|
||||
*/
|
||||
public Set<Class<? extends ExecutableCommand>> getCommandClasses() {
|
||||
Set<Class<? extends ExecutableCommand>> classes = new HashSet<>(50);
|
||||
for (CommandDescription command : baseCommands) {
|
||||
classes.add(command.getExecutableCommand());
|
||||
for (CommandDescription child : command.getChildren()) {
|
||||
classes.add(child.getExecutableCommand());
|
||||
}
|
||||
}
|
||||
return classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the command whose label matches the given parts the best. This method is called when
|
||||
* a successful mapping could not be performed.
|
||||
*
|
||||
* @param base the base command
|
||||
* @param parts the command parts
|
||||
* @return the closest result
|
||||
*/
|
||||
private static FoundCommandResult getCommandWithSmallestDifference(CommandDescription base, List<String> parts) {
|
||||
// Return the base command with incorrect arg count error if we only have one part
|
||||
if (parts.size() <= 1) {
|
||||
return new FoundCommandResult(base, parts, new ArrayList<>(), 0.0, INCORRECT_ARGUMENTS);
|
||||
}
|
||||
|
||||
final String childLabel = parts.get(1);
|
||||
double minDifference = Double.POSITIVE_INFINITY;
|
||||
CommandDescription closestCommand = null;
|
||||
|
||||
for (CommandDescription child : base.getChildren()) {
|
||||
double difference = getLabelDifference(child, childLabel);
|
||||
if (difference < minDifference) {
|
||||
minDifference = difference;
|
||||
closestCommand = child;
|
||||
}
|
||||
}
|
||||
|
||||
// base command may have no children, in which case we return the base command with incorrect arguments error
|
||||
if (closestCommand == null) {
|
||||
return new FoundCommandResult(
|
||||
base, parts.subList(0, 1), parts.subList(1, parts.size()), 0.0, INCORRECT_ARGUMENTS);
|
||||
}
|
||||
|
||||
FoundResultStatus status = (minDifference == 0.0) ? INCORRECT_ARGUMENTS : UNKNOWN_LABEL;
|
||||
final int partsSize = parts.size();
|
||||
List<String> labels = parts.subList(0, Math.min(closestCommand.getLabelCount(), partsSize));
|
||||
List<String> arguments = (labels.size() == partsSize)
|
||||
? new ArrayList<>()
|
||||
: parts.subList(labels.size(), partsSize);
|
||||
|
||||
return new FoundCommandResult(closestCommand, labels, arguments, minDifference, status);
|
||||
}
|
||||
|
||||
private CommandDescription getBaseCommand(String label) {
|
||||
String baseLabel = label.toLowerCase(Locale.ROOT);
|
||||
if (baseLabel.startsWith("authme:")) {
|
||||
baseLabel = baseLabel.substring("authme:".length());
|
||||
}
|
||||
for (CommandDescription command : baseCommands) {
|
||||
if (command.hasLabel(baseLabel)) {
|
||||
return command;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a child from a base command if the label and the argument count match.
|
||||
*
|
||||
* @param baseCommand The base command whose children should be checked
|
||||
* @param parts The command parts received from the invocation; the first item is the potential label and any
|
||||
* other items are command arguments. The first initial part that led to the base command should not
|
||||
* be present.
|
||||
*
|
||||
* @return A command if there was a complete match (including proper argument count), null otherwise
|
||||
*/
|
||||
private static CommandDescription getSuitableChild(CommandDescription baseCommand, List<String> parts) {
|
||||
if (Utils.isCollectionEmpty(parts)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final String label = parts.get(0).toLowerCase(Locale.ROOT);
|
||||
final int argumentCount = parts.size() - 1;
|
||||
|
||||
for (CommandDescription child : baseCommand.getChildren()) {
|
||||
if (child.hasLabel(label) && hasSuitableArgumentCount(child, argumentCount)) {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static FoundCommandResult transformResultForHelp(FoundCommandResult result) {
|
||||
if (result.getCommandDescription() != null
|
||||
&& HELP_COMMAND_CLASS == result.getCommandDescription().getExecutableCommand()) {
|
||||
// For "/authme help register" we have labels = [authme, help] and arguments = [register]
|
||||
// But for the help command we want labels = [authme, help] and arguments = [authme, register],
|
||||
// so we can use the arguments as the labels to the command to show help for
|
||||
List<String> arguments = new ArrayList<>(result.getArguments());
|
||||
arguments.add(0, result.getLabels().get(0));
|
||||
return new FoundCommandResult(result.getCommandDescription(), result.getLabels(),
|
||||
arguments, result.getDifference(), result.getResultStatus());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private FoundResultStatus getPermissionAwareStatus(CommandSender sender, CommandDescription command) {
|
||||
if (sender != null && !permissionsManager.hasPermission(sender, command.getPermission())) {
|
||||
return FoundResultStatus.NO_PERMISSION;
|
||||
}
|
||||
return FoundResultStatus.SUCCESS;
|
||||
}
|
||||
|
||||
private static boolean hasSuitableArgumentCount(CommandDescription command, int argumentCount) {
|
||||
int minArgs = CommandUtils.getMinNumberOfArguments(command);
|
||||
int maxArgs = CommandUtils.getMaxNumberOfArguments(command);
|
||||
|
||||
return argumentCount >= minArgs && argumentCount <= maxArgs;
|
||||
}
|
||||
|
||||
private static double getLabelDifference(CommandDescription command, String givenLabel) {
|
||||
return command.getLabels().stream()
|
||||
.map(label -> StringUtils.getDifference(label, givenLabel))
|
||||
.min(Double::compareTo)
|
||||
.orElseThrow(() -> new IllegalStateException("Command does not have any labels set"));
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,109 +0,0 @@
|
||||
package fr.xephi.authme.command;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import org.bukkit.ChatColor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Utility functions for {@link CommandDescription} objects.
|
||||
*/
|
||||
public final class CommandUtils {
|
||||
|
||||
private CommandUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the minimum number of arguments required for running the command (= number of mandatory arguments).
|
||||
*
|
||||
* @param command the command to process
|
||||
* @return min number of arguments required by the command
|
||||
*/
|
||||
public static int getMinNumberOfArguments(CommandDescription command) {
|
||||
int mandatoryArguments = 0;
|
||||
for (CommandArgumentDescription argument : command.getArguments()) {
|
||||
if (!argument.isOptional()) {
|
||||
++mandatoryArguments;
|
||||
}
|
||||
}
|
||||
return mandatoryArguments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum number of arguments the command accepts.
|
||||
*
|
||||
* @param command the command to process
|
||||
* @return max number of arguments that may be passed to the command
|
||||
*/
|
||||
public static int getMaxNumberOfArguments(CommandDescription command) {
|
||||
return command.getArguments().size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a hierarchical list of commands for the given command. The commands are in order:
|
||||
* the parents of the given command precede the provided command. For example, given the command
|
||||
* for {@code /authme register}, a list with {@code [{authme}, {authme register}]} is returned.
|
||||
*
|
||||
* @param command the command to build a parent list for
|
||||
* @return the parent list
|
||||
*/
|
||||
public static List<CommandDescription> constructParentList(CommandDescription command) {
|
||||
List<CommandDescription> commands = new ArrayList<>();
|
||||
CommandDescription currentCommand = command;
|
||||
while (currentCommand != null) {
|
||||
commands.add(currentCommand);
|
||||
currentCommand = currentCommand.getParent();
|
||||
}
|
||||
return Lists.reverse(commands);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a textual representation of the command, e.g. {@code /authme register}.
|
||||
*
|
||||
* @param command the command to create the path for
|
||||
* @return the command string
|
||||
*/
|
||||
public static String constructCommandPath(CommandDescription command) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String prefix = "/";
|
||||
for (CommandDescription ancestor : constructParentList(command)) {
|
||||
sb.append(prefix).append(ancestor.getLabels().get(0));
|
||||
prefix = " ";
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a command path with color formatting, based on the supplied labels. This includes
|
||||
* the command's arguments, as defined in the provided command description. The list of labels
|
||||
* must contain all labels to be used.
|
||||
*
|
||||
* @param command the command to read arguments from
|
||||
* @param correctLabels the labels to use (must be complete)
|
||||
* @return formatted command syntax incl. arguments
|
||||
*/
|
||||
public static String buildSyntax(CommandDescription command, List<String> correctLabels) {
|
||||
String commandSyntax = ChatColor.WHITE + "/" + correctLabels.get(0) + ChatColor.YELLOW;
|
||||
for (int i = 1; i < correctLabels.size(); ++i) {
|
||||
commandSyntax += " " + correctLabels.get(i);
|
||||
}
|
||||
for (CommandArgumentDescription argument : command.getArguments()) {
|
||||
commandSyntax += " " + formatArgument(argument);
|
||||
}
|
||||
return commandSyntax;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a command argument with the proper type of brackets.
|
||||
*
|
||||
* @param argument the argument to format
|
||||
* @return the formatted argument
|
||||
*/
|
||||
public static String formatArgument(CommandArgumentDescription argument) {
|
||||
if (argument.isOptional()) {
|
||||
return "[" + argument.getName() + "]";
|
||||
}
|
||||
return "<" + argument.getName() + ">";
|
||||
}
|
||||
}
|
||||
@ -1,31 +0,0 @@
|
||||
package fr.xephi.authme.command;
|
||||
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Base class for AuthMe commands that can be executed.
|
||||
*/
|
||||
public interface ExecutableCommand {
|
||||
|
||||
/**
|
||||
* Executes the command with the given arguments.
|
||||
*
|
||||
* @param sender the command sender (initiator of the command)
|
||||
* @param arguments the arguments
|
||||
*/
|
||||
void executeCommand(CommandSender sender, List<String> arguments);
|
||||
|
||||
/**
|
||||
* Returns the message to show to the user if the command is used with the wrong arguments.
|
||||
* If null is returned, the standard help (/<i>command</i> help) output is shown.
|
||||
*
|
||||
* @return the message explaining the command's usage, or {@code null} for default behavior
|
||||
*/
|
||||
default MessageKey getArgumentsMismatchMessage() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,79 +0,0 @@
|
||||
package fr.xephi.authme.command;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Result of a command mapping by {@link CommandHandler}. An object of this class represents a successful mapping
|
||||
* as well as erroneous ones, as communicated with {@link FoundResultStatus}.
|
||||
* <p>
|
||||
* Fields other than {@link FoundResultStatus} are available depending, among other factors, on the status:
|
||||
* <ul>
|
||||
* <li>{@link FoundResultStatus#SUCCESS} entails that mapping the input to a command was successful. Therefore,
|
||||
* the command description, labels and arguments are set. The difference is 0.0.</li>
|
||||
* <li>{@link FoundResultStatus#INCORRECT_ARGUMENTS}: The received parts could be mapped to a command but the argument
|
||||
* count doesn't match. Guarantees that the command description field is not null; difference is 0.0</li>
|
||||
* <li>{@link FoundResultStatus#UNKNOWN_LABEL}: The labels could not be mapped to a command. The command description
|
||||
* may be set to the most similar command, or it may be null. Difference is above 0.0.</li>
|
||||
* <li>{@link FoundResultStatus#NO_PERMISSION}: The command could be matched properly but the sender does not have
|
||||
* permission to execute it.</li>
|
||||
* <li>{@link FoundResultStatus#MISSING_BASE_COMMAND} should never occur. All other fields may be null and any further
|
||||
* processing of the object should be aborted.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public class FoundCommandResult {
|
||||
|
||||
/**
|
||||
* The command description instance.
|
||||
*/
|
||||
private final CommandDescription commandDescription;
|
||||
/**
|
||||
* The labels used to invoke the command. This may be different for the same {@link ExecutableCommand} instance
|
||||
* if multiple labels have been defined, e.g. "/authme register" and "/authme reg".
|
||||
*/
|
||||
private final List<String> labels;
|
||||
/** The command arguments. */
|
||||
private final List<String> arguments;
|
||||
/** The difference between the matched command and the supplied labels. */
|
||||
private final double difference;
|
||||
/** The status of the result (see class description). */
|
||||
private final FoundResultStatus resultStatus;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param commandDescription The command description.
|
||||
* @param labels The labels used to access the command.
|
||||
* @param arguments The command arguments.
|
||||
* @param difference The difference between the supplied labels and the matched command.
|
||||
* @param resultStatus The status of the result.
|
||||
*/
|
||||
public FoundCommandResult(CommandDescription commandDescription, List<String> labels, List<String> arguments,
|
||||
double difference, FoundResultStatus resultStatus) {
|
||||
this.commandDescription = commandDescription;
|
||||
this.labels = labels;
|
||||
this.arguments = arguments;
|
||||
this.difference = difference;
|
||||
this.resultStatus = resultStatus;
|
||||
}
|
||||
|
||||
public CommandDescription getCommandDescription() {
|
||||
return this.commandDescription;
|
||||
}
|
||||
|
||||
public List<String> getArguments() {
|
||||
return this.arguments;
|
||||
}
|
||||
|
||||
public List<String> getLabels() {
|
||||
return this.labels;
|
||||
}
|
||||
|
||||
public double getDifference() {
|
||||
return difference;
|
||||
}
|
||||
|
||||
public FoundResultStatus getResultStatus() {
|
||||
return resultStatus;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,18 +0,0 @@
|
||||
package fr.xephi.authme.command;
|
||||
|
||||
/**
|
||||
* Result status for mapping command parts. See {@link FoundCommandResult} for a detailed description of the states.
|
||||
*/
|
||||
public enum FoundResultStatus {
|
||||
|
||||
SUCCESS,
|
||||
|
||||
INCORRECT_ARGUMENTS,
|
||||
|
||||
UNKNOWN_LABEL,
|
||||
|
||||
NO_PERMISSION,
|
||||
|
||||
MISSING_BASE_COMMAND
|
||||
|
||||
}
|
||||
@ -1,45 +0,0 @@
|
||||
package fr.xephi.authme.command;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Common base type for player-only commands, handling the verification that the command sender is indeed a player.
|
||||
*/
|
||||
public abstract class PlayerCommand implements ExecutableCommand {
|
||||
|
||||
@Override
|
||||
public void executeCommand(CommandSender sender, List<String> arguments) {
|
||||
if (sender instanceof Player) {
|
||||
runCommand((Player) sender, arguments);
|
||||
} else {
|
||||
String alternative = getAlternativeCommand();
|
||||
if (alternative != null) {
|
||||
sender.sendMessage("Player only! Please use " + alternative + " instead.");
|
||||
} else {
|
||||
sender.sendMessage("This command is only for players.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the command with the given player and arguments.
|
||||
*
|
||||
* @param player the player who initiated the command
|
||||
* @param arguments the arguments supplied with the command
|
||||
*/
|
||||
protected abstract void runCommand(Player player, List<String> arguments);
|
||||
|
||||
/**
|
||||
* Returns an alternative command (textual representation) that is not restricted to players only.
|
||||
* Example: {@code "/authme register <playerName> <password>"}
|
||||
*
|
||||
* @return Alternative command not restricted to players, or null if not applicable
|
||||
*/
|
||||
protected String getAlternativeCommand() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,64 +0,0 @@
|
||||
package fr.xephi.authme.command.executable;
|
||||
|
||||
import fr.xephi.authme.command.CommandMapper;
|
||||
import fr.xephi.authme.command.CommandUtils;
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.command.FoundCommandResult;
|
||||
import fr.xephi.authme.command.FoundResultStatus;
|
||||
import fr.xephi.authme.command.help.HelpProvider;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
import static fr.xephi.authme.command.FoundResultStatus.MISSING_BASE_COMMAND;
|
||||
import static fr.xephi.authme.command.FoundResultStatus.UNKNOWN_LABEL;
|
||||
import static fr.xephi.authme.command.help.HelpProvider.ALL_OPTIONS;
|
||||
import static fr.xephi.authme.command.help.HelpProvider.SHOW_ALTERNATIVES;
|
||||
import static fr.xephi.authme.command.help.HelpProvider.SHOW_CHILDREN;
|
||||
import static fr.xephi.authme.command.help.HelpProvider.SHOW_COMMAND;
|
||||
import static fr.xephi.authme.command.help.HelpProvider.SHOW_DESCRIPTION;
|
||||
|
||||
/**
|
||||
* Displays help information to a user.
|
||||
*/
|
||||
public class HelpCommand implements ExecutableCommand {
|
||||
|
||||
@Inject
|
||||
private CommandMapper commandMapper;
|
||||
|
||||
@Inject
|
||||
private HelpProvider helpProvider;
|
||||
|
||||
|
||||
// Convention: arguments is not the actual invoked arguments but the command that was invoked,
|
||||
// e.g. "/authme help register" would typically be arguments = [register], but here we pass [authme, register]
|
||||
@Override
|
||||
public void executeCommand(CommandSender sender, List<String> arguments) {
|
||||
FoundCommandResult result = commandMapper.mapPartsToCommand(sender, arguments);
|
||||
|
||||
FoundResultStatus resultStatus = result.getResultStatus();
|
||||
if (MISSING_BASE_COMMAND.equals(resultStatus)) {
|
||||
sender.sendMessage(ChatColor.DARK_RED + "Could not get base command");
|
||||
return;
|
||||
} else if (UNKNOWN_LABEL.equals(resultStatus)) {
|
||||
if (result.getCommandDescription() == null) {
|
||||
sender.sendMessage(ChatColor.DARK_RED + "Unknown command");
|
||||
return;
|
||||
} else {
|
||||
sender.sendMessage(ChatColor.GOLD + "Assuming " + ChatColor.WHITE
|
||||
+ CommandUtils.constructCommandPath(result.getCommandDescription()));
|
||||
}
|
||||
}
|
||||
|
||||
int mappedCommandLevel = result.getCommandDescription().getLabelCount();
|
||||
if (mappedCommandLevel == 1) {
|
||||
helpProvider.outputHelp(sender, result,
|
||||
SHOW_COMMAND | SHOW_DESCRIPTION | SHOW_CHILDREN | SHOW_ALTERNATIVES);
|
||||
} else {
|
||||
helpProvider.outputHelp(sender, result, ALL_OPTIONS);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,74 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.service.BukkitService;
|
||||
import fr.xephi.authme.service.CommonService;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Shows all accounts registered by the same IP address for the given player name or IP address.
|
||||
*/
|
||||
public class AccountsCommand implements ExecutableCommand {
|
||||
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
|
||||
@Inject
|
||||
private BukkitService bukkitService;
|
||||
|
||||
@Inject
|
||||
private CommonService commonService;
|
||||
|
||||
@Override
|
||||
public void executeCommand(final CommandSender sender, List<String> arguments) {
|
||||
// TODO #1366: last IP vs. registration IP?
|
||||
final String playerName = arguments.isEmpty() ? sender.getName() : arguments.get(0);
|
||||
|
||||
// Assumption: a player name cannot contain '.'
|
||||
if (playerName.contains(".")) {
|
||||
bukkitService.runTaskAsynchronously(() -> {
|
||||
List<String> accountList = dataSource.getAllAuthsByIp(playerName);
|
||||
if (accountList.isEmpty()) {
|
||||
sender.sendMessage("[AuthMe] This IP does not exist in the database.");
|
||||
} else if (accountList.size() == 1) {
|
||||
sender.sendMessage("[AuthMe] " + playerName + " is a single account player");
|
||||
} else {
|
||||
outputAccountsList(sender, playerName, accountList);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
bukkitService.runTaskAsynchronously(() -> {
|
||||
PlayerAuth auth = dataSource.getAuth(playerName.toLowerCase(Locale.ROOT));
|
||||
if (auth == null) {
|
||||
commonService.send(sender, MessageKey.UNKNOWN_USER);
|
||||
return;
|
||||
} else if (auth.getLastIp() == null) {
|
||||
sender.sendMessage("No known last IP address for player");
|
||||
return;
|
||||
}
|
||||
|
||||
List<String> accountList = dataSource.getAllAuthsByIp(auth.getLastIp());
|
||||
if (accountList.isEmpty()) {
|
||||
commonService.send(sender, MessageKey.UNKNOWN_USER);
|
||||
} else if (accountList.size() == 1) {
|
||||
sender.sendMessage("[AuthMe] " + playerName + " is a single account player");
|
||||
} else {
|
||||
outputAccountsList(sender, playerName, accountList);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static void outputAccountsList(CommandSender sender, String playerName, List<String> accountList) {
|
||||
sender.sendMessage("[AuthMe] " + playerName + " has " + accountList.size() + " accounts.");
|
||||
String message = "[AuthMe] " + String.join(", ", accountList) + ".";
|
||||
sender.sendMessage(message);
|
||||
}
|
||||
}
|
||||
@ -1,24 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import fr.xephi.authme.AuthMe;
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* AuthMe base command; shows the version and some command pointers.
|
||||
*/
|
||||
public class AuthMeCommand implements ExecutableCommand {
|
||||
|
||||
@Override
|
||||
public void executeCommand(CommandSender sender, List<String> arguments) {
|
||||
sender.sendMessage(ChatColor.GREEN + "This server is running " + AuthMe.getPluginName() + " v"
|
||||
+ AuthMe.getPluginVersion() + " b" + AuthMe.getPluginBuildNumber()+ "! " + ChatColor.RED + "<3");
|
||||
sender.sendMessage(ChatColor.YELLOW + "Use the command " + ChatColor.GOLD + "/authme help" + ChatColor.YELLOW
|
||||
+ " to view help.");
|
||||
sender.sendMessage(ChatColor.YELLOW + "Use the command " + ChatColor.GOLD + "/authme about" + ChatColor.YELLOW
|
||||
+ " to view about.");
|
||||
}
|
||||
}
|
||||
@ -1,23 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.service.BackupService;
|
||||
import fr.xephi.authme.service.BackupService.BackupCause;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Command to perform a backup.
|
||||
*/
|
||||
public class BackupCommand implements ExecutableCommand {
|
||||
|
||||
@Inject
|
||||
private BackupService backupService;
|
||||
|
||||
@Override
|
||||
public void executeCommand(CommandSender sender, List<String> arguments) {
|
||||
backupService.doBackup(BackupCause.COMMAND, sender);
|
||||
}
|
||||
}
|
||||
@ -1,41 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.process.Management;
|
||||
import fr.xephi.authme.service.CommonService;
|
||||
import fr.xephi.authme.service.ValidationService;
|
||||
import fr.xephi.authme.service.ValidationService.ValidationResult;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Admin command for changing a player's password.
|
||||
*/
|
||||
public class ChangePasswordAdminCommand implements ExecutableCommand {
|
||||
|
||||
@Inject
|
||||
private ValidationService validationService;
|
||||
|
||||
@Inject
|
||||
private CommonService commonService;
|
||||
|
||||
@Inject
|
||||
private Management management;
|
||||
|
||||
@Override
|
||||
public void executeCommand(CommandSender sender, List<String> arguments) {
|
||||
// Get the player and password
|
||||
final String playerName = arguments.get(0);
|
||||
final String playerPass = arguments.get(1);
|
||||
|
||||
// Validate the password
|
||||
ValidationResult validationResult = validationService.validatePassword(playerPass, playerName);
|
||||
if (validationResult.hasError()) {
|
||||
commonService.send(sender, validationResult.getMessageKey(), validationResult.getArgs());
|
||||
} else {
|
||||
management.performPasswordChangeAsAdmin(sender, playerName, playerPass);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,95 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import ch.jalu.injector.factory.Factory;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.datasource.converter.Converter;
|
||||
import fr.xephi.authme.datasource.converter.CrazyLoginConverter;
|
||||
import fr.xephi.authme.datasource.converter.LoginSecurityConverter;
|
||||
import fr.xephi.authme.datasource.converter.MySqlToSqlite;
|
||||
import fr.xephi.authme.datasource.converter.RakamakConverter;
|
||||
import fr.xephi.authme.datasource.converter.RoyalAuthConverter;
|
||||
import fr.xephi.authme.datasource.converter.SqliteToSql;
|
||||
import fr.xephi.authme.datasource.converter.VAuthConverter;
|
||||
import fr.xephi.authme.datasource.converter.XAuthConverter;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.output.ConsoleLoggerFactory;
|
||||
import fr.xephi.authme.service.BukkitService;
|
||||
import fr.xephi.authme.service.CommonService;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Converter command: launches conversion based on its parameters.
|
||||
*/
|
||||
public class ConverterCommand implements ExecutableCommand {
|
||||
|
||||
@VisibleForTesting
|
||||
static final Map<String, Class<? extends Converter>> CONVERTERS = getConverters();
|
||||
|
||||
private final ConsoleLogger logger = ConsoleLoggerFactory.get(ConverterCommand.class);
|
||||
|
||||
@Inject
|
||||
private CommonService commonService;
|
||||
|
||||
@Inject
|
||||
private BukkitService bukkitService;
|
||||
|
||||
@Inject
|
||||
private Factory<Converter> converterFactory;
|
||||
|
||||
@Override
|
||||
public void executeCommand(CommandSender sender, List<String> arguments) {
|
||||
Class<? extends Converter> converterClass = getConverterClassFromArgs(arguments);
|
||||
if (converterClass == null) {
|
||||
sender.sendMessage("Converters: " + String.join(", ", CONVERTERS.keySet()));
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the proper converter instance
|
||||
final Converter converter = converterFactory.newInstance(converterClass);
|
||||
|
||||
// Run the convert job
|
||||
bukkitService.runTaskAsynchronously(() -> {
|
||||
try {
|
||||
converter.execute(sender);
|
||||
} catch (Exception e) {
|
||||
commonService.send(sender, MessageKey.ERROR);
|
||||
logger.logException("Error during conversion:", e);
|
||||
}
|
||||
});
|
||||
|
||||
// Show a status message
|
||||
sender.sendMessage("[AuthMe] Successfully started " + arguments.get(0));
|
||||
}
|
||||
|
||||
private static Class<? extends Converter> getConverterClassFromArgs(List<String> arguments) {
|
||||
return arguments.isEmpty()
|
||||
? null
|
||||
: CONVERTERS.get(arguments.get(0).toLowerCase(Locale.ROOT));
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a map with all available converters.
|
||||
*
|
||||
* @return map with all available converters
|
||||
*/
|
||||
private static Map<String, Class<? extends Converter>> getConverters() {
|
||||
return ImmutableSortedMap.<String, Class<? extends Converter>>naturalOrder()
|
||||
.put("xauth", XAuthConverter.class)
|
||||
.put("crazylogin", CrazyLoginConverter.class)
|
||||
.put("rakamak", RakamakConverter.class)
|
||||
.put("royalauth", RoyalAuthConverter.class)
|
||||
.put("vauth", VAuthConverter.class)
|
||||
.put("sqlitetosql", SqliteToSql.class)
|
||||
.put("mysqltosqlite", MySqlToSqlite.class)
|
||||
.put("loginsecurity", LoginSecurityConverter.class)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@ -1,26 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import fr.xephi.authme.command.PlayerCommand;
|
||||
import fr.xephi.authme.settings.SpawnLoader;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Teleports the player to the first spawn.
|
||||
*/
|
||||
public class FirstSpawnCommand extends PlayerCommand {
|
||||
|
||||
@Inject
|
||||
private SpawnLoader spawnLoader;
|
||||
|
||||
@Override
|
||||
public void runCommand(Player player, List<String> arguments) {
|
||||
if (spawnLoader.getFirstSpawn() == null) {
|
||||
player.sendMessage("[AuthMe] First spawn has failed, please try to define the first spawn");
|
||||
} else {
|
||||
player.teleport(spawnLoader.getFirstSpawn());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,44 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.permission.PermissionsManager;
|
||||
import fr.xephi.authme.process.Management;
|
||||
import fr.xephi.authme.service.BukkitService;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
import static fr.xephi.authme.permission.PlayerPermission.CAN_LOGIN_BE_FORCED;
|
||||
|
||||
/**
|
||||
* Forces the login of a player, i.e. logs the player in without the need of a (correct) password.
|
||||
*/
|
||||
public class ForceLoginCommand implements ExecutableCommand {
|
||||
|
||||
@Inject
|
||||
private PermissionsManager permissionsManager;
|
||||
|
||||
@Inject
|
||||
private Management management;
|
||||
|
||||
@Inject
|
||||
private BukkitService bukkitService;
|
||||
|
||||
@Override
|
||||
public void executeCommand(CommandSender sender, List<String> arguments) {
|
||||
// Get the player query
|
||||
String playerName = arguments.isEmpty() ? sender.getName() : arguments.get(0);
|
||||
|
||||
Player player = bukkitService.getPlayerExact(playerName);
|
||||
if (player == null || !player.isOnline()) {
|
||||
sender.sendMessage("Player needs to be online!");
|
||||
} else if (!permissionsManager.hasPermission(player, CAN_LOGIN_BE_FORCED)) {
|
||||
sender.sendMessage("You cannot force login the player " + playerName + "!");
|
||||
} else {
|
||||
management.forceLogin(player);
|
||||
sender.sendMessage("Force login for " + playerName + " performed!");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,35 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import ch.jalu.datasourcecolumns.data.DataSourceValue;
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.service.CommonService;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Returns a player's email.
|
||||
*/
|
||||
public class GetEmailCommand implements ExecutableCommand {
|
||||
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
|
||||
@Inject
|
||||
private CommonService commonService;
|
||||
|
||||
@Override
|
||||
public void executeCommand(CommandSender sender, List<String> arguments) {
|
||||
String playerName = arguments.isEmpty() ? sender.getName() : arguments.get(0);
|
||||
|
||||
DataSourceValue<String> email = dataSource.getEmail(playerName);
|
||||
if (email.rowExists()) {
|
||||
sender.sendMessage("[AuthMe] " + playerName + "'s email: " + email.getValue());
|
||||
} else {
|
||||
commonService.send(sender, MessageKey.UNKNOWN_USER);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,41 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.service.BukkitService;
|
||||
import fr.xephi.authme.util.PlayerUtils;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
public class GetIpCommand implements ExecutableCommand {
|
||||
|
||||
@Inject
|
||||
private BukkitService bukkitService;
|
||||
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
|
||||
@Override
|
||||
public void executeCommand(CommandSender sender, List<String> arguments) {
|
||||
String playerName = arguments.get(0);
|
||||
Player player = bukkitService.getPlayerExact(playerName);
|
||||
PlayerAuth auth = dataSource.getAuth(playerName);
|
||||
|
||||
if (player != null) {
|
||||
sender.sendMessage("Current IP of " + player.getName() + " is " + PlayerUtils.getPlayerIp(player)
|
||||
+ ":" + player.getAddress().getPort());
|
||||
}
|
||||
|
||||
if (auth == null) {
|
||||
String displayName = player == null ? playerName : player.getName();
|
||||
sender.sendMessage(displayName + " is not registered in the database");
|
||||
} else {
|
||||
sender.sendMessage("Database: last IP: " + auth.getLastIp() + ", registration IP: "
|
||||
+ auth.getRegistrationIp());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,56 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.service.CommonService;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Returns the last login date of the given user.
|
||||
*/
|
||||
public class LastLoginCommand implements ExecutableCommand {
|
||||
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
|
||||
@Inject
|
||||
private CommonService commonService;
|
||||
|
||||
@Override
|
||||
public void executeCommand(CommandSender sender, List<String> arguments) {
|
||||
// Get the player
|
||||
String playerName = arguments.isEmpty() ? sender.getName() : arguments.get(0);
|
||||
|
||||
PlayerAuth auth = dataSource.getAuth(playerName);
|
||||
if (auth == null) {
|
||||
commonService.send(sender, MessageKey.UNKNOWN_USER);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the last login date
|
||||
final Long lastLogin = auth.getLastLogin();
|
||||
final String lastLoginDate = lastLogin == null ? "never" : new Date(lastLogin).toString();
|
||||
|
||||
// Show the player status
|
||||
sender.sendMessage("[AuthMe] " + playerName + " last login: " + lastLoginDate);
|
||||
if (lastLogin != null) {
|
||||
sender.sendMessage("[AuthMe] The player " + playerName + " last logged in "
|
||||
+ createLastLoginIntervalMessage(lastLogin) + " ago");
|
||||
}
|
||||
sender.sendMessage("[AuthMe] Last player's IP: " + auth.getLastIp());
|
||||
}
|
||||
|
||||
private static String createLastLoginIntervalMessage(long lastLogin) {
|
||||
final long diff = System.currentTimeMillis() - lastLogin;
|
||||
return (int) (diff / 86400000) + " days "
|
||||
+ (int) (diff / 3600000 % 24) + " hours "
|
||||
+ (int) (diff / 60000 % 60) + " mins "
|
||||
+ (int) (diff / 1000 % 60) + " secs";
|
||||
}
|
||||
}
|
||||
@ -1,38 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.service.BukkitService;
|
||||
import fr.xephi.authme.task.purge.PurgeService;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Command for purging data of banned players. Depending on the settings
|
||||
* it purges (deletes) data from third-party plugins as well.
|
||||
*/
|
||||
public class PurgeBannedPlayersCommand implements ExecutableCommand {
|
||||
|
||||
@Inject
|
||||
private PurgeService purgeService;
|
||||
|
||||
@Inject
|
||||
private BukkitService bukkitService;
|
||||
|
||||
@Override
|
||||
public void executeCommand(CommandSender sender, List<String> arguments) {
|
||||
// Get the list of banned players
|
||||
Set<OfflinePlayer> bannedPlayers = bukkitService.getBannedPlayers();
|
||||
Set<String> namedBanned = new HashSet<>(bannedPlayers.size());
|
||||
for (OfflinePlayer offlinePlayer : bannedPlayers) {
|
||||
namedBanned.add(offlinePlayer.getName().toLowerCase(Locale.ROOT));
|
||||
}
|
||||
|
||||
purgeService.purgePlayers(sender, namedBanned, bannedPlayers.toArray(new OfflinePlayer[bannedPlayers.size()]));
|
||||
}
|
||||
}
|
||||
@ -1,51 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import com.google.common.primitives.Ints;
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.task.purge.PurgeService;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Command for purging the data of players which have not been online for a given number
|
||||
* of days. Depending on the settings, this removes player data in third-party plugins as well.
|
||||
*/
|
||||
public class PurgeCommand implements ExecutableCommand {
|
||||
|
||||
private static final int MINIMUM_LAST_SEEN_DAYS = 30;
|
||||
|
||||
@Inject
|
||||
private PurgeService purgeService;
|
||||
|
||||
@Override
|
||||
public void executeCommand(CommandSender sender, List<String> arguments) {
|
||||
// Get the days parameter
|
||||
String daysStr = arguments.get(0);
|
||||
|
||||
// Convert the days string to an integer value, and make sure it's valid
|
||||
Integer days = Ints.tryParse(daysStr);
|
||||
if (days == null) {
|
||||
sender.sendMessage(ChatColor.RED + "The value you've entered is invalid!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate the value
|
||||
if (days < MINIMUM_LAST_SEEN_DAYS) {
|
||||
sender.sendMessage(ChatColor.RED + "You can only purge data older than "
|
||||
+ MINIMUM_LAST_SEEN_DAYS + " days");
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a calender instance to determine the date
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.add(Calendar.DATE, -days);
|
||||
long until = calendar.getTimeInMillis();
|
||||
|
||||
// Run the purge
|
||||
purgeService.runPurge(sender, until);
|
||||
}
|
||||
}
|
||||
@ -1,56 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.service.CommonService;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Removes the stored last position of a user or of all.
|
||||
*/
|
||||
public class PurgeLastPositionCommand implements ExecutableCommand {
|
||||
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
|
||||
@Inject
|
||||
private CommonService commonService;
|
||||
|
||||
@Override
|
||||
public void executeCommand(CommandSender sender, List<String> arguments) {
|
||||
String playerName = arguments.isEmpty() ? sender.getName() : arguments.get(0);
|
||||
|
||||
if ("*".equals(playerName)) {
|
||||
for (PlayerAuth auth : dataSource.getAllAuths()) {
|
||||
resetLastPosition(auth);
|
||||
dataSource.updateQuitLoc(auth);
|
||||
// TODO: send an update when a messaging service will be implemented (QUITLOC)
|
||||
}
|
||||
sender.sendMessage("All players last position locations are now reset");
|
||||
} else {
|
||||
// Get the user auth and make sure the user exists
|
||||
PlayerAuth auth = dataSource.getAuth(playerName);
|
||||
if (auth == null) {
|
||||
commonService.send(sender, MessageKey.UNKNOWN_USER);
|
||||
return;
|
||||
}
|
||||
|
||||
resetLastPosition(auth);
|
||||
dataSource.updateQuitLoc(auth);
|
||||
// TODO: send an update when a messaging service will be implemented (QUITLOC)
|
||||
sender.sendMessage(playerName + "'s last position location is now reset");
|
||||
}
|
||||
}
|
||||
|
||||
private static void resetLastPosition(PlayerAuth auth) {
|
||||
auth.setQuitLocX(0d);
|
||||
auth.setQuitLocY(0d);
|
||||
auth.setQuitLocZ(0d);
|
||||
auth.setWorld("world");
|
||||
}
|
||||
}
|
||||
@ -1,47 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.service.BukkitService;
|
||||
import fr.xephi.authme.task.purge.PurgeExecutor;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import static java.util.Collections.singletonList;
|
||||
|
||||
/**
|
||||
* Command to purge a player.
|
||||
*/
|
||||
public class PurgePlayerCommand implements ExecutableCommand {
|
||||
|
||||
@Inject
|
||||
private PurgeExecutor purgeExecutor;
|
||||
|
||||
@Inject
|
||||
private BukkitService bukkitService;
|
||||
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
|
||||
@Override
|
||||
public void executeCommand(CommandSender sender, List<String> arguments) {
|
||||
String option = arguments.size() > 1 ? arguments.get(1) : null;
|
||||
bukkitService.runTaskAsynchronously(
|
||||
() -> executeCommand(sender, arguments.get(0), option));
|
||||
}
|
||||
|
||||
private void executeCommand(CommandSender sender, String name, String option) {
|
||||
if ("force".equals(option) || !dataSource.isAuthAvailable(name)) {
|
||||
OfflinePlayer offlinePlayer = bukkitService.getOfflinePlayer(name);
|
||||
purgeExecutor.executePurge(singletonList(offlinePlayer), singletonList(name.toLowerCase(Locale.ROOT)));
|
||||
sender.sendMessage("Purged data for player " + name);
|
||||
} else {
|
||||
sender.sendMessage("This player is still registered! Are you sure you want to proceed? "
|
||||
+ "Use '/authme purgeplayer " + name + " force' to run the command anyway");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,55 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
|
||||
import static java.time.Instant.ofEpochMilli;
|
||||
|
||||
/**
|
||||
* Command showing the most recent logged in players.
|
||||
*/
|
||||
public class RecentPlayersCommand implements ExecutableCommand {
|
||||
|
||||
/** DateTime formatter, producing Strings such as "10:42 AM, 11 Jul". */
|
||||
private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("hh:mm a, dd MMM");
|
||||
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
|
||||
@Override
|
||||
public void executeCommand(CommandSender sender, List<String> arguments) {
|
||||
List<PlayerAuth> recentPlayers = dataSource.getRecentlyLoggedInPlayers();
|
||||
|
||||
sender.sendMessage(ChatColor.BLUE + "[AuthMe] Recently logged in players");
|
||||
for (PlayerAuth auth : recentPlayers) {
|
||||
sender.sendMessage(formatPlayerMessage(auth));
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
ZoneId getZoneId() {
|
||||
return ZoneId.systemDefault();
|
||||
}
|
||||
|
||||
private String formatPlayerMessage(PlayerAuth auth) {
|
||||
String lastLoginText;
|
||||
if (auth.getLastLogin() == null) {
|
||||
lastLoginText = "never";
|
||||
} else {
|
||||
LocalDateTime lastLogin = LocalDateTime.ofInstant(ofEpochMilli(auth.getLastLogin()), getZoneId());
|
||||
lastLoginText = DATE_FORMAT.format(lastLogin);
|
||||
}
|
||||
|
||||
return "- " + auth.getRealName() + " (" + lastLoginText + " with IP " + auth.getLastIp() + ")";
|
||||
}
|
||||
}
|
||||
@ -1,85 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.output.ConsoleLoggerFactory;
|
||||
import fr.xephi.authme.security.PasswordSecurity;
|
||||
import fr.xephi.authme.security.crypts.HashedPassword;
|
||||
import fr.xephi.authme.service.BukkitService;
|
||||
import fr.xephi.authme.service.CommonService;
|
||||
import fr.xephi.authme.service.ValidationService;
|
||||
import fr.xephi.authme.service.ValidationService.ValidationResult;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Admin command to register a user.
|
||||
*/
|
||||
public class RegisterAdminCommand implements ExecutableCommand {
|
||||
|
||||
private final ConsoleLogger logger = ConsoleLoggerFactory.get(RegisterAdminCommand.class);
|
||||
|
||||
@Inject
|
||||
private PasswordSecurity passwordSecurity;
|
||||
|
||||
@Inject
|
||||
private CommonService commonService;
|
||||
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
|
||||
@Inject
|
||||
private BukkitService bukkitService;
|
||||
|
||||
@Inject
|
||||
private ValidationService validationService;
|
||||
|
||||
@Override
|
||||
public void executeCommand(final CommandSender sender, List<String> arguments) {
|
||||
// Get the player name and password
|
||||
final String playerName = arguments.get(0);
|
||||
final String playerPass = arguments.get(1);
|
||||
final String playerNameLowerCase = playerName.toLowerCase(Locale.ROOT);
|
||||
|
||||
// Command logic
|
||||
ValidationResult passwordValidation = validationService.validatePassword(playerPass, playerName);
|
||||
if (passwordValidation.hasError()) {
|
||||
commonService.send(sender, passwordValidation.getMessageKey(), passwordValidation.getArgs());
|
||||
return;
|
||||
}
|
||||
|
||||
bukkitService.runTaskOptionallyAsync(() -> {
|
||||
if (dataSource.isAuthAvailable(playerNameLowerCase)) {
|
||||
commonService.send(sender, MessageKey.NAME_ALREADY_REGISTERED);
|
||||
return;
|
||||
}
|
||||
HashedPassword hashedPassword = passwordSecurity.computeHash(playerPass, playerNameLowerCase);
|
||||
PlayerAuth auth = PlayerAuth.builder()
|
||||
.name(playerNameLowerCase)
|
||||
.realName(playerName)
|
||||
.password(hashedPassword)
|
||||
.registrationDate(System.currentTimeMillis())
|
||||
.build();
|
||||
|
||||
if (!dataSource.saveAuth(auth)) {
|
||||
commonService.send(sender, MessageKey.ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
commonService.send(sender, MessageKey.REGISTER_SUCCESS);
|
||||
logger.info(sender.getName() + " registered " + playerName);
|
||||
final Player player = bukkitService.getPlayerExact(playerName);
|
||||
if (player != null) {
|
||||
bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(() ->
|
||||
player.kickPlayer(commonService.retrieveSingleMessage(player, MessageKey.KICK_FOR_ADMIN_REGISTER)));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -1,77 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import ch.jalu.injector.factory.SingletonStore;
|
||||
import fr.xephi.authme.AuthMe;
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.initialization.Reloadable;
|
||||
import fr.xephi.authme.initialization.SettingsDependent;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.output.ConsoleLoggerFactory;
|
||||
import fr.xephi.authme.service.CommonService;
|
||||
import fr.xephi.authme.settings.Settings;
|
||||
import fr.xephi.authme.settings.SettingsWarner;
|
||||
import fr.xephi.authme.settings.properties.DatabaseSettings;
|
||||
import fr.xephi.authme.util.Utils;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The reload command.
|
||||
*/
|
||||
public class ReloadCommand implements ExecutableCommand {
|
||||
|
||||
private final ConsoleLogger logger = ConsoleLoggerFactory.get(ReloadCommand.class);
|
||||
|
||||
@Inject
|
||||
private AuthMe plugin;
|
||||
|
||||
@Inject
|
||||
private Settings settings;
|
||||
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
|
||||
@Inject
|
||||
private CommonService commonService;
|
||||
|
||||
@Inject
|
||||
private SettingsWarner settingsWarner;
|
||||
|
||||
@Inject
|
||||
private SingletonStore<Reloadable> reloadableStore;
|
||||
|
||||
@Inject
|
||||
private SingletonStore<SettingsDependent> settingsDependentStore;
|
||||
|
||||
@Override
|
||||
public void executeCommand(CommandSender sender, List<String> arguments) {
|
||||
try {
|
||||
settings.reload();
|
||||
ConsoleLoggerFactory.reloadSettings(settings);
|
||||
settingsWarner.logWarningsForMisconfigurations();
|
||||
|
||||
// We do not change database type for consistency issues, but we'll output a note in the logs
|
||||
if (!settings.getProperty(DatabaseSettings.BACKEND).equals(dataSource.getType())) {
|
||||
Utils.logAndSendMessage(sender, "Note: cannot change database type during /authme reload");
|
||||
}
|
||||
performReloadOnServices();
|
||||
commonService.send(sender, MessageKey.CONFIG_RELOAD_SUCCESS);
|
||||
} catch (Exception e) {
|
||||
sender.sendMessage("Error occurred during reload of AuthMe: aborting");
|
||||
logger.logException("Aborting! Encountered exception during reload of AuthMe:", e);
|
||||
plugin.stopOrUnload();
|
||||
}
|
||||
}
|
||||
|
||||
private void performReloadOnServices() {
|
||||
reloadableStore.retrieveAllOfType()
|
||||
.forEach(r -> r.reload());
|
||||
|
||||
settingsDependentStore.retrieveAllOfType()
|
||||
.forEach(s -> s.reload(settings));
|
||||
}
|
||||
}
|
||||
@ -1,78 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
import fr.xephi.authme.data.auth.PlayerCache;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.service.BukkitService;
|
||||
import fr.xephi.authme.service.CommonService;
|
||||
import fr.xephi.authme.service.ValidationService;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Admin command for setting an email to an account.
|
||||
*/
|
||||
public class SetEmailCommand implements ExecutableCommand {
|
||||
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
|
||||
@Inject
|
||||
private CommonService commonService;
|
||||
|
||||
@Inject
|
||||
private PlayerCache playerCache;
|
||||
|
||||
@Inject
|
||||
private BukkitService bukkitService;
|
||||
|
||||
@Inject
|
||||
private ValidationService validationService;
|
||||
|
||||
@Override
|
||||
public void executeCommand(final CommandSender sender, List<String> arguments) {
|
||||
// Get the player name and email address
|
||||
final String playerName = arguments.get(0);
|
||||
final String playerEmail = arguments.get(1);
|
||||
|
||||
// Validate the email address
|
||||
if (!validationService.validateEmail(playerEmail)) {
|
||||
commonService.send(sender, MessageKey.INVALID_EMAIL);
|
||||
return;
|
||||
}
|
||||
|
||||
bukkitService.runTaskOptionallyAsync(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// Validate the user
|
||||
PlayerAuth auth = dataSource.getAuth(playerName);
|
||||
if (auth == null) {
|
||||
commonService.send(sender, MessageKey.UNKNOWN_USER);
|
||||
return;
|
||||
} else if (!validationService.isEmailFreeForRegistration(playerEmail, sender)) {
|
||||
commonService.send(sender, MessageKey.EMAIL_ALREADY_USED_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the email address
|
||||
auth.setEmail(playerEmail);
|
||||
if (!dataSource.updateEmail(auth)) {
|
||||
commonService.send(sender, MessageKey.ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the player cache
|
||||
if (playerCache.getAuth(playerName) != null) {
|
||||
playerCache.updatePlayer(auth);
|
||||
}
|
||||
|
||||
// Show a status message
|
||||
commonService.send(sender, MessageKey.EMAIL_CHANGED_SUCCESS);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -1,23 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import fr.xephi.authme.command.PlayerCommand;
|
||||
import fr.xephi.authme.settings.SpawnLoader;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
public class SetFirstSpawnCommand extends PlayerCommand {
|
||||
|
||||
@Inject
|
||||
private SpawnLoader spawnLoader;
|
||||
|
||||
@Override
|
||||
public void runCommand(Player player, List<String> arguments) {
|
||||
if (spawnLoader.setFirstSpawn(player.getLocation())) {
|
||||
player.sendMessage("[AuthMe] Correctly defined new first spawn point");
|
||||
} else {
|
||||
player.sendMessage("[AuthMe] SetFirstSpawn has failed, please retry");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,23 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import fr.xephi.authme.command.PlayerCommand;
|
||||
import fr.xephi.authme.settings.SpawnLoader;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
public class SetSpawnCommand extends PlayerCommand {
|
||||
|
||||
@Inject
|
||||
private SpawnLoader spawnLoader;
|
||||
|
||||
@Override
|
||||
public void runCommand(Player player, List<String> arguments) {
|
||||
if (spawnLoader.setSpawn(player.getLocation())) {
|
||||
player.sendMessage("[AuthMe] Correctly defined new spawn point");
|
||||
} else {
|
||||
player.sendMessage("[AuthMe] SetSpawn has failed, please retry");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,23 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import fr.xephi.authme.command.PlayerCommand;
|
||||
import fr.xephi.authme.settings.SpawnLoader;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
public class SpawnCommand extends PlayerCommand {
|
||||
|
||||
@Inject
|
||||
private SpawnLoader spawnLoader;
|
||||
|
||||
@Override
|
||||
public void runCommand(Player player, List<String> arguments) {
|
||||
if (spawnLoader.getSpawn() == null) {
|
||||
player.sendMessage("[AuthMe] Spawn has failed, please try to define the spawn");
|
||||
} else {
|
||||
player.teleport(spawnLoader.getSpawn());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,52 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import fr.xephi.authme.command.CommandMapper;
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.command.FoundCommandResult;
|
||||
import fr.xephi.authme.command.help.HelpProvider;
|
||||
import fr.xephi.authme.service.AntiBotService;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Display or change the status of the antibot mod.
|
||||
*/
|
||||
public class SwitchAntiBotCommand implements ExecutableCommand {
|
||||
|
||||
@Inject
|
||||
private AntiBotService antiBotService;
|
||||
|
||||
@Inject
|
||||
private CommandMapper commandMapper;
|
||||
|
||||
@Inject
|
||||
private HelpProvider helpProvider;
|
||||
|
||||
@Override
|
||||
public void executeCommand(final CommandSender sender, List<String> arguments) {
|
||||
if (arguments.isEmpty()) {
|
||||
sender.sendMessage("[AuthMe] AntiBot status: " + antiBotService.getAntiBotStatus().name());
|
||||
return;
|
||||
}
|
||||
|
||||
String newState = arguments.get(0);
|
||||
|
||||
// Enable or disable the mod
|
||||
if ("ON".equalsIgnoreCase(newState)) {
|
||||
antiBotService.overrideAntiBotStatus(true);
|
||||
sender.sendMessage("[AuthMe] AntiBot Manual Override: enabled!");
|
||||
} else if ("OFF".equalsIgnoreCase(newState)) {
|
||||
antiBotService.overrideAntiBotStatus(false);
|
||||
sender.sendMessage("[AuthMe] AntiBot Manual Override: disabled!");
|
||||
} else {
|
||||
sender.sendMessage(ChatColor.DARK_RED + "Invalid AntiBot mode!");
|
||||
FoundCommandResult result = commandMapper.mapPartsToCommand(sender, Arrays.asList("authme", "antibot"));
|
||||
helpProvider.outputHelp(sender, result, HelpProvider.SHOW_ARGUMENTS);
|
||||
sender.sendMessage(ChatColor.GOLD + "Detailed help: " + ChatColor.WHITE + "/authme help antibot");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,61 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.message.Messages;
|
||||
import fr.xephi.authme.output.ConsoleLoggerFactory;
|
||||
import fr.xephi.authme.service.BukkitService;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Command to disable two-factor authentication for a user.
|
||||
*/
|
||||
public class TotpDisableAdminCommand implements ExecutableCommand {
|
||||
|
||||
private final ConsoleLogger logger = ConsoleLoggerFactory.get(TotpDisableAdminCommand.class);
|
||||
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
|
||||
@Inject
|
||||
private Messages messages;
|
||||
|
||||
@Inject
|
||||
private BukkitService bukkitService;
|
||||
|
||||
@Override
|
||||
public void executeCommand(CommandSender sender, List<String> arguments) {
|
||||
String player = arguments.get(0);
|
||||
|
||||
PlayerAuth auth = dataSource.getAuth(player);
|
||||
if (auth == null) {
|
||||
messages.send(sender, MessageKey.UNKNOWN_USER);
|
||||
} else if (auth.getTotpKey() == null) {
|
||||
sender.sendMessage(ChatColor.RED + "Player '" + player + "' does not have two-factor auth enabled");
|
||||
} else {
|
||||
removeTotpKey(sender, player);
|
||||
}
|
||||
}
|
||||
|
||||
private void removeTotpKey(CommandSender sender, String player) {
|
||||
if (dataSource.removeTotpKey(player)) {
|
||||
sender.sendMessage("Disabled two-factor authentication successfully for '" + player + "'");
|
||||
logger.info(sender.getName() + " disable two-factor authentication for '" + player + "'");
|
||||
|
||||
Player onlinePlayer = bukkitService.getPlayerExact(player);
|
||||
if (onlinePlayer != null) {
|
||||
messages.send(onlinePlayer, MessageKey.TWO_FACTOR_REMOVED_SUCCESS);
|
||||
}
|
||||
} else {
|
||||
messages.send(sender, MessageKey.ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,38 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.message.Messages;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Command to see whether a user has enabled two-factor authentication.
|
||||
*/
|
||||
public class TotpViewStatusCommand implements ExecutableCommand {
|
||||
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
|
||||
@Inject
|
||||
private Messages messages;
|
||||
|
||||
@Override
|
||||
public void executeCommand(CommandSender sender, List<String> arguments) {
|
||||
String player = arguments.get(0);
|
||||
|
||||
PlayerAuth auth = dataSource.getAuth(player);
|
||||
if (auth == null) {
|
||||
messages.send(sender, MessageKey.UNKNOWN_USER);
|
||||
} else if (auth.getTotpKey() == null) {
|
||||
sender.sendMessage(ChatColor.RED + "Player '" + player + "' does NOT have two-factor auth enabled");
|
||||
} else {
|
||||
sender.sendMessage(ChatColor.DARK_GREEN + "Player '" + player + "' has enabled two-factor authentication");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,49 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.process.Management;
|
||||
import fr.xephi.authme.service.BukkitService;
|
||||
import fr.xephi.authme.service.CommonService;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Admin command to unregister a player.
|
||||
*/
|
||||
public class UnregisterAdminCommand implements ExecutableCommand {
|
||||
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
|
||||
@Inject
|
||||
private CommonService commonService;
|
||||
|
||||
@Inject
|
||||
private BukkitService bukkitService;
|
||||
|
||||
@Inject
|
||||
private Management management;
|
||||
|
||||
UnregisterAdminCommand() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void executeCommand(final CommandSender sender, List<String> arguments) {
|
||||
String playerName = arguments.get(0);
|
||||
|
||||
// Make sure the user exists
|
||||
if (!dataSource.isAuthAvailable(playerName)) {
|
||||
commonService.send(sender, MessageKey.UNKNOWN_USER);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the player from the server and perform unregister
|
||||
Player target = bukkitService.getPlayerExact(playerName);
|
||||
management.performUnregisterByAdmin(sender, playerName, target);
|
||||
}
|
||||
}
|
||||
@ -1,39 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.command.help.HelpMessagesService;
|
||||
import fr.xephi.authme.output.ConsoleLoggerFactory;
|
||||
import fr.xephi.authme.service.HelpTranslationGenerator;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Messages command, updates the user's help messages file with any missing files
|
||||
* from the provided file in the JAR.
|
||||
*/
|
||||
public class UpdateHelpMessagesCommand implements ExecutableCommand {
|
||||
|
||||
private final ConsoleLogger logger = ConsoleLoggerFactory.get(UpdateHelpMessagesCommand.class);
|
||||
|
||||
@Inject
|
||||
private HelpTranslationGenerator helpTranslationGenerator;
|
||||
@Inject
|
||||
private HelpMessagesService helpMessagesService;
|
||||
|
||||
@Override
|
||||
public void executeCommand(CommandSender sender, List<String> arguments) {
|
||||
try {
|
||||
File updatedFile = helpTranslationGenerator.updateHelpFile();
|
||||
sender.sendMessage("Successfully updated the help file '" + updatedFile.getName() + "'");
|
||||
helpMessagesService.reloadMessagesFile();
|
||||
} catch (IOException e) {
|
||||
sender.sendMessage("Could not update help file: " + e.getMessage());
|
||||
logger.logException("Could not update help file:", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,94 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import fr.xephi.authme.AuthMe;
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.service.BukkitService;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
public class VersionCommand implements ExecutableCommand {
|
||||
|
||||
@Inject
|
||||
private BukkitService bukkitService;
|
||||
|
||||
@Override
|
||||
public void executeCommand(CommandSender sender, List<String> arguments) {
|
||||
// Show some version info
|
||||
sender.sendMessage(ChatColor.GOLD + "==========[ " + AuthMe.getPluginName() + " ABOUT ]==========");
|
||||
sender.sendMessage(ChatColor.GOLD + "Version: " + ChatColor.WHITE + AuthMe.getPluginName()
|
||||
+ " v" + AuthMe.getPluginVersion() + ChatColor.GRAY + " (build: " + AuthMe.getPluginBuildNumber() + ")");
|
||||
sender.sendMessage(ChatColor.GOLD + "Authors:");
|
||||
Collection<Player> onlinePlayers = bukkitService.getOnlinePlayers();
|
||||
printDeveloper(sender, "Gabriele C.", "sgdc3", "Project manager, Contributor", onlinePlayers);
|
||||
printDeveloper(sender, "Lucas J.", "ljacqu", "Main Developer", onlinePlayers);
|
||||
printDeveloper(sender, "games647", "games647", "Developer", onlinePlayers);
|
||||
printDeveloper(sender, "Hex3l", "Hex3l", "Developer", onlinePlayers);
|
||||
printDeveloper(sender, "krusic22", "krusic22", "Support", onlinePlayers);
|
||||
sender.sendMessage(ChatColor.GOLD + "Retired authors:");
|
||||
printDeveloper(sender, "Alexandre Vanhecke", "xephi59", "Original Author", onlinePlayers);
|
||||
printDeveloper(sender, "Gnat008", "gnat008", "Developer, Retired", onlinePlayers);
|
||||
printDeveloper(sender, "DNx5", "DNx5", "Developer, Retired", onlinePlayers);
|
||||
printDeveloper(sender, "Tim Visee", "timvisee", "Developer, Retired", onlinePlayers);
|
||||
sender.sendMessage(ChatColor.GOLD + "Website: " + ChatColor.WHITE
|
||||
+ "https://github.com/AuthMe/AuthMeReloaded");
|
||||
sender.sendMessage(ChatColor.GOLD + "License: " + ChatColor.WHITE + "GNU GPL v3.0"
|
||||
+ ChatColor.GRAY + ChatColor.ITALIC + " (See LICENSE file)");
|
||||
sender.sendMessage(ChatColor.GOLD + "Copyright: " + ChatColor.WHITE
|
||||
+ "Copyright (c) AuthMe-Team " + new SimpleDateFormat("yyyy").format(new Date())
|
||||
+ ". Released under GPL v3 License.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Print a developer with proper styling.
|
||||
*
|
||||
* @param sender The command sender
|
||||
* @param name The display name of the developer
|
||||
* @param minecraftName The Minecraft username of the developer, if available
|
||||
* @param function The function of the developer
|
||||
* @param onlinePlayers The list of online players
|
||||
*/
|
||||
private static void printDeveloper(CommandSender sender, String name, String minecraftName, String function,
|
||||
Collection<Player> onlinePlayers) {
|
||||
// Print the name
|
||||
StringBuilder msg = new StringBuilder();
|
||||
msg.append(" ")
|
||||
.append(ChatColor.WHITE)
|
||||
.append(name);
|
||||
|
||||
// Append the Minecraft name
|
||||
msg.append(ChatColor.GRAY).append(" // ").append(ChatColor.WHITE).append(minecraftName);
|
||||
msg.append(ChatColor.GRAY).append(ChatColor.ITALIC).append(" (").append(function).append(")");
|
||||
|
||||
// Show the online status
|
||||
if (isPlayerOnline(minecraftName, onlinePlayers)) {
|
||||
msg.append(ChatColor.GREEN).append(ChatColor.ITALIC).append(" (In-Game)");
|
||||
}
|
||||
|
||||
// Print the message
|
||||
sender.sendMessage(msg.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a player is online.
|
||||
*
|
||||
* @param minecraftName The Minecraft player name
|
||||
* @param onlinePlayers List of online players
|
||||
*
|
||||
* @return True if the player is online, false otherwise
|
||||
*/
|
||||
private static boolean isPlayerOnline(String minecraftName, Collection<Player> onlinePlayers) {
|
||||
for (Player player : onlinePlayers) {
|
||||
if (player.getName().equalsIgnoreCase(minecraftName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -1,40 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.service.WhiteListService;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Display or change the status of the antibot mod.
|
||||
*/
|
||||
public class WhitelistCommand implements ExecutableCommand {
|
||||
|
||||
@Inject
|
||||
private WhiteListService whiteListService;
|
||||
|
||||
|
||||
@Override
|
||||
public void executeCommand(final CommandSender sender, List<String> arguments) {
|
||||
if (arguments.isEmpty()) {
|
||||
sender.sendMessage("[AuthMe] WhiteList: " + whiteListService.getWhiteListStatus().name());
|
||||
return;
|
||||
}
|
||||
|
||||
String newState = arguments.get(0);
|
||||
|
||||
// Enable or disable the mod
|
||||
if ("ON".equalsIgnoreCase(newState)) {
|
||||
whiteListService.overrideWhiteListStatus(true);
|
||||
sender.sendMessage("[AuthMe] WhiteList enabled!");
|
||||
} else if ("OFF".equalsIgnoreCase(newState)) {
|
||||
sender.sendMessage("[AuthMe] WhiteList disabled!");
|
||||
whiteListService.overrideWhiteListStatus(false);
|
||||
} else {
|
||||
sender.sendMessage(ChatColor.GOLD + "Detailed help: " + ChatColor.WHITE + "/authme help whitelist");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,88 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.authme.debug;
|
||||
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.permission.DebugSectionPermissions;
|
||||
import fr.xephi.authme.permission.PermissionNode;
|
||||
import fr.xephi.authme.service.GeoIpService;
|
||||
import fr.xephi.authme.service.ValidationService;
|
||||
import fr.xephi.authme.settings.properties.ProtectionSettings;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Shows the GeoIP information as returned by the geoIpService.
|
||||
*/
|
||||
class CountryLookup implements DebugSection {
|
||||
|
||||
private static final Pattern IS_IP_ADDR = Pattern.compile("(\\d{1,3}\\.){3}\\d{1,3}");
|
||||
|
||||
@Inject
|
||||
private GeoIpService geoIpService;
|
||||
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
|
||||
@Inject
|
||||
private ValidationService validationService;
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "cty";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Check country protection / country data";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandSender sender, List<String> arguments) {
|
||||
sender.sendMessage(ChatColor.BLUE + "AuthMe country lookup");
|
||||
if (arguments.isEmpty()) {
|
||||
sender.sendMessage("Check player: /authme debug cty Bobby");
|
||||
sender.sendMessage("Check IP address: /authme debug cty 127.123.45.67");
|
||||
return;
|
||||
}
|
||||
|
||||
String argument = arguments.get(0);
|
||||
if (IS_IP_ADDR.matcher(argument).matches()) {
|
||||
outputInfoForIpAddr(sender, argument);
|
||||
} else {
|
||||
outputInfoForPlayer(sender, argument);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PermissionNode getRequiredPermission() {
|
||||
return DebugSectionPermissions.COUNTRY_LOOKUP;
|
||||
}
|
||||
|
||||
private void outputInfoForIpAddr(CommandSender sender, String ipAddr) {
|
||||
sender.sendMessage("IP '" + ipAddr + "' maps to country '" + geoIpService.getCountryCode(ipAddr)
|
||||
+ "' (" + geoIpService.getCountryName(ipAddr) + ")");
|
||||
if (validationService.isCountryAdmitted(ipAddr)) {
|
||||
sender.sendMessage(ChatColor.DARK_GREEN + "This IP address' country is not blocked");
|
||||
} else {
|
||||
sender.sendMessage(ChatColor.DARK_RED + "This IP address' country is blocked from the server");
|
||||
}
|
||||
sender.sendMessage("Note: if " + ProtectionSettings.ENABLE_PROTECTION + " is false no country is blocked");
|
||||
}
|
||||
|
||||
// TODO #1366: Extend with registration IP?
|
||||
private void outputInfoForPlayer(CommandSender sender, String name) {
|
||||
PlayerAuth auth = dataSource.getAuth(name);
|
||||
if (auth == null) {
|
||||
sender.sendMessage("No player with name '" + name + "'");
|
||||
} else if (auth.getLastIp() == null) {
|
||||
sender.sendMessage("No last IP address known for '" + name + "'");
|
||||
} else {
|
||||
sender.sendMessage("Player '" + name + "' has IP address " + auth.getLastIp());
|
||||
outputInfoForIpAddr(sender, auth.getLastIp());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,81 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.authme.debug;
|
||||
|
||||
import ch.jalu.injector.factory.SingletonStore;
|
||||
import fr.xephi.authme.data.auth.PlayerCache;
|
||||
import fr.xephi.authme.data.limbo.LimboService;
|
||||
import fr.xephi.authme.datasource.CacheDataSource;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.initialization.HasCleanup;
|
||||
import fr.xephi.authme.initialization.Reloadable;
|
||||
import fr.xephi.authme.initialization.SettingsDependent;
|
||||
import fr.xephi.authme.output.ConsoleLoggerFactory;
|
||||
import fr.xephi.authme.permission.DebugSectionPermissions;
|
||||
import fr.xephi.authme.permission.PermissionNode;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static fr.xephi.authme.command.executable.authme.debug.DebugSectionUtils.applyToLimboPlayersMap;
|
||||
|
||||
/**
|
||||
* Fetches various statistics, particularly regarding in-memory data that is stored.
|
||||
*/
|
||||
class DataStatistics implements DebugSection {
|
||||
|
||||
@Inject
|
||||
private PlayerCache playerCache;
|
||||
|
||||
@Inject
|
||||
private LimboService limboService;
|
||||
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
|
||||
@Inject
|
||||
private SingletonStore<Object> singletonStore;
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "stats";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Outputs general data statistics";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandSender sender, List<String> arguments) {
|
||||
sender.sendMessage(ChatColor.BLUE + "AuthMe statistics");
|
||||
sender.sendMessage("LimboPlayers in memory: " + applyToLimboPlayersMap(limboService, Map::size));
|
||||
sender.sendMessage("PlayerCache size: " + playerCache.getLogged() + " (= logged in players)");
|
||||
|
||||
outputDatabaseStats(sender);
|
||||
outputInjectorStats(sender);
|
||||
sender.sendMessage("Total logger instances: " + ConsoleLoggerFactory.getTotalLoggers());
|
||||
}
|
||||
|
||||
@Override
|
||||
public PermissionNode getRequiredPermission() {
|
||||
return DebugSectionPermissions.DATA_STATISTICS;
|
||||
}
|
||||
|
||||
private void outputDatabaseStats(CommandSender sender) {
|
||||
sender.sendMessage("Total players in DB: " + dataSource.getAccountsRegistered());
|
||||
if (dataSource instanceof CacheDataSource) {
|
||||
CacheDataSource cacheDataSource = (CacheDataSource) this.dataSource;
|
||||
sender.sendMessage("Cached PlayerAuth objects: " + cacheDataSource.getCachedAuths().size());
|
||||
}
|
||||
}
|
||||
|
||||
private void outputInjectorStats(CommandSender sender) {
|
||||
sender.sendMessage("Singleton Java classes: " + singletonStore.retrieveAllOfType().size());
|
||||
sender.sendMessage(String.format("(Reloadable: %d / SettingsDependent: %d / HasCleanup: %d)",
|
||||
singletonStore.retrieveAllOfType(Reloadable.class).size(),
|
||||
singletonStore.retrieveAllOfType(SettingsDependent.class).size(),
|
||||
singletonStore.retrieveAllOfType(HasCleanup.class).size()));
|
||||
}
|
||||
}
|
||||
@ -1,85 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.authme.debug;
|
||||
|
||||
import ch.jalu.injector.factory.Factory;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.permission.PermissionsManager;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
|
||||
/**
|
||||
* Debug command main.
|
||||
*/
|
||||
public class DebugCommand implements ExecutableCommand {
|
||||
|
||||
private static final Set<Class<? extends DebugSection>> SECTION_CLASSES = ImmutableSet.of(
|
||||
PermissionGroups.class, DataStatistics.class, CountryLookup.class, PlayerAuthViewer.class, InputValidator.class,
|
||||
LimboPlayerViewer.class, CountryLookup.class, HasPermissionChecker.class, TestEmailSender.class,
|
||||
SpawnLocationViewer.class, MySqlDefaultChanger.class);
|
||||
|
||||
@Inject
|
||||
private Factory<DebugSection> debugSectionFactory;
|
||||
|
||||
@Inject
|
||||
private PermissionsManager permissionsManager;
|
||||
|
||||
private Map<String, DebugSection> sections;
|
||||
|
||||
@Override
|
||||
public void executeCommand(CommandSender sender, List<String> arguments) {
|
||||
DebugSection debugSection = findDebugSection(arguments);
|
||||
if (debugSection == null) {
|
||||
sendAvailableSections(sender);
|
||||
} else {
|
||||
executeSection(debugSection, sender, arguments);
|
||||
}
|
||||
}
|
||||
|
||||
private DebugSection findDebugSection(List<String> arguments) {
|
||||
if (arguments.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return getSections().get(arguments.get(0).toLowerCase(Locale.ROOT));
|
||||
}
|
||||
|
||||
private void sendAvailableSections(CommandSender sender) {
|
||||
sender.sendMessage(ChatColor.BLUE + "AuthMe debug utils");
|
||||
sender.sendMessage("Sections available to you:");
|
||||
long availableSections = getSections().values().stream()
|
||||
.filter(section -> permissionsManager.hasPermission(sender, section.getRequiredPermission()))
|
||||
.peek(e -> sender.sendMessage("- " + e.getName() + ": " + e.getDescription()))
|
||||
.count();
|
||||
|
||||
if (availableSections == 0) {
|
||||
sender.sendMessage(ChatColor.RED + "You don't have permission to view any debug section");
|
||||
}
|
||||
}
|
||||
|
||||
private void executeSection(DebugSection section, CommandSender sender, List<String> arguments) {
|
||||
if (permissionsManager.hasPermission(sender, section.getRequiredPermission())) {
|
||||
section.execute(sender, arguments.subList(1, arguments.size()));
|
||||
} else {
|
||||
sender.sendMessage(ChatColor.RED + "You don't have permission for this section. See /authme debug");
|
||||
}
|
||||
}
|
||||
|
||||
// Lazy getter
|
||||
private Map<String, DebugSection> getSections() {
|
||||
if (sections == null) {
|
||||
Map<String, DebugSection> sections = new TreeMap<>();
|
||||
for (Class<? extends DebugSection> sectionClass : SECTION_CLASSES) {
|
||||
DebugSection section = debugSectionFactory.newInstance(sectionClass);
|
||||
sections.put(section.getName(), section);
|
||||
}
|
||||
this.sections = sections;
|
||||
}
|
||||
return sections;
|
||||
}
|
||||
}
|
||||
@ -1,36 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.authme.debug;
|
||||
|
||||
import fr.xephi.authme.permission.PermissionNode;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A debug section: "child" command of the debug command.
|
||||
*/
|
||||
interface DebugSection {
|
||||
|
||||
/**
|
||||
* @return the name to get to this child command
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* @return short description of the child command
|
||||
*/
|
||||
String getDescription();
|
||||
|
||||
/**
|
||||
* Executes the debug child command.
|
||||
*
|
||||
* @param sender the sender executing the command
|
||||
* @param arguments the arguments, without the label of the child command
|
||||
*/
|
||||
void execute(CommandSender sender, List<String> arguments);
|
||||
|
||||
/**
|
||||
* @return permission required to run this section
|
||||
*/
|
||||
PermissionNode getRequiredPermission();
|
||||
|
||||
}
|
||||
@ -1,130 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.authme.debug;
|
||||
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.data.limbo.LimboService;
|
||||
import fr.xephi.authme.datasource.CacheDataSource;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.output.ConsoleLoggerFactory;
|
||||
import org.bukkit.Location;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.math.RoundingMode;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.DecimalFormatSymbols;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Utilities used within the DebugSection implementations.
|
||||
*/
|
||||
final class DebugSectionUtils {
|
||||
|
||||
private static ConsoleLogger logger = ConsoleLoggerFactory.get(DebugSectionUtils.class);
|
||||
private static Field limboEntriesField;
|
||||
|
||||
private DebugSectionUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the given location in a human readable way. Null-safe.
|
||||
*
|
||||
* @param location the location to format
|
||||
* @return the formatted location
|
||||
*/
|
||||
static String formatLocation(Location location) {
|
||||
if (location == null) {
|
||||
return "null";
|
||||
}
|
||||
|
||||
String worldName = location.getWorld() == null ? "null" : location.getWorld().getName();
|
||||
return formatLocation(location.getX(), location.getY(), location.getZ(), worldName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the given location in a human readable way.
|
||||
*
|
||||
* @param x the x coordinate
|
||||
* @param y the y coordinate
|
||||
* @param z the z coordinate
|
||||
* @param world the world name
|
||||
* @return the formatted location
|
||||
*/
|
||||
static String formatLocation(double x, double y, double z, String world) {
|
||||
return "(" + round(x) + ", " + round(y) + ", " + round(z) + ") in '" + world + "'";
|
||||
}
|
||||
|
||||
/**
|
||||
* Rounds the given number to two decimals.
|
||||
*
|
||||
* @param number the number to round
|
||||
* @return the rounded number
|
||||
*/
|
||||
private static String round(double number) {
|
||||
DecimalFormat df = new DecimalFormat("#.##");
|
||||
df.setDecimalFormatSymbols(DecimalFormatSymbols.getInstance(Locale.US));
|
||||
df.setRoundingMode(RoundingMode.HALF_UP);
|
||||
return df.format(number);
|
||||
}
|
||||
|
||||
private static Field getLimboPlayerEntriesField() {
|
||||
if (limboEntriesField == null) {
|
||||
try {
|
||||
Field field = LimboService.class.getDeclaredField("entries");
|
||||
field.setAccessible(true);
|
||||
limboEntriesField = field;
|
||||
} catch (Exception e) {
|
||||
logger.logException("Could not retrieve LimboService entries field:", e);
|
||||
}
|
||||
}
|
||||
return limboEntriesField;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the given function to the map in LimboService containing the LimboPlayers.
|
||||
* As we don't want to expose this information in non-debug settings, this is done with reflection.
|
||||
* Exceptions are generously caught and {@code null} is returned on failure.
|
||||
*
|
||||
* @param limboService the limbo service instance to get the map from
|
||||
* @param function the function to apply to the map
|
||||
* @param <U> the result type of the function
|
||||
*
|
||||
* @return the value of the function applied to the map, or null upon error
|
||||
*/
|
||||
static <U> U applyToLimboPlayersMap(LimboService limboService, Function<Map, U> function) {
|
||||
Field limboPlayerEntriesField = getLimboPlayerEntriesField();
|
||||
if (limboPlayerEntriesField != null) {
|
||||
try {
|
||||
return function.apply((Map) limboEntriesField.get(limboService));
|
||||
} catch (Exception e) {
|
||||
logger.logException("Could not retrieve LimboService values:", e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static <T> T castToTypeOrNull(Object object, Class<T> clazz) {
|
||||
return clazz.isInstance(object) ? clazz.cast(object) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unwraps the "cache data source" and returns the underlying source. Returns the
|
||||
* same as the input argument otherwise.
|
||||
*
|
||||
* @param dataSource the data source to unwrap if applicable
|
||||
* @return the non-cache data source
|
||||
*/
|
||||
static DataSource unwrapSourceFromCacheDataSource(DataSource dataSource) {
|
||||
if (dataSource instanceof CacheDataSource) {
|
||||
try {
|
||||
Field source = CacheDataSource.class.getDeclaredField("source");
|
||||
source.setAccessible(true);
|
||||
return (DataSource) source.get(dataSource);
|
||||
} catch (NoSuchFieldException | IllegalAccessException e) {
|
||||
logger.logException("Could not get source of CacheDataSource:", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return dataSource;
|
||||
}
|
||||
}
|
||||
@ -1,138 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.authme.debug;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import fr.xephi.authme.permission.AdminPermission;
|
||||
import fr.xephi.authme.permission.DebugSectionPermissions;
|
||||
import fr.xephi.authme.permission.DefaultPermission;
|
||||
import fr.xephi.authme.permission.PermissionNode;
|
||||
import fr.xephi.authme.permission.PermissionsManager;
|
||||
import fr.xephi.authme.permission.PlayerPermission;
|
||||
import fr.xephi.authme.permission.PlayerStatePermission;
|
||||
import fr.xephi.authme.service.BukkitService;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
/**
|
||||
* Checks if a player has a given permission, as checked by AuthMe.
|
||||
*/
|
||||
class HasPermissionChecker implements DebugSection {
|
||||
|
||||
static final List<Class<? extends PermissionNode>> PERMISSION_NODE_CLASSES = ImmutableList.of(
|
||||
AdminPermission.class, PlayerPermission.class, PlayerStatePermission.class, DebugSectionPermissions.class);
|
||||
|
||||
@Inject
|
||||
private PermissionsManager permissionsManager;
|
||||
|
||||
@Inject
|
||||
private BukkitService bukkitService;
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "perm";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Checks if a player has a given permission";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandSender sender, List<String> arguments) {
|
||||
sender.sendMessage(ChatColor.BLUE + "AuthMe permission check");
|
||||
if (arguments.size() < 2) {
|
||||
sender.sendMessage("Check if a player has permission:");
|
||||
sender.sendMessage("Example: /authme debug perm bobby my.perm.node");
|
||||
sender.sendMessage("Permission system type used: " + permissionsManager.getPermissionSystem());
|
||||
return;
|
||||
}
|
||||
|
||||
final String playerName = arguments.get(0);
|
||||
final String permissionNode = arguments.get(1);
|
||||
|
||||
Player player = bukkitService.getPlayerExact(playerName);
|
||||
if (player == null) {
|
||||
OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(playerName);
|
||||
if (offlinePlayer == null) {
|
||||
sender.sendMessage(ChatColor.DARK_RED + "Player '" + playerName + "' does not exist");
|
||||
} else {
|
||||
sender.sendMessage("Player '" + playerName + "' not online; checking with offline player");
|
||||
performPermissionCheck(offlinePlayer, permissionNode, permissionsManager::hasPermissionOffline, sender);
|
||||
}
|
||||
} else {
|
||||
performPermissionCheck(player, permissionNode, permissionsManager::hasPermission, sender);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PermissionNode getRequiredPermission() {
|
||||
return DebugSectionPermissions.HAS_PERMISSION_CHECK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a permission check and informs the given sender of the result. {@code permissionChecker} is the
|
||||
* permission check to perform with the given {@code node} and the {@code player}.
|
||||
*
|
||||
* @param player the player to check a permission for
|
||||
* @param node the node of the permission to check
|
||||
* @param permissionChecker permission checking function
|
||||
* @param sender the sender to inform of the result
|
||||
* @param <P> the player type
|
||||
*/
|
||||
private static <P extends OfflinePlayer> void performPermissionCheck(
|
||||
P player, String node, BiFunction<P, PermissionNode, Boolean> permissionChecker, CommandSender sender) {
|
||||
|
||||
PermissionNode permNode = getPermissionNode(sender, node);
|
||||
if (permissionChecker.apply(player, permNode)) {
|
||||
sender.sendMessage(ChatColor.DARK_GREEN + "Success: player '" + player.getName()
|
||||
+ "' has permission '" + node + "'");
|
||||
} else {
|
||||
sender.sendMessage(ChatColor.DARK_RED + "Check failed: player '" + player.getName()
|
||||
+ "' does NOT have permission '" + node + "'");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Based on the given permission node (String), tries to find the according AuthMe {@link PermissionNode}
|
||||
* instance, or creates a new one if not available.
|
||||
*
|
||||
* @param sender the sender (used to inform him if no AuthMe PermissionNode can be matched)
|
||||
* @param node the node to search for
|
||||
* @return the node as {@link PermissionNode} object
|
||||
*/
|
||||
private static PermissionNode getPermissionNode(CommandSender sender, String node) {
|
||||
Optional<? extends PermissionNode> permNode = PERMISSION_NODE_CLASSES.stream()
|
||||
.map(Class::getEnumConstants)
|
||||
.flatMap(Arrays::stream)
|
||||
.filter(perm -> perm.getNode().equals(node))
|
||||
.findFirst();
|
||||
if (permNode.isPresent()) {
|
||||
return permNode.get();
|
||||
} else {
|
||||
sender.sendMessage("Did not detect AuthMe permission; using default permission = DENIED");
|
||||
return createPermNode(node);
|
||||
}
|
||||
}
|
||||
|
||||
private static PermissionNode createPermNode(String node) {
|
||||
return new PermissionNode() {
|
||||
@Override
|
||||
public String getNode() {
|
||||
return node;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultPermission getDefaultPermission() {
|
||||
return DefaultPermission.NOT_ALLOWED;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -1,124 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.authme.debug;
|
||||
|
||||
import fr.xephi.authme.listener.FailedVerificationException;
|
||||
import fr.xephi.authme.listener.OnJoinVerifier;
|
||||
import fr.xephi.authme.message.Messages;
|
||||
import fr.xephi.authme.permission.DebugSectionPermissions;
|
||||
import fr.xephi.authme.permission.PermissionNode;
|
||||
import fr.xephi.authme.service.ValidationService;
|
||||
import fr.xephi.authme.service.ValidationService.ValidationResult;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static fr.xephi.authme.command.executable.authme.debug.InputValidator.ValidationObject.MAIL;
|
||||
import static fr.xephi.authme.command.executable.authme.debug.InputValidator.ValidationObject.NAME;
|
||||
import static fr.xephi.authme.command.executable.authme.debug.InputValidator.ValidationObject.PASS;
|
||||
|
||||
/**
|
||||
* Checks if a sample username, email or password is valid according to the AuthMe settings.
|
||||
*/
|
||||
class InputValidator implements DebugSection {
|
||||
|
||||
@Inject
|
||||
private ValidationService validationService;
|
||||
|
||||
@Inject
|
||||
private Messages messages;
|
||||
|
||||
@Inject
|
||||
private OnJoinVerifier onJoinVerifier;
|
||||
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "valid";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Checks if your config.yml allows a password / email";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandSender sender, List<String> arguments) {
|
||||
if (arguments.size() < 2 || !ValidationObject.matchesAny(arguments.get(0))) {
|
||||
displayUsageHint(sender);
|
||||
|
||||
} else if (PASS.matches(arguments.get(0))) {
|
||||
validatePassword(sender, arguments.get(1));
|
||||
|
||||
} else if (MAIL.matches(arguments.get(0))) {
|
||||
validateEmail(sender, arguments.get(1));
|
||||
|
||||
} else if (NAME.matches(arguments.get(0))) {
|
||||
validateUsername(sender, arguments.get(1));
|
||||
|
||||
} else {
|
||||
throw new IllegalStateException("Unexpected validation object with arg[0] = '" + arguments.get(0) + "'");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PermissionNode getRequiredPermission() {
|
||||
return DebugSectionPermissions.INPUT_VALIDATOR;
|
||||
}
|
||||
|
||||
private void displayUsageHint(CommandSender sender) {
|
||||
sender.sendMessage(ChatColor.BLUE + "Validation tests");
|
||||
sender.sendMessage("You can define forbidden emails and passwords in your config.yml."
|
||||
+ " You can test your settings with this command.");
|
||||
final String command = ChatColor.GOLD + "/authme debug valid";
|
||||
sender.sendMessage(" Use " + command + " pass <pass>" + ChatColor.RESET + " to check a password");
|
||||
sender.sendMessage(" Use " + command + " mail <mail>" + ChatColor.RESET + " to check an email");
|
||||
sender.sendMessage(" Use " + command + " name <name>" + ChatColor.RESET + " to check a username");
|
||||
}
|
||||
|
||||
private void validatePassword(CommandSender sender, String password) {
|
||||
ValidationResult validationResult = validationService.validatePassword(password, "");
|
||||
sender.sendMessage(ChatColor.BLUE + "Validation of password '" + password + "'");
|
||||
if (validationResult.hasError()) {
|
||||
messages.send(sender, validationResult.getMessageKey(), validationResult.getArgs());
|
||||
} else {
|
||||
sender.sendMessage(ChatColor.DARK_GREEN + "Valid password!");
|
||||
}
|
||||
}
|
||||
|
||||
private void validateEmail(CommandSender sender, String email) {
|
||||
boolean isValidEmail = validationService.validateEmail(email);
|
||||
sender.sendMessage(ChatColor.BLUE + "Validation of email '" + email + "'");
|
||||
if (isValidEmail) {
|
||||
sender.sendMessage(ChatColor.DARK_GREEN + "Valid email!");
|
||||
} else {
|
||||
sender.sendMessage(ChatColor.DARK_RED + "Email is not valid!");
|
||||
}
|
||||
}
|
||||
|
||||
private void validateUsername(CommandSender sender, String username) {
|
||||
sender.sendMessage(ChatColor.BLUE + "Validation of username '" + username + "'");
|
||||
try {
|
||||
onJoinVerifier.checkIsValidName(username);
|
||||
sender.sendMessage("Valid username!");
|
||||
} catch (FailedVerificationException failedVerificationEx) {
|
||||
messages.send(sender, failedVerificationEx.getReason(), failedVerificationEx.getArgs());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
enum ValidationObject {
|
||||
|
||||
PASS, MAIL, NAME;
|
||||
|
||||
static boolean matchesAny(String arg) {
|
||||
return Arrays.stream(values()).anyMatch(vo -> vo.matches(arg));
|
||||
}
|
||||
|
||||
boolean matches(String arg) {
|
||||
return name().equalsIgnoreCase(arg);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,143 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.authme.debug;
|
||||
|
||||
import fr.xephi.authme.data.limbo.LimboPlayer;
|
||||
import fr.xephi.authme.data.limbo.LimboService;
|
||||
import fr.xephi.authme.data.limbo.persistence.LimboPersistence;
|
||||
import fr.xephi.authme.permission.DebugSectionPermissions;
|
||||
import fr.xephi.authme.permission.PermissionNode;
|
||||
import fr.xephi.authme.permission.PermissionsManager;
|
||||
import fr.xephi.authme.service.BukkitService;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static fr.xephi.authme.command.executable.authme.debug.DebugSectionUtils.applyToLimboPlayersMap;
|
||||
import static fr.xephi.authme.command.executable.authme.debug.DebugSectionUtils.formatLocation;
|
||||
|
||||
/**
|
||||
* Shows the data stored in LimboPlayers and the equivalent properties on online players.
|
||||
*/
|
||||
class LimboPlayerViewer implements DebugSection {
|
||||
|
||||
@Inject
|
||||
private LimboService limboService;
|
||||
|
||||
@Inject
|
||||
private LimboPersistence limboPersistence;
|
||||
|
||||
@Inject
|
||||
private BukkitService bukkitService;
|
||||
|
||||
@Inject
|
||||
private PermissionsManager permissionsManager;
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "limbo";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "View LimboPlayers and player's \"limbo stats\"";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandSender sender, List<String> arguments) {
|
||||
if (arguments.isEmpty()) {
|
||||
sender.sendMessage(ChatColor.BLUE + "AuthMe limbo viewer");
|
||||
sender.sendMessage("/authme debug limbo <player>: show a player's limbo info");
|
||||
sender.sendMessage("Available limbo records: " + applyToLimboPlayersMap(limboService, Map::keySet));
|
||||
return;
|
||||
}
|
||||
|
||||
LimboPlayer memoryLimbo = limboService.getLimboPlayer(arguments.get(0));
|
||||
Player player = bukkitService.getPlayerExact(arguments.get(0));
|
||||
LimboPlayer diskLimbo = player != null ? limboPersistence.getLimboPlayer(player) : null;
|
||||
if (memoryLimbo == null && player == null) {
|
||||
sender.sendMessage(ChatColor.BLUE + "No AuthMe limbo data");
|
||||
sender.sendMessage("No limbo data and no player online with name '" + arguments.get(0) + "'");
|
||||
return;
|
||||
}
|
||||
|
||||
sender.sendMessage(ChatColor.BLUE + "Player / limbo / disk limbo info for '" + arguments.get(0) + "'");
|
||||
new InfoDisplayer(sender, player, memoryLimbo, diskLimbo)
|
||||
.sendEntry("Is op", Player::isOp, LimboPlayer::isOperator)
|
||||
.sendEntry("Walk speed", Player::getWalkSpeed, LimboPlayer::getWalkSpeed)
|
||||
.sendEntry("Can fly", Player::getAllowFlight, LimboPlayer::isCanFly)
|
||||
.sendEntry("Fly speed", Player::getFlySpeed, LimboPlayer::getFlySpeed)
|
||||
.sendEntry("Location", p -> formatLocation(p.getLocation()), l -> formatLocation(l.getLocation()))
|
||||
.sendEntry("Prim. group",
|
||||
p -> permissionsManager.hasGroupSupport() ? permissionsManager.getPrimaryGroup(p) : "N/A",
|
||||
LimboPlayer::getGroups);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PermissionNode getRequiredPermission() {
|
||||
return DebugSectionPermissions.LIMBO_PLAYER_VIEWER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the info for the given LimboPlayer and Player to the provided CommandSender.
|
||||
*/
|
||||
private static final class InfoDisplayer {
|
||||
private final CommandSender sender;
|
||||
private final Optional<Player> player;
|
||||
private final Optional<LimboPlayer> memoryLimbo;
|
||||
private final Optional<LimboPlayer> diskLimbo;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param sender command sender to send the information to
|
||||
* @param player the player to get data from
|
||||
* @param memoryLimbo the limbo player to get data from
|
||||
*/
|
||||
InfoDisplayer(CommandSender sender, Player player, LimboPlayer memoryLimbo, LimboPlayer diskLimbo) {
|
||||
this.sender = sender;
|
||||
this.player = Optional.ofNullable(player);
|
||||
this.memoryLimbo = Optional.ofNullable(memoryLimbo);
|
||||
this.diskLimbo = Optional.ofNullable(diskLimbo);
|
||||
|
||||
if (memoryLimbo == null) {
|
||||
sender.sendMessage("Note: no Limbo information available");
|
||||
}
|
||||
if (player == null) {
|
||||
sender.sendMessage("Note: player is not online");
|
||||
} else if (diskLimbo == null) {
|
||||
sender.sendMessage("Note: no Limbo on disk available");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a piece of information to the command sender.
|
||||
*
|
||||
* @param title the designation of the piece of information
|
||||
* @param playerGetter getter for data retrieval on Player
|
||||
* @param limboGetter getter for data retrieval on the LimboPlayer
|
||||
* @param <T> the data type
|
||||
* @return this instance (for chaining)
|
||||
*/
|
||||
<T> InfoDisplayer sendEntry(String title,
|
||||
Function<Player, T> playerGetter,
|
||||
Function<LimboPlayer, T> limboGetter) {
|
||||
sender.sendMessage(
|
||||
title + ": "
|
||||
+ getData(player, playerGetter)
|
||||
+ " / "
|
||||
+ getData(memoryLimbo, limboGetter)
|
||||
+ " / "
|
||||
+ getData(diskLimbo, limboGetter));
|
||||
return this;
|
||||
}
|
||||
|
||||
static <E, T> String getData(Optional<E> entity, Function<E, T> getter) {
|
||||
return entity.map(getter).map(String::valueOf).orElse(" -- ");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,327 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.authme.debug;
|
||||
|
||||
import ch.jalu.configme.properties.Property;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.datasource.MySQL;
|
||||
import fr.xephi.authme.output.ConsoleLoggerFactory;
|
||||
import fr.xephi.authme.permission.DebugSectionPermissions;
|
||||
import fr.xephi.authme.permission.PermissionNode;
|
||||
import fr.xephi.authme.settings.Settings;
|
||||
import fr.xephi.authme.settings.properties.DatabaseSettings;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.inject.Inject;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DatabaseMetaData;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static fr.xephi.authme.command.executable.authme.debug.DebugSectionUtils.castToTypeOrNull;
|
||||
import static fr.xephi.authme.command.executable.authme.debug.DebugSectionUtils.unwrapSourceFromCacheDataSource;
|
||||
import static fr.xephi.authme.data.auth.PlayerAuth.DB_EMAIL_DEFAULT;
|
||||
import static fr.xephi.authme.data.auth.PlayerAuth.DB_LAST_IP_DEFAULT;
|
||||
import static fr.xephi.authme.data.auth.PlayerAuth.DB_LAST_LOGIN_DEFAULT;
|
||||
import static fr.xephi.authme.datasource.SqlDataSourceUtils.getColumnDefaultValue;
|
||||
import static fr.xephi.authme.datasource.SqlDataSourceUtils.isNotNullColumn;
|
||||
import static java.lang.String.format;
|
||||
|
||||
/**
|
||||
* Convenience command to add or remove the default value of a column and its nullable status
|
||||
* in the MySQL data source.
|
||||
*/
|
||||
class MySqlDefaultChanger implements DebugSection {
|
||||
|
||||
private static final String NOT_NULL_SUFFIX = ChatColor.DARK_AQUA + "@" + ChatColor.RESET;
|
||||
private static final String DEFAULT_VALUE_SUFFIX = ChatColor.GOLD + "#" + ChatColor.RESET;
|
||||
|
||||
private ConsoleLogger logger = ConsoleLoggerFactory.get(MySqlDefaultChanger.class);
|
||||
|
||||
@Inject
|
||||
private Settings settings;
|
||||
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
|
||||
private MySQL mySql;
|
||||
|
||||
@PostConstruct
|
||||
void setMySqlField() {
|
||||
this.mySql = castToTypeOrNull(unwrapSourceFromCacheDataSource(this.dataSource), MySQL.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "mysqldef";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Add or remove the default value of MySQL columns";
|
||||
}
|
||||
|
||||
@Override
|
||||
public PermissionNode getRequiredPermission() {
|
||||
return DebugSectionPermissions.MYSQL_DEFAULT_CHANGER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandSender sender, List<String> arguments) {
|
||||
if (mySql == null) {
|
||||
sender.sendMessage("Defaults can be changed for the MySQL data source only.");
|
||||
return;
|
||||
}
|
||||
|
||||
Operation operation = matchToEnum(arguments, 0, Operation.class);
|
||||
Columns column = matchToEnum(arguments, 1, Columns.class);
|
||||
if (operation == Operation.DETAILS) {
|
||||
showColumnDetails(sender);
|
||||
} else if (operation == null || column == null) {
|
||||
displayUsageHints(sender);
|
||||
} else {
|
||||
sender.sendMessage(ChatColor.BLUE + "[AuthMe] MySQL change '" + column + "'");
|
||||
try (Connection con = getConnection(mySql)) {
|
||||
switch (operation) {
|
||||
case ADD:
|
||||
changeColumnToNotNullWithDefault(sender, column, con);
|
||||
break;
|
||||
case REMOVE:
|
||||
removeNotNullAndDefault(sender, column, con);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Unknown operation '" + operation + "'");
|
||||
}
|
||||
} catch (SQLException | IllegalStateException e) {
|
||||
logger.logException("Failed to perform MySQL default altering operation:", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a default value to the column definition and adds a {@code NOT NULL} constraint for
|
||||
* the specified column.
|
||||
*
|
||||
* @param sender the command sender initiation the action
|
||||
* @param column the column to modify
|
||||
* @param con connection to the database
|
||||
* @throws SQLException .
|
||||
*/
|
||||
private void changeColumnToNotNullWithDefault(CommandSender sender, Columns column,
|
||||
Connection con) throws SQLException {
|
||||
final String tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE);
|
||||
final String columnName = settings.getProperty(column.getColumnNameProperty());
|
||||
|
||||
// Replace NULLs with future default value
|
||||
String sql = format("UPDATE %s SET %s = ? WHERE %s IS NULL;", tableName, columnName, columnName);
|
||||
int updatedRows;
|
||||
try (PreparedStatement pst = con.prepareStatement(sql)) {
|
||||
pst.setObject(1, column.getDefaultValue());
|
||||
updatedRows = pst.executeUpdate();
|
||||
}
|
||||
sender.sendMessage("Replaced NULLs with default value ('" + column.getDefaultValue()
|
||||
+ "'), modifying " + updatedRows + " entries");
|
||||
|
||||
// Change column definition to NOT NULL version
|
||||
try (Statement st = con.createStatement()) {
|
||||
st.execute(format("ALTER TABLE %s MODIFY %s %s", tableName, columnName, column.getNotNullDefinition()));
|
||||
sender.sendMessage("Changed column '" + columnName + "' to have NOT NULL constraint");
|
||||
}
|
||||
|
||||
// Log success message
|
||||
logger.info("Changed MySQL column '" + columnName + "' to be NOT NULL, as initiated by '"
|
||||
+ sender.getName() + "'");
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the {@code NOT NULL} constraint of a column definition and replaces rows with the
|
||||
* default value to {@code NULL}.
|
||||
*
|
||||
* @param sender the command sender initiation the action
|
||||
* @param column the column to modify
|
||||
* @param con connection to the database
|
||||
* @throws SQLException .
|
||||
*/
|
||||
private void removeNotNullAndDefault(CommandSender sender, Columns column, Connection con) throws SQLException {
|
||||
final String tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE);
|
||||
final String columnName = settings.getProperty(column.getColumnNameProperty());
|
||||
|
||||
// Change column definition to nullable version
|
||||
try (Statement st = con.createStatement()) {
|
||||
st.execute(format("ALTER TABLE %s MODIFY %s %s", tableName, columnName, column.getNullableDefinition()));
|
||||
sender.sendMessage("Changed column '" + columnName + "' to allow nulls");
|
||||
}
|
||||
|
||||
// Replace old default value with NULL
|
||||
String sql = format("UPDATE %s SET %s = NULL WHERE %s = ?;", tableName, columnName, columnName);
|
||||
int updatedRows;
|
||||
try (PreparedStatement pst = con.prepareStatement(sql)) {
|
||||
pst.setObject(1, column.getDefaultValue());
|
||||
updatedRows = pst.executeUpdate();
|
||||
}
|
||||
sender.sendMessage("Replaced default value ('" + column.getDefaultValue()
|
||||
+ "') to be NULL, modifying " + updatedRows + " entries");
|
||||
|
||||
// Log success message
|
||||
logger.info("Changed MySQL column '" + columnName + "' to allow NULL, as initiated by '"
|
||||
+ sender.getName() + "'");
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the current definitions of all {@link Columns} which can be migrated.
|
||||
*
|
||||
* @param sender command sender to output the data to
|
||||
*/
|
||||
private void showColumnDetails(CommandSender sender) {
|
||||
sender.sendMessage(ChatColor.BLUE + "MySQL column details");
|
||||
final String tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE);
|
||||
try (Connection con = getConnection(mySql)) {
|
||||
final DatabaseMetaData metaData = con.getMetaData();
|
||||
for (Columns col : Columns.values()) {
|
||||
String columnName = settings.getProperty(col.getColumnNameProperty());
|
||||
String isNullText = isNotNullColumn(metaData, tableName, columnName) ? "NOT NULL" : "nullable";
|
||||
Object defaultValue = getColumnDefaultValue(metaData, tableName, columnName);
|
||||
String defaultText = defaultValue == null ? "no default" : "default: '" + defaultValue + "'";
|
||||
sender.sendMessage(formatColumnWithMetadata(col, metaData, tableName)
|
||||
+ " (" + columnName + "): " + isNullText + ", " + defaultText);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
logger.logException("Failed while showing column details:", e);
|
||||
sender.sendMessage("Failed while showing column details. See log for info");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays sample commands and the list of columns that can be changed.
|
||||
*
|
||||
* @param sender the sender issuing the command
|
||||
*/
|
||||
private void displayUsageHints(CommandSender sender) {
|
||||
sender.sendMessage(ChatColor.BLUE + "MySQL column changer");
|
||||
sender.sendMessage("Adds or removes a NOT NULL constraint for a column.");
|
||||
sender.sendMessage("Examples: add a NOT NULL constraint with");
|
||||
sender.sendMessage(" /authme debug mysqldef add <column>");
|
||||
sender.sendMessage("Remove one with /authme debug mysqldef remove <column>");
|
||||
|
||||
sender.sendMessage("Available columns: " + constructColumnListWithMetadata());
|
||||
sender.sendMessage(" " + NOT_NULL_SUFFIX + ": not-null, " + DEFAULT_VALUE_SUFFIX
|
||||
+ ": has default. See /authme debug mysqldef details");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list of {@link Columns} we can toggle with suffixes indicating their NOT NULL and default value status
|
||||
*/
|
||||
private String constructColumnListWithMetadata() {
|
||||
try (Connection con = getConnection(mySql)) {
|
||||
final DatabaseMetaData metaData = con.getMetaData();
|
||||
final String tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE);
|
||||
|
||||
List<String> formattedColumns = new ArrayList<>(Columns.values().length);
|
||||
for (Columns col : Columns.values()) {
|
||||
formattedColumns.add(formatColumnWithMetadata(col, metaData, tableName));
|
||||
}
|
||||
return String.join(ChatColor.RESET + ", ", formattedColumns);
|
||||
} catch (SQLException e) {
|
||||
logger.logException("Failed to construct column list:", e);
|
||||
return ChatColor.RED + "An error occurred! Please see the console for details.";
|
||||
}
|
||||
}
|
||||
|
||||
private String formatColumnWithMetadata(Columns column, DatabaseMetaData metaData,
|
||||
String tableName) throws SQLException {
|
||||
String columnName = settings.getProperty(column.getColumnNameProperty());
|
||||
boolean isNotNull = isNotNullColumn(metaData, tableName, columnName);
|
||||
boolean hasDefaultValue = getColumnDefaultValue(metaData, tableName, columnName) != null;
|
||||
return column.name()
|
||||
+ (isNotNull ? NOT_NULL_SUFFIX : "")
|
||||
+ (hasDefaultValue ? DEFAULT_VALUE_SUFFIX : "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Connection object from the MySQL data source.
|
||||
*
|
||||
* @param mySql the MySQL data source to get the connection from
|
||||
* @return the connection
|
||||
*/
|
||||
@VisibleForTesting
|
||||
Connection getConnection(MySQL mySql) {
|
||||
try {
|
||||
Method method = MySQL.class.getDeclaredMethod("getConnection");
|
||||
method.setAccessible(true);
|
||||
return (Connection) method.invoke(mySql);
|
||||
} catch (IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
|
||||
throw new IllegalStateException("Could not get MySQL connection", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static <E extends Enum<E>> E matchToEnum(List<String> arguments, int index, Class<E> clazz) {
|
||||
if (arguments.size() <= index) {
|
||||
return null;
|
||||
}
|
||||
String str = arguments.get(index);
|
||||
return Arrays.stream(clazz.getEnumConstants())
|
||||
.filter(e -> e.name().equalsIgnoreCase(str))
|
||||
.findFirst().orElse(null);
|
||||
}
|
||||
|
||||
private enum Operation {
|
||||
ADD, REMOVE, DETAILS
|
||||
}
|
||||
|
||||
/** MySQL columns which can be toggled between being NOT NULL and allowing NULL values. */
|
||||
enum Columns {
|
||||
|
||||
LASTLOGIN(DatabaseSettings.MYSQL_COL_LASTLOGIN,
|
||||
"BIGINT", "BIGINT NOT NULL DEFAULT 0", DB_LAST_LOGIN_DEFAULT),
|
||||
|
||||
LASTIP(DatabaseSettings.MYSQL_COL_LAST_IP,
|
||||
"VARCHAR(40) CHARACTER SET ascii COLLATE ascii_bin",
|
||||
"VARCHAR(40) CHARACTER SET ascii COLLATE ascii_bin NOT NULL DEFAULT '127.0.0.1'",
|
||||
DB_LAST_IP_DEFAULT),
|
||||
|
||||
EMAIL(DatabaseSettings.MYSQL_COL_EMAIL,
|
||||
"VARCHAR(255)", "VARCHAR(255) NOT NULL DEFAULT 'your@email.com'", DB_EMAIL_DEFAULT);
|
||||
|
||||
private final Property<String> columnNameProperty;
|
||||
private final String nullableDefinition;
|
||||
private final String notNullDefinition;
|
||||
private final Object defaultValue;
|
||||
|
||||
Columns(Property<String> columnNameProperty, String nullableDefinition,
|
||||
String notNullDefinition, Object defaultValue) {
|
||||
this.columnNameProperty = columnNameProperty;
|
||||
this.nullableDefinition = nullableDefinition;
|
||||
this.notNullDefinition = notNullDefinition;
|
||||
this.defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
/** @return property defining the column name in the database */
|
||||
Property<String> getColumnNameProperty() {
|
||||
return columnNameProperty;
|
||||
}
|
||||
|
||||
/** @return SQL definition of the column allowing NULL values */
|
||||
String getNullableDefinition() {
|
||||
return nullableDefinition;
|
||||
}
|
||||
|
||||
/** @return SQL definition of the column with a NOT NULL constraint */
|
||||
String getNotNullDefinition() {
|
||||
return notNullDefinition;
|
||||
}
|
||||
|
||||
/** @return the default value used in {@link #notNullDefinition} */
|
||||
Object getDefaultValue() {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,56 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.authme.debug;
|
||||
|
||||
import fr.xephi.authme.data.limbo.UserGroup;
|
||||
import fr.xephi.authme.permission.DebugSectionPermissions;
|
||||
import fr.xephi.authme.permission.PermissionNode;
|
||||
import fr.xephi.authme.permission.PermissionsManager;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
/**
|
||||
* Outputs the permission groups of a player.
|
||||
*/
|
||||
class PermissionGroups implements DebugSection {
|
||||
|
||||
@Inject
|
||||
private PermissionsManager permissionsManager;
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "groups";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Show permission groups a player belongs to";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandSender sender, List<String> arguments) {
|
||||
sender.sendMessage(ChatColor.BLUE + "AuthMe permission groups");
|
||||
String name = arguments.isEmpty() ? sender.getName() : arguments.get(0);
|
||||
Player player = Bukkit.getPlayer(name);
|
||||
if (player == null) {
|
||||
sender.sendMessage("Player " + name + " could not be found");
|
||||
} else {
|
||||
List<String> groupNames = permissionsManager.getGroups(player).stream()
|
||||
.map(UserGroup::getGroupName)
|
||||
.collect(toList());
|
||||
|
||||
sender.sendMessage("Player " + name + " has permission groups: " + String.join(", ", groupNames));
|
||||
sender.sendMessage("Primary group is: " + permissionsManager.getGroups(player));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PermissionNode getRequiredPermission() {
|
||||
return DebugSectionPermissions.PERM_GROUPS;
|
||||
}
|
||||
}
|
||||
@ -1,117 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.authme.debug;
|
||||
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.permission.DebugSectionPermissions;
|
||||
import fr.xephi.authme.permission.PermissionNode;
|
||||
import fr.xephi.authme.security.crypts.HashedPassword;
|
||||
import fr.xephi.authme.util.StringUtils;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
|
||||
import static fr.xephi.authme.command.executable.authme.debug.DebugSectionUtils.formatLocation;
|
||||
|
||||
/**
|
||||
* Allows to view the data of a PlayerAuth in the database.
|
||||
*/
|
||||
class PlayerAuthViewer implements DebugSection {
|
||||
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "db";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "View player's data in the database";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandSender sender, List<String> arguments) {
|
||||
if (arguments.isEmpty()) {
|
||||
sender.sendMessage(ChatColor.BLUE + "AuthMe database viewer");
|
||||
sender.sendMessage("Enter player name to view his data in the database.");
|
||||
sender.sendMessage("Example: /authme debug db Bobby");
|
||||
return;
|
||||
}
|
||||
|
||||
PlayerAuth auth = dataSource.getAuth(arguments.get(0));
|
||||
if (auth == null) {
|
||||
sender.sendMessage(ChatColor.BLUE + "AuthMe database viewer");
|
||||
sender.sendMessage("No record exists for '" + arguments.get(0) + "'");
|
||||
} else {
|
||||
displayAuthToSender(auth, sender);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PermissionNode getRequiredPermission() {
|
||||
return DebugSectionPermissions.PLAYER_AUTH_VIEWER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the PlayerAuth information to the given sender.
|
||||
*
|
||||
* @param auth the PlayerAuth to display
|
||||
* @param sender the sender to send the messages to
|
||||
*/
|
||||
private void displayAuthToSender(PlayerAuth auth, CommandSender sender) {
|
||||
sender.sendMessage(ChatColor.BLUE + "[AuthMe] Player " + auth.getNickname() + " / " + auth.getRealName());
|
||||
sender.sendMessage("Email: " + auth.getEmail() + ". IP: " + auth.getLastIp() + ". Group: " + auth.getGroupId());
|
||||
sender.sendMessage("Quit location: "
|
||||
+ formatLocation(auth.getQuitLocX(), auth.getQuitLocY(), auth.getQuitLocZ(), auth.getWorld()));
|
||||
sender.sendMessage("Last login: " + formatDate(auth.getLastLogin()));
|
||||
sender.sendMessage("Registration: " + formatDate(auth.getRegistrationDate())
|
||||
+ " with IP " + auth.getRegistrationIp());
|
||||
|
||||
HashedPassword hashedPass = auth.getPassword();
|
||||
sender.sendMessage("Hash / salt (partial): '" + safeSubstring(hashedPass.getHash(), 6)
|
||||
+ "' / '" + safeSubstring(hashedPass.getSalt(), 4) + "'");
|
||||
sender.sendMessage("TOTP code (partial): '" + safeSubstring(auth.getTotpKey(), 3) + "'");
|
||||
}
|
||||
|
||||
/**
|
||||
* Fail-safe substring method. Guarantees not to show the entire String.
|
||||
*
|
||||
* @param str the string to transform
|
||||
* @param length number of characters to show from the start of the String
|
||||
* @return the first <code>length</code> characters of the string, or half of the string if it is shorter,
|
||||
* or empty string if the string is null or empty
|
||||
*/
|
||||
private static String safeSubstring(String str, int length) {
|
||||
if (StringUtils.isBlank(str)) {
|
||||
return "";
|
||||
} else if (str.length() < length) {
|
||||
return str.substring(0, str.length() / 2) + "...";
|
||||
} else {
|
||||
return str.substring(0, length) + "...";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the given timestamp to a human readable date.
|
||||
*
|
||||
* @param timestamp the timestamp to format (nullable)
|
||||
* @return the formatted timestamp
|
||||
*/
|
||||
private static String formatDate(Long timestamp) {
|
||||
if (timestamp == null) {
|
||||
return "Not available (null)";
|
||||
} else if (timestamp == 0) {
|
||||
return "Not available (0)";
|
||||
} else {
|
||||
LocalDateTime date = LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneId.systemDefault());
|
||||
return DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(date);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,87 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.authme.debug;
|
||||
|
||||
import fr.xephi.authme.permission.DebugSectionPermissions;
|
||||
import fr.xephi.authme.permission.PermissionNode;
|
||||
import fr.xephi.authme.service.BukkitService;
|
||||
import fr.xephi.authme.settings.Settings;
|
||||
import fr.xephi.authme.settings.SpawnLoader;
|
||||
import fr.xephi.authme.settings.properties.RestrictionSettings;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
import static fr.xephi.authme.command.executable.authme.debug.DebugSectionUtils.formatLocation;
|
||||
|
||||
/**
|
||||
* Shows the spawn location that AuthMe is configured to use.
|
||||
*/
|
||||
class SpawnLocationViewer implements DebugSection {
|
||||
|
||||
@Inject
|
||||
private SpawnLoader spawnLoader;
|
||||
|
||||
@Inject
|
||||
private Settings settings;
|
||||
|
||||
@Inject
|
||||
private BukkitService bukkitService;
|
||||
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "spawn";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Shows the spawn location that AuthMe will use";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandSender sender, List<String> arguments) {
|
||||
sender.sendMessage(ChatColor.BLUE + "AuthMe spawn location viewer");
|
||||
if (arguments.isEmpty()) {
|
||||
showGeneralInfo(sender);
|
||||
} else if ("?".equals(arguments.get(0))) {
|
||||
showHelp(sender);
|
||||
} else {
|
||||
showPlayerSpawn(sender, arguments.get(0));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PermissionNode getRequiredPermission() {
|
||||
return DebugSectionPermissions.SPAWN_LOCATION;
|
||||
}
|
||||
|
||||
private void showGeneralInfo(CommandSender sender) {
|
||||
sender.sendMessage("Spawn priority: "
|
||||
+ String.join(", ", settings.getProperty(RestrictionSettings.SPAWN_PRIORITY)));
|
||||
sender.sendMessage("AuthMe spawn location: " + formatLocation(spawnLoader.getSpawn()));
|
||||
sender.sendMessage("AuthMe first spawn location: " + formatLocation(spawnLoader.getFirstSpawn()));
|
||||
sender.sendMessage("AuthMe (first)spawn are only used depending on the configured priority!");
|
||||
sender.sendMessage("Use '/authme debug spawn ?' for further help");
|
||||
}
|
||||
|
||||
private void showHelp(CommandSender sender) {
|
||||
sender.sendMessage("Use /authme spawn and /authme firstspawn to teleport to the spawns.");
|
||||
sender.sendMessage("/authme set(first)spawn sets the (first) spawn to your current location.");
|
||||
sender.sendMessage("Use /authme debug spawn <player> to view where a player would be teleported to.");
|
||||
sender.sendMessage("Read more at https://github.com/AuthMe/AuthMeReloaded/wiki/Spawn-Handling");
|
||||
}
|
||||
|
||||
private void showPlayerSpawn(CommandSender sender, String playerName) {
|
||||
Player player = bukkitService.getPlayerExact(playerName);
|
||||
if (player == null) {
|
||||
sender.sendMessage("Player '" + playerName + "' is not online!");
|
||||
} else {
|
||||
Location spawn = spawnLoader.getSpawnLocation(player);
|
||||
sender.sendMessage("Player '" + playerName + "' has spawn location: " + formatLocation(spawn));
|
||||
sender.sendMessage("Note: this check excludes the AuthMe firstspawn.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,125 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.authme.debug;
|
||||
|
||||
import ch.jalu.datasourcecolumns.data.DataSourceValue;
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.mail.SendMailSsl;
|
||||
import fr.xephi.authme.output.ConsoleLoggerFactory;
|
||||
import fr.xephi.authme.permission.DebugSectionPermissions;
|
||||
import fr.xephi.authme.permission.PermissionNode;
|
||||
import fr.xephi.authme.util.StringUtils;
|
||||
import fr.xephi.authme.util.Utils;
|
||||
import org.apache.commons.mail.EmailException;
|
||||
import org.apache.commons.mail.HtmlEmail;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Sends out a test email.
|
||||
*/
|
||||
class TestEmailSender implements DebugSection {
|
||||
|
||||
private final ConsoleLogger logger = ConsoleLoggerFactory.get(TestEmailSender.class);
|
||||
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
|
||||
@Inject
|
||||
private SendMailSsl sendMailSsl;
|
||||
|
||||
@Inject
|
||||
private Server server;
|
||||
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "mail";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Sends out a test email";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandSender sender, List<String> arguments) {
|
||||
sender.sendMessage(ChatColor.BLUE + "AuthMe test email sender");
|
||||
if (!sendMailSsl.hasAllInformation()) {
|
||||
sender.sendMessage(ChatColor.RED + "You haven't set all required configurations in config.yml "
|
||||
+ "for sending emails. Please check your config.yml");
|
||||
return;
|
||||
}
|
||||
|
||||
String email = getEmail(sender, arguments);
|
||||
|
||||
// getEmail() takes care of informing the sender of the error if email == null
|
||||
if (email != null) {
|
||||
boolean sendMail = sendTestEmail(email);
|
||||
if (sendMail) {
|
||||
sender.sendMessage("Test email sent to " + email + " with success");
|
||||
} else {
|
||||
sender.sendMessage(ChatColor.RED + "Failed to send test mail to " + email + "; please check your logs");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PermissionNode getRequiredPermission() {
|
||||
return DebugSectionPermissions.TEST_EMAIL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the email address to use based on the sender and the arguments. If the arguments are empty,
|
||||
* we attempt to retrieve the email from the sender. If there is an argument, we verify that it is
|
||||
* an email address.
|
||||
* {@code null} is returned if no email address could be found. This method informs the sender of
|
||||
* the specific error in such cases.
|
||||
*
|
||||
* @param sender the command sender
|
||||
* @param arguments the provided arguments
|
||||
* @return the email to use, or null if none found
|
||||
*/
|
||||
private String getEmail(CommandSender sender, List<String> arguments) {
|
||||
if (arguments.isEmpty()) {
|
||||
DataSourceValue<String> emailResult = dataSource.getEmail(sender.getName());
|
||||
if (!emailResult.rowExists()) {
|
||||
sender.sendMessage(ChatColor.RED + "Please provide an email address, "
|
||||
+ "e.g. /authme debug mail test@example.com");
|
||||
return null;
|
||||
}
|
||||
final String email = emailResult.getValue();
|
||||
if (Utils.isEmailEmpty(email)) {
|
||||
sender.sendMessage(ChatColor.RED + "No email set for your account!"
|
||||
+ " Please use /authme debug mail <email>");
|
||||
return null;
|
||||
}
|
||||
return email;
|
||||
} else {
|
||||
String email = arguments.get(0);
|
||||
if (StringUtils.isInsideString('@', email)) {
|
||||
return email;
|
||||
}
|
||||
sender.sendMessage(ChatColor.RED + "Invalid email! Usage: /authme debug mail test@example.com");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean sendTestEmail(String email) {
|
||||
HtmlEmail htmlEmail;
|
||||
try {
|
||||
htmlEmail = sendMailSsl.initializeMail(email);
|
||||
} catch (EmailException e) {
|
||||
logger.logException("Failed to create email for sample email:", e);
|
||||
return false;
|
||||
}
|
||||
|
||||
htmlEmail.setSubject("AuthMe test email");
|
||||
String message = "Hello there!<br />This is a sample email sent to you from a Minecraft server ("
|
||||
+ server.getName() + ") via /authme debug mail. If you're seeing this, sending emails should be fine.";
|
||||
return sendMailSsl.sendEmail(message, htmlEmail);
|
||||
}
|
||||
}
|
||||
@ -1,86 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.captcha;
|
||||
|
||||
import fr.xephi.authme.command.PlayerCommand;
|
||||
import fr.xephi.authme.data.auth.PlayerCache;
|
||||
import fr.xephi.authme.data.captcha.LoginCaptchaManager;
|
||||
import fr.xephi.authme.data.captcha.RegistrationCaptchaManager;
|
||||
import fr.xephi.authme.data.limbo.LimboMessageType;
|
||||
import fr.xephi.authme.data.limbo.LimboService;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.service.CommonService;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Captcha command, allowing a player to solve a captcha.
|
||||
*/
|
||||
public class CaptchaCommand extends PlayerCommand {
|
||||
|
||||
@Inject
|
||||
private PlayerCache playerCache;
|
||||
|
||||
@Inject
|
||||
private LoginCaptchaManager loginCaptchaManager;
|
||||
|
||||
@Inject
|
||||
private RegistrationCaptchaManager registrationCaptchaManager;
|
||||
|
||||
@Inject
|
||||
private CommonService commonService;
|
||||
|
||||
@Inject
|
||||
private LimboService limboService;
|
||||
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
|
||||
@Override
|
||||
public void runCommand(Player player, List<String> arguments) {
|
||||
final String name = player.getName();
|
||||
|
||||
if (playerCache.isAuthenticated(name)) {
|
||||
// No captcha is relevant if the player is logged in
|
||||
commonService.send(player, MessageKey.ALREADY_LOGGED_IN_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (loginCaptchaManager.isCaptchaRequired(name)) {
|
||||
checkLoginCaptcha(player, arguments.get(0));
|
||||
} else {
|
||||
final boolean isPlayerRegistered = dataSource.isAuthAvailable(name);
|
||||
if (!isPlayerRegistered && registrationCaptchaManager.isCaptchaRequired(name)) {
|
||||
checkRegisterCaptcha(player, arguments.get(0));
|
||||
} else {
|
||||
MessageKey errorMessage = isPlayerRegistered ? MessageKey.USAGE_LOGIN : MessageKey.USAGE_REGISTER;
|
||||
commonService.send(player, errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkLoginCaptcha(Player player, String captchaCode) {
|
||||
final boolean isCorrectCode = loginCaptchaManager.checkCode(player, captchaCode);
|
||||
if (isCorrectCode) {
|
||||
commonService.send(player, MessageKey.CAPTCHA_SUCCESS);
|
||||
commonService.send(player, MessageKey.LOGIN_MESSAGE);
|
||||
limboService.unmuteMessageTask(player);
|
||||
} else {
|
||||
String newCode = loginCaptchaManager.getCaptchaCodeOrGenerateNew(player.getName());
|
||||
commonService.send(player, MessageKey.CAPTCHA_WRONG_ERROR, newCode);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkRegisterCaptcha(Player player, String captchaCode) {
|
||||
final boolean isCorrectCode = registrationCaptchaManager.checkCode(player, captchaCode);
|
||||
if (isCorrectCode) {
|
||||
commonService.send(player, MessageKey.REGISTER_CAPTCHA_SUCCESS);
|
||||
commonService.send(player, MessageKey.REGISTER_MESSAGE);
|
||||
} else {
|
||||
String newCode = registrationCaptchaManager.getCaptchaCodeOrGenerateNew(player.getName());
|
||||
commonService.send(player, MessageKey.CAPTCHA_WRONG_ERROR, newCode);
|
||||
}
|
||||
limboService.resetMessageTask(player, LimboMessageType.REGISTER);
|
||||
}
|
||||
}
|
||||
@ -1,68 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.changepassword;
|
||||
|
||||
import fr.xephi.authme.command.PlayerCommand;
|
||||
import fr.xephi.authme.data.VerificationCodeManager;
|
||||
import fr.xephi.authme.data.auth.PlayerCache;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.process.Management;
|
||||
import fr.xephi.authme.service.CommonService;
|
||||
import fr.xephi.authme.service.ValidationService;
|
||||
import fr.xephi.authme.service.ValidationService.ValidationResult;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* The command for a player to change his password with.
|
||||
*/
|
||||
public class ChangePasswordCommand extends PlayerCommand {
|
||||
|
||||
@Inject
|
||||
private CommonService commonService;
|
||||
|
||||
@Inject
|
||||
private PlayerCache playerCache;
|
||||
|
||||
@Inject
|
||||
private ValidationService validationService;
|
||||
|
||||
@Inject
|
||||
private Management management;
|
||||
|
||||
@Inject
|
||||
private VerificationCodeManager codeManager;
|
||||
|
||||
@Override
|
||||
public void runCommand(Player player, List<String> arguments) {
|
||||
String name = player.getName().toLowerCase(Locale.ROOT);
|
||||
|
||||
if (!playerCache.isAuthenticated(name)) {
|
||||
commonService.send(player, MessageKey.NOT_LOGGED_IN);
|
||||
return;
|
||||
}
|
||||
|
||||
String oldPassword = arguments.get(0);
|
||||
String newPassword = arguments.get(1);
|
||||
|
||||
// Make sure the password is allowed
|
||||
ValidationResult passwordValidation = validationService.validatePassword(newPassword, name);
|
||||
if (passwordValidation.hasError()) {
|
||||
commonService.send(player, passwordValidation.getMessageKey(), passwordValidation.getArgs());
|
||||
return;
|
||||
}
|
||||
|
||||
management.performPasswordChange(player, oldPassword, newPassword);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getAlternativeCommand() {
|
||||
return "/authme password <playername> <password>";
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageKey getArgumentsMismatchMessage() {
|
||||
return MessageKey.USAGE_CHANGE_PASSWORD;
|
||||
}
|
||||
}
|
||||
@ -1,40 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.email;
|
||||
|
||||
import fr.xephi.authme.command.PlayerCommand;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.process.Management;
|
||||
import fr.xephi.authme.service.CommonService;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Command for setting an email to an account.
|
||||
*/
|
||||
public class AddEmailCommand extends PlayerCommand {
|
||||
|
||||
@Inject
|
||||
private Management management;
|
||||
|
||||
@Inject
|
||||
private CommonService commonService;
|
||||
|
||||
@Override
|
||||
public void runCommand(Player player, List<String> arguments) {
|
||||
String email = arguments.get(0);
|
||||
String emailConfirmation = arguments.get(1);
|
||||
|
||||
if (email.equals(emailConfirmation)) {
|
||||
// Closer inspection of the mail address handled by the async task
|
||||
management.performAddEmail(player, email);
|
||||
} else {
|
||||
commonService.send(player, MessageKey.CONFIRM_EMAIL_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageKey getArgumentsMismatchMessage() {
|
||||
return MessageKey.USAGE_ADD_EMAIL;
|
||||
}
|
||||
}
|
||||
@ -1,46 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.email;
|
||||
|
||||
import fr.xephi.authme.command.PlayerCommand;
|
||||
import fr.xephi.authme.data.VerificationCodeManager;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.process.Management;
|
||||
import fr.xephi.authme.service.CommonService;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Change email command.
|
||||
*/
|
||||
public class ChangeEmailCommand extends PlayerCommand {
|
||||
|
||||
@Inject
|
||||
private Management management;
|
||||
|
||||
@Inject
|
||||
private CommonService commonService;
|
||||
|
||||
@Inject
|
||||
private VerificationCodeManager codeManager;
|
||||
|
||||
@Override
|
||||
public void runCommand(Player player, List<String> arguments) {
|
||||
final String playerName = player.getName();
|
||||
// Check if the user has been verified or not
|
||||
if (codeManager.isVerificationRequired(player)) {
|
||||
codeManager.codeExistOrGenerateNew(playerName);
|
||||
commonService.send(player, MessageKey.VERIFICATION_CODE_REQUIRED);
|
||||
return;
|
||||
}
|
||||
|
||||
String playerMailOld = arguments.get(0);
|
||||
String playerMailNew = arguments.get(1);
|
||||
management.performChangeEmail(player, playerMailOld, playerMailNew);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageKey getArgumentsMismatchMessage() {
|
||||
return MessageKey.USAGE_CHANGE_EMAIL;
|
||||
}
|
||||
}
|
||||
@ -1,29 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.email;
|
||||
|
||||
import fr.xephi.authme.command.CommandMapper;
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.command.FoundCommandResult;
|
||||
import fr.xephi.authme.command.help.HelpProvider;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Base command for /email, showing information about the child commands.
|
||||
*/
|
||||
public class EmailBaseCommand implements ExecutableCommand {
|
||||
|
||||
@Inject
|
||||
private CommandMapper commandMapper;
|
||||
|
||||
@Inject
|
||||
private HelpProvider helpProvider;
|
||||
|
||||
@Override
|
||||
public void executeCommand(CommandSender sender, List<String> arguments) {
|
||||
FoundCommandResult result = commandMapper.mapPartsToCommand(sender, Collections.singletonList("email"));
|
||||
helpProvider.outputHelp(sender, result, HelpProvider.SHOW_CHILDREN);
|
||||
}
|
||||
}
|
||||
@ -1,61 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.email;
|
||||
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.command.PlayerCommand;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.output.ConsoleLoggerFactory;
|
||||
import fr.xephi.authme.security.PasswordSecurity;
|
||||
import fr.xephi.authme.security.crypts.HashedPassword;
|
||||
import fr.xephi.authme.service.CommonService;
|
||||
import fr.xephi.authme.service.PasswordRecoveryService;
|
||||
import fr.xephi.authme.service.ValidationService;
|
||||
import fr.xephi.authme.service.ValidationService.ValidationResult;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Command for changing password following successful recovery.
|
||||
*/
|
||||
public class EmailSetPasswordCommand extends PlayerCommand {
|
||||
|
||||
private final ConsoleLogger logger = ConsoleLoggerFactory.get(EmailSetPasswordCommand.class);
|
||||
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
|
||||
@Inject
|
||||
private CommonService commonService;
|
||||
|
||||
@Inject
|
||||
private PasswordRecoveryService recoveryService;
|
||||
|
||||
@Inject
|
||||
private PasswordSecurity passwordSecurity;
|
||||
|
||||
@Inject
|
||||
private ValidationService validationService;
|
||||
|
||||
@Override
|
||||
protected void runCommand(Player player, List<String> arguments) {
|
||||
if (recoveryService.canChangePassword(player)) {
|
||||
String name = player.getName();
|
||||
String password = arguments.get(0);
|
||||
|
||||
ValidationResult result = validationService.validatePassword(password, name);
|
||||
if (!result.hasError()) {
|
||||
HashedPassword hashedPassword = passwordSecurity.computeHash(password, name);
|
||||
dataSource.updatePassword(name, hashedPassword);
|
||||
recoveryService.removeFromSuccessfulRecovery(player);
|
||||
logger.info("Player '" + name + "' has changed their password from recovery");
|
||||
commonService.send(player, MessageKey.PASSWORD_CHANGED_SUCCESS);
|
||||
} else {
|
||||
commonService.send(player, result.getMessageKey(), result.getArgs());
|
||||
}
|
||||
} else {
|
||||
commonService.send(player, MessageKey.CHANGE_PASSWORD_EXPIRED);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,46 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.email;
|
||||
|
||||
import fr.xephi.authme.command.PlayerCommand;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.service.CommonService;
|
||||
import fr.xephi.authme.service.PasswordRecoveryService;
|
||||
import fr.xephi.authme.service.RecoveryCodeService;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Command for submitting email recovery code.
|
||||
*/
|
||||
public class ProcessCodeCommand extends PlayerCommand {
|
||||
|
||||
@Inject
|
||||
private CommonService commonService;
|
||||
|
||||
@Inject
|
||||
private RecoveryCodeService codeService;
|
||||
|
||||
@Inject
|
||||
private PasswordRecoveryService recoveryService;
|
||||
|
||||
@Override
|
||||
protected void runCommand(Player player, List<String> arguments) {
|
||||
String name = player.getName();
|
||||
String code = arguments.get(0);
|
||||
|
||||
if (codeService.hasTriesLeft(name)) {
|
||||
if (codeService.isCodeValid(name, code)) {
|
||||
commonService.send(player, MessageKey.RECOVERY_CODE_CORRECT);
|
||||
recoveryService.addSuccessfulRecovery(player);
|
||||
codeService.removeCode(name);
|
||||
} else {
|
||||
commonService.send(player, MessageKey.INCORRECT_RECOVERY_CODE,
|
||||
Integer.toString(codeService.getTriesLeft(name)));
|
||||
}
|
||||
} else {
|
||||
codeService.removeCode(name);
|
||||
commonService.send(player, MessageKey.RECOVERY_TRIES_EXCEEDED);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,91 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.email;
|
||||
|
||||
import ch.jalu.datasourcecolumns.data.DataSourceValue;
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.command.PlayerCommand;
|
||||
import fr.xephi.authme.data.auth.PlayerCache;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.mail.EmailService;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.output.ConsoleLoggerFactory;
|
||||
import fr.xephi.authme.service.BukkitService;
|
||||
import fr.xephi.authme.service.CommonService;
|
||||
import fr.xephi.authme.service.PasswordRecoveryService;
|
||||
import fr.xephi.authme.service.RecoveryCodeService;
|
||||
import fr.xephi.authme.util.Utils;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Command for password recovery by email.
|
||||
*/
|
||||
public class RecoverEmailCommand extends PlayerCommand {
|
||||
|
||||
private final ConsoleLogger logger = ConsoleLoggerFactory.get(RecoverEmailCommand.class);
|
||||
|
||||
@Inject
|
||||
private CommonService commonService;
|
||||
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
|
||||
@Inject
|
||||
private PlayerCache playerCache;
|
||||
|
||||
@Inject
|
||||
private EmailService emailService;
|
||||
|
||||
@Inject
|
||||
private PasswordRecoveryService recoveryService;
|
||||
|
||||
@Inject
|
||||
private RecoveryCodeService recoveryCodeService;
|
||||
|
||||
@Inject
|
||||
private BukkitService bukkitService;
|
||||
|
||||
@Override
|
||||
protected void runCommand(Player player, List<String> arguments) {
|
||||
final String playerMail = arguments.get(0);
|
||||
final String playerName = player.getName();
|
||||
|
||||
if (!emailService.hasAllInformation()) {
|
||||
logger.warning("Mail API is not set");
|
||||
commonService.send(player, MessageKey.INCOMPLETE_EMAIL_SETTINGS);
|
||||
return;
|
||||
}
|
||||
if (playerCache.isAuthenticated(playerName)) {
|
||||
commonService.send(player, MessageKey.ALREADY_LOGGED_IN_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
DataSourceValue<String> emailResult = dataSource.getEmail(playerName);
|
||||
if (!emailResult.rowExists()) {
|
||||
commonService.send(player, MessageKey.USAGE_REGISTER);
|
||||
return;
|
||||
}
|
||||
|
||||
final String email = emailResult.getValue();
|
||||
if (Utils.isEmailEmpty(email) || !email.equalsIgnoreCase(playerMail)) {
|
||||
commonService.send(player, MessageKey.INVALID_EMAIL);
|
||||
return;
|
||||
}
|
||||
|
||||
bukkitService.runTaskAsynchronously(() -> {
|
||||
if (recoveryCodeService.isRecoveryCodeNeeded()) {
|
||||
// Recovery code is needed; generate and send one
|
||||
recoveryService.createAndSendRecoveryCode(player, email);
|
||||
} else {
|
||||
// Code not needed, just send them a new password
|
||||
recoveryService.generateAndSendNewPassword(player, email);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageKey getArgumentsMismatchMessage() {
|
||||
return MessageKey.USAGE_RECOVER_EMAIL;
|
||||
}
|
||||
}
|
||||
@ -1,48 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.email;
|
||||
|
||||
import fr.xephi.authme.command.PlayerCommand;
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
import fr.xephi.authme.data.auth.PlayerCache;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.service.CommonService;
|
||||
import fr.xephi.authme.settings.properties.SecuritySettings;
|
||||
import fr.xephi.authme.util.Utils;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Show email command.
|
||||
*/
|
||||
public class ShowEmailCommand extends PlayerCommand {
|
||||
|
||||
@Inject
|
||||
private CommonService commonService;
|
||||
|
||||
@Inject
|
||||
private PlayerCache playerCache;
|
||||
|
||||
@Override
|
||||
public void runCommand(Player player, List<String> arguments) {
|
||||
PlayerAuth auth = playerCache.getAuth(player.getName());
|
||||
if (auth != null && !Utils.isEmailEmpty(auth.getEmail())) {
|
||||
if (commonService.getProperty(SecuritySettings.USE_EMAIL_MASKING)){
|
||||
commonService.send(player, MessageKey.EMAIL_SHOW, emailMask(auth.getEmail()));
|
||||
} else {
|
||||
commonService.send(player, MessageKey.EMAIL_SHOW, auth.getEmail());
|
||||
}
|
||||
} else {
|
||||
commonService.send(player, MessageKey.SHOW_NO_EMAIL);
|
||||
}
|
||||
}
|
||||
|
||||
private String emailMask(String email){
|
||||
String[] frag = email.split("@"); //Split id and domain
|
||||
int sid = frag[0].length() / 3 + 1; //Define the id view (required length >= 1)
|
||||
int sdomain = frag[1].length() / 3; //Define the domain view (required length >= 0)
|
||||
String id = frag[0].substring(0, sid) + "***"; //Build the id
|
||||
String domain = "***" + frag[1].substring(sdomain); //Build the domain
|
||||
return id + "@" + domain;
|
||||
}
|
||||
}
|
||||
@ -1,34 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.login;
|
||||
|
||||
import fr.xephi.authme.command.PlayerCommand;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.process.Management;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Login command.
|
||||
*/
|
||||
public class LoginCommand extends PlayerCommand {
|
||||
|
||||
@Inject
|
||||
private Management management;
|
||||
|
||||
@Override
|
||||
public void runCommand(Player player, List<String> arguments) {
|
||||
String password = arguments.get(0);
|
||||
management.performLogin(player, password);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageKey getArgumentsMismatchMessage() {
|
||||
return MessageKey.USAGE_LOGIN;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getAlternativeCommand() {
|
||||
return "/authme forcelogin <player>";
|
||||
}
|
||||
}
|
||||
@ -1,22 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.logout;
|
||||
|
||||
import fr.xephi.authme.command.PlayerCommand;
|
||||
import fr.xephi.authme.process.Management;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Logout command.
|
||||
*/
|
||||
public class LogoutCommand extends PlayerCommand {
|
||||
|
||||
@Inject
|
||||
private Management management;
|
||||
|
||||
@Override
|
||||
public void runCommand(Player player, List<String> arguments) {
|
||||
management.performLogout(player);
|
||||
}
|
||||
}
|
||||
@ -1,225 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.register;
|
||||
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.command.PlayerCommand;
|
||||
import fr.xephi.authme.data.captcha.RegistrationCaptchaManager;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.mail.EmailService;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.output.ConsoleLoggerFactory;
|
||||
import fr.xephi.authme.process.Management;
|
||||
import fr.xephi.authme.process.register.RegisterSecondaryArgument;
|
||||
import fr.xephi.authme.process.register.RegistrationType;
|
||||
import fr.xephi.authme.process.register.executors.EmailRegisterParams;
|
||||
import fr.xephi.authme.process.register.executors.PasswordRegisterParams;
|
||||
import fr.xephi.authme.process.register.executors.RegistrationMethod;
|
||||
import fr.xephi.authme.process.register.executors.TwoFactorRegisterParams;
|
||||
import fr.xephi.authme.security.HashAlgorithm;
|
||||
import fr.xephi.authme.service.CommonService;
|
||||
import fr.xephi.authme.service.ValidationService;
|
||||
import fr.xephi.authme.settings.properties.EmailSettings;
|
||||
import fr.xephi.authme.settings.properties.RegistrationSettings;
|
||||
import fr.xephi.authme.settings.properties.SecuritySettings;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
import static fr.xephi.authme.process.register.RegisterSecondaryArgument.CONFIRMATION;
|
||||
import static fr.xephi.authme.process.register.RegisterSecondaryArgument.EMAIL_MANDATORY;
|
||||
import static fr.xephi.authme.process.register.RegisterSecondaryArgument.EMAIL_OPTIONAL;
|
||||
import static fr.xephi.authme.process.register.RegisterSecondaryArgument.NONE;
|
||||
import static fr.xephi.authme.settings.properties.RegistrationSettings.REGISTER_SECOND_ARGUMENT;
|
||||
|
||||
/**
|
||||
* Command for /register.
|
||||
*/
|
||||
public class RegisterCommand extends PlayerCommand {
|
||||
|
||||
private final ConsoleLogger logger = ConsoleLoggerFactory.get(RegisterCommand.class);
|
||||
|
||||
@Inject
|
||||
private Management management;
|
||||
|
||||
@Inject
|
||||
private CommonService commonService;
|
||||
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
|
||||
@Inject
|
||||
private EmailService emailService;
|
||||
|
||||
@Inject
|
||||
private ValidationService validationService;
|
||||
|
||||
@Inject
|
||||
private RegistrationCaptchaManager registrationCaptchaManager;
|
||||
|
||||
@Override
|
||||
public void runCommand(Player player, List<String> arguments) {
|
||||
if (!isCaptchaFulfilled(player)) {
|
||||
return; // isCaptchaFulfilled handles informing the player on failure
|
||||
}
|
||||
|
||||
if (commonService.getProperty(SecuritySettings.PASSWORD_HASH) == HashAlgorithm.TWO_FACTOR) {
|
||||
//for two factor auth we don't need to check the usage
|
||||
management.performRegister(RegistrationMethod.TWO_FACTOR_REGISTRATION,
|
||||
TwoFactorRegisterParams.of(player));
|
||||
return;
|
||||
} else if (arguments.size() < 1) {
|
||||
commonService.send(player, MessageKey.USAGE_REGISTER);
|
||||
return;
|
||||
}
|
||||
|
||||
RegistrationType registrationType = commonService.getProperty(RegistrationSettings.REGISTRATION_TYPE);
|
||||
if (registrationType == RegistrationType.PASSWORD) {
|
||||
handlePasswordRegistration(player, arguments);
|
||||
} else if (registrationType == RegistrationType.EMAIL) {
|
||||
handleEmailRegistration(player, arguments);
|
||||
} else {
|
||||
throw new IllegalStateException("Unknown registration type '" + registrationType + "'");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getAlternativeCommand() {
|
||||
return "/authme register <playername> <password>";
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageKey getArgumentsMismatchMessage() {
|
||||
return MessageKey.USAGE_REGISTER;
|
||||
}
|
||||
|
||||
private boolean isCaptchaFulfilled(Player player) {
|
||||
if (registrationCaptchaManager.isCaptchaRequired(player.getName())) {
|
||||
String code = registrationCaptchaManager.getCaptchaCodeOrGenerateNew(player.getName());
|
||||
commonService.send(player, MessageKey.CAPTCHA_FOR_REGISTRATION_REQUIRED, code);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void handlePasswordRegistration(Player player, List<String> arguments) {
|
||||
if (isSecondArgValidForPasswordRegistration(player, arguments)) {
|
||||
final String password = arguments.get(0);
|
||||
final String email = getEmailIfAvailable(arguments);
|
||||
|
||||
management.performRegister(RegistrationMethod.PASSWORD_REGISTRATION,
|
||||
PasswordRegisterParams.of(player, password, email));
|
||||
}
|
||||
}
|
||||
|
||||
private String getEmailIfAvailable(List<String> arguments) {
|
||||
if (arguments.size() >= 2) {
|
||||
RegisterSecondaryArgument secondArgType = commonService.getProperty(REGISTER_SECOND_ARGUMENT);
|
||||
if (secondArgType == EMAIL_MANDATORY || secondArgType == EMAIL_OPTIONAL) {
|
||||
return arguments.get(1);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that the second argument is valid (based on the configuration)
|
||||
* to perform a password registration. The player is informed if the check
|
||||
* is unsuccessful.
|
||||
*
|
||||
* @param player the player to register
|
||||
* @param arguments the provided arguments
|
||||
* @return true if valid, false otherwise
|
||||
*/
|
||||
private boolean isSecondArgValidForPasswordRegistration(Player player, List<String> arguments) {
|
||||
RegisterSecondaryArgument secondArgType = commonService.getProperty(REGISTER_SECOND_ARGUMENT);
|
||||
// cases where args.size < 2
|
||||
if (secondArgType == NONE || secondArgType == EMAIL_OPTIONAL && arguments.size() < 2) {
|
||||
return true;
|
||||
} else if (arguments.size() < 2) {
|
||||
commonService.send(player, MessageKey.USAGE_REGISTER);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (secondArgType == CONFIRMATION) {
|
||||
if (arguments.get(0).equals(arguments.get(1))) {
|
||||
return true;
|
||||
} else {
|
||||
commonService.send(player, MessageKey.PASSWORD_MATCH_ERROR);
|
||||
return false;
|
||||
}
|
||||
} else if (secondArgType == EMAIL_MANDATORY || secondArgType == EMAIL_OPTIONAL) {
|
||||
if (validationService.validateEmail(arguments.get(1))) {
|
||||
return true;
|
||||
} else {
|
||||
commonService.send(player, MessageKey.INVALID_EMAIL);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
throw new IllegalStateException("Unknown secondary argument type '" + secondArgType + "'");
|
||||
}
|
||||
}
|
||||
|
||||
private void handleEmailRegistration(Player player, List<String> arguments) {
|
||||
if (!emailService.hasAllInformation()) {
|
||||
commonService.send(player, MessageKey.INCOMPLETE_EMAIL_SETTINGS);
|
||||
logger.warning("Cannot register player '" + player.getName() + "': no email or password is set "
|
||||
+ "to send emails from. Please adjust your config at " + EmailSettings.MAIL_ACCOUNT.getPath());
|
||||
return;
|
||||
}
|
||||
|
||||
final String email = arguments.get(0);
|
||||
if (!validationService.validateEmail(email)) {
|
||||
commonService.send(player, MessageKey.INVALID_EMAIL);
|
||||
} else if (isSecondArgValidForEmailRegistration(player, arguments)) {
|
||||
management.performRegister(RegistrationMethod.EMAIL_REGISTRATION,
|
||||
EmailRegisterParams.of(player, email));
|
||||
Timer timer = new Timer();
|
||||
timer.schedule(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (dataSource.getAuth(player.getName()) != null) {
|
||||
if (dataSource.getAuth(player.getName()).getLastLogin() == null) {
|
||||
management.performUnregisterByAdmin(null, player.getName(), player);
|
||||
timer.cancel();
|
||||
}
|
||||
} else {
|
||||
timer.cancel();
|
||||
}
|
||||
}
|
||||
}, 600000);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that the second argument is valid (based on the configuration)
|
||||
* to perform an email registration. The player is informed if the check
|
||||
* is unsuccessful.
|
||||
*
|
||||
* @param player the player to register
|
||||
* @param arguments the provided arguments
|
||||
* @return true if valid, false otherwise
|
||||
*/
|
||||
private boolean isSecondArgValidForEmailRegistration(Player player, List<String> arguments) {
|
||||
RegisterSecondaryArgument secondArgType = commonService.getProperty(REGISTER_SECOND_ARGUMENT);
|
||||
// cases where args.size < 2
|
||||
if (secondArgType == NONE || secondArgType == EMAIL_OPTIONAL && arguments.size() < 2) {
|
||||
return true;
|
||||
} else if (arguments.size() < 2) {
|
||||
commonService.send(player, MessageKey.USAGE_REGISTER);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (secondArgType == EMAIL_OPTIONAL || secondArgType == EMAIL_MANDATORY || secondArgType == CONFIRMATION) {
|
||||
if (arguments.get(0).equals(arguments.get(1))) {
|
||||
return true;
|
||||
} else {
|
||||
commonService.send(player, MessageKey.USAGE_REGISTER);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
throw new IllegalStateException("Unknown secondary argument type '" + secondArgType + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,43 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.totp;
|
||||
|
||||
import fr.xephi.authme.command.PlayerCommand;
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
import fr.xephi.authme.data.auth.PlayerCache;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.message.Messages;
|
||||
import fr.xephi.authme.security.totp.GenerateTotpService;
|
||||
import fr.xephi.authme.security.totp.TotpAuthenticator.TotpGenerationResult;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Command for a player to enable TOTP.
|
||||
*/
|
||||
public class AddTotpCommand extends PlayerCommand {
|
||||
|
||||
@Inject
|
||||
private GenerateTotpService generateTotpService;
|
||||
|
||||
@Inject
|
||||
private PlayerCache playerCache;
|
||||
|
||||
@Inject
|
||||
private Messages messages;
|
||||
|
||||
@Override
|
||||
protected void runCommand(Player player, List<String> arguments) {
|
||||
PlayerAuth auth = playerCache.getAuth(player.getName());
|
||||
if (auth == null) {
|
||||
messages.send(player, MessageKey.NOT_LOGGED_IN);
|
||||
} else if (auth.getTotpKey() == null) {
|
||||
TotpGenerationResult createdTotpInfo = generateTotpService.generateTotpKey(player);
|
||||
messages.send(player, MessageKey.TWO_FACTOR_CREATE,
|
||||
createdTotpInfo.getTotpKey(), createdTotpInfo.getAuthenticatorQrCodeUrl());
|
||||
messages.send(player, MessageKey.TWO_FACTOR_CREATE_CONFIRMATION_REQUIRED);
|
||||
} else {
|
||||
messages.send(player, MessageKey.TWO_FACTOR_ALREADY_ENABLED);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,74 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.totp;
|
||||
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.command.PlayerCommand;
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
import fr.xephi.authme.data.auth.PlayerCache;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.message.Messages;
|
||||
import fr.xephi.authme.output.ConsoleLoggerFactory;
|
||||
import fr.xephi.authme.security.totp.GenerateTotpService;
|
||||
import fr.xephi.authme.security.totp.TotpAuthenticator.TotpGenerationResult;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Command to enable TOTP by supplying the proper code as confirmation.
|
||||
*/
|
||||
public class ConfirmTotpCommand extends PlayerCommand {
|
||||
|
||||
private final ConsoleLogger logger = ConsoleLoggerFactory.get(ConfirmTotpCommand.class);
|
||||
|
||||
@Inject
|
||||
private GenerateTotpService generateTotpService;
|
||||
|
||||
@Inject
|
||||
private PlayerCache playerCache;
|
||||
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
|
||||
@Inject
|
||||
private Messages messages;
|
||||
|
||||
@Override
|
||||
protected void runCommand(Player player, List<String> arguments) {
|
||||
PlayerAuth auth = playerCache.getAuth(player.getName());
|
||||
if (auth == null) {
|
||||
messages.send(player, MessageKey.NOT_LOGGED_IN);
|
||||
} else if (auth.getTotpKey() != null) {
|
||||
messages.send(player, MessageKey.TWO_FACTOR_ALREADY_ENABLED);
|
||||
} else {
|
||||
verifyTotpCodeConfirmation(player, auth, arguments.get(0));
|
||||
}
|
||||
}
|
||||
|
||||
private void verifyTotpCodeConfirmation(Player player, PlayerAuth auth, String inputTotpCode) {
|
||||
final TotpGenerationResult totpDetails = generateTotpService.getGeneratedTotpKey(player);
|
||||
if (totpDetails == null) {
|
||||
messages.send(player, MessageKey.TWO_FACTOR_ENABLE_ERROR_NO_CODE);
|
||||
} else {
|
||||
boolean isCodeValid = generateTotpService.isTotpCodeCorrectForGeneratedTotpKey(player, inputTotpCode);
|
||||
if (isCodeValid) {
|
||||
generateTotpService.removeGenerateTotpKey(player);
|
||||
insertTotpKeyIntoDatabase(player, auth, totpDetails);
|
||||
} else {
|
||||
messages.send(player, MessageKey.TWO_FACTOR_ENABLE_ERROR_WRONG_CODE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void insertTotpKeyIntoDatabase(Player player, PlayerAuth auth, TotpGenerationResult totpDetails) {
|
||||
if (dataSource.setTotpKey(player.getName(), totpDetails.getTotpKey())) {
|
||||
messages.send(player, MessageKey.TWO_FACTOR_ENABLE_SUCCESS);
|
||||
auth.setTotpKey(totpDetails.getTotpKey());
|
||||
playerCache.updatePlayer(auth);
|
||||
logger.info("Player '" + player.getName() + "' has successfully added a TOTP key to their account");
|
||||
} else {
|
||||
messages.send(player, MessageKey.ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,62 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.totp;
|
||||
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.command.PlayerCommand;
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
import fr.xephi.authme.data.auth.PlayerCache;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.message.Messages;
|
||||
import fr.xephi.authme.output.ConsoleLoggerFactory;
|
||||
import fr.xephi.authme.security.totp.TotpAuthenticator;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Command for a player to remove 2FA authentication.
|
||||
*/
|
||||
public class RemoveTotpCommand extends PlayerCommand {
|
||||
|
||||
private final ConsoleLogger logger = ConsoleLoggerFactory.get(RemoveTotpCommand.class);
|
||||
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
|
||||
@Inject
|
||||
private PlayerCache playerCache;
|
||||
|
||||
@Inject
|
||||
private TotpAuthenticator totpAuthenticator;
|
||||
|
||||
@Inject
|
||||
private Messages messages;
|
||||
|
||||
@Override
|
||||
protected void runCommand(Player player, List<String> arguments) {
|
||||
PlayerAuth auth = playerCache.getAuth(player.getName());
|
||||
if (auth == null) {
|
||||
messages.send(player, MessageKey.NOT_LOGGED_IN);
|
||||
} else if (auth.getTotpKey() == null) {
|
||||
messages.send(player, MessageKey.TWO_FACTOR_NOT_ENABLED_ERROR);
|
||||
} else {
|
||||
if (totpAuthenticator.checkCode(auth, arguments.get(0))) {
|
||||
removeTotpKeyFromDatabase(player, auth);
|
||||
} else {
|
||||
messages.send(player, MessageKey.TWO_FACTOR_INVALID_CODE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void removeTotpKeyFromDatabase(Player player, PlayerAuth auth) {
|
||||
if (dataSource.removeTotpKey(auth.getNickname())) {
|
||||
auth.setTotpKey(null);
|
||||
playerCache.updatePlayer(auth);
|
||||
messages.send(player, MessageKey.TWO_FACTOR_REMOVED_SUCCESS);
|
||||
logger.info("Player '" + player.getName() + "' removed their TOTP key");
|
||||
} else {
|
||||
messages.send(player, MessageKey.ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,29 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.totp;
|
||||
|
||||
import fr.xephi.authme.command.CommandMapper;
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.command.FoundCommandResult;
|
||||
import fr.xephi.authme.command.help.HelpProvider;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Base command for /totp.
|
||||
*/
|
||||
public class TotpBaseCommand implements ExecutableCommand {
|
||||
|
||||
@Inject
|
||||
private CommandMapper commandMapper;
|
||||
|
||||
@Inject
|
||||
private HelpProvider helpProvider;
|
||||
|
||||
@Override
|
||||
public void executeCommand(CommandSender sender, List<String> arguments) {
|
||||
FoundCommandResult result = commandMapper.mapPartsToCommand(sender, Collections.singletonList("totp"));
|
||||
helpProvider.outputHelp(sender, result, HelpProvider.SHOW_CHILDREN);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user