mirror of
https://github.com/elyby/chrly.git
synced 2024-11-26 16:51:59 +05:30
Rename the signature key param.
Rename the signature verification key endpoint. Update CHANGELOG and README files
This commit is contained in:
parent
6f148a8791
commit
2bc9f8eb57
@ -6,8 +6,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## [Unreleased] - xxxx-xx-xx
|
## [Unreleased] - xxxx-xx-xx
|
||||||
### Added
|
### Added
|
||||||
- `/profile/{username}` endpoint.
|
- `/profile/{username}` endpoint, which returns a profile and its textures, equivalent of the Mojang's
|
||||||
- `/signing-key` endpoint.
|
[UUID -> Profile + Skin/Cape endpoint](https://wiki.vg/Mojang_API#UUID_-.3E_Profile_.2B_Skin.2FCape).
|
||||||
|
- `/signature-verification-key` endpoint, which returns the public key in `DER` format for signature verification.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- [#29](https://github.com/elyby/chrly/issues/29) If a previously cached UUID no longer exists,
|
- [#29](https://github.com/elyby/chrly/issues/29) If a previously cached UUID no longer exists,
|
||||||
@ -15,7 +16,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Use correct status code for error about empty response from Mojang's API.
|
- Use correct status code for error about empty response from Mojang's API.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- All skinsystem's endpoints are now returns `500` status code when an error occurred during request processing.
|
- **BREAKING**: `/cloaks/{username}` and `/textures/{username}` endpoints will no longer return a cape if there are no
|
||||||
|
textures for the requested username.
|
||||||
|
- All endpoints are now returns `500` status code when an error occurred during request processing.
|
||||||
|
|
||||||
## [4.5.0] - 2020-05-01
|
## [4.5.0] - 2020-05-01
|
||||||
### Added
|
### Added
|
||||||
|
84
README.md
84
README.md
@ -15,8 +15,9 @@ production ready.
|
|||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
You can easily install Chrly using [docker-compose](https://docs.docker.com/compose/). The configuration below (save
|
You can easily install Chrly using [docker-compose](https://docs.docker.com/compose/). The configuration below (save
|
||||||
it as `docker-compose.yml`) can be used to start a Chrly server. It relies on `CHRLY_SECRET` environment variable
|
it as `docker-compose.yml`) can be used to start a Chrly server. It relies on `CHRLY_SECRET` and `CHRLY_SIGNING_KEY`
|
||||||
that you must set before running `docker-compose up -d`. Other possible variables are described below.
|
environment variables that you must set before running `docker-compose up -d`. Other possible variables are described
|
||||||
|
below.
|
||||||
|
|
||||||
```yml
|
```yml
|
||||||
version: '2'
|
version: '2'
|
||||||
@ -33,6 +34,7 @@ services:
|
|||||||
- "80:80"
|
- "80:80"
|
||||||
environment:
|
environment:
|
||||||
CHRLY_SECRET: replace_this_value_in_production
|
CHRLY_SECRET: replace_this_value_in_production
|
||||||
|
CHRLY_SIGNING_KEY: base64:LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlCT3dJQkFBSkJBTmJVcFZDWmtNS3BmdllaMDhXM2x1bWRBYVl4TEJubVVEbHpIQlFIM0RwWWVmNVdDTzMyClREVTZmZUlKNThBMGxBeXdndFo0d3dpMmRHSE96LzFoQXZjQ0F3RUFBUUpBSXRheFNIVGU2UEtieUVVLzlweGoKT05kaFlSWXdWTExvNTZnbk1ZaGt5b0VxYWFNc2ZvdjhoaG9lcGtZWkJNdlpGQjJiRE9zUTJTYUorRTJlaUJPNApBUUloQVBzc1MwK0JSOXcwYk9kbWpHcW1kRTlOck41VUpRY09XMTNzMjkrNlF6VUJBaUVBMnZXT2VwQTVBcGl1CnBFQTNwd29HZGtWQ3JOU25uS2pEUXpEWEJucGQzL2NDSUVGTmQ5c1k0cVVHNEZXZFhONlJubVhMN1NqMHVaZkgKRE13enU4ckVNNXNCQWlFQWh2ZG9ETnFMbWJNZHEzYytGc1BTT2VMMWQyMVpwL0pLOGtiUHRGbUhOZjhDSVFEVgo2RlNaRHd2V2Z1eGFNN0JzeWNRT05rakRCVFBOdStscWN0SkJHbkJ2M0E9PQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=
|
||||||
|
|
||||||
redis:
|
redis:
|
||||||
image: redis:4.0-32bit
|
image: redis:4.0-32bit
|
||||||
@ -41,6 +43,11 @@ services:
|
|||||||
- ./data/redis:/data
|
- ./data/redis:/data
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Tip**: to generate a value for the `CHRLY_SIGNING_KEY` use the command below and then join it with a `base64:` prefix.
|
||||||
|
```sh
|
||||||
|
openssl genrsa 4096 | base64 -w0
|
||||||
|
```
|
||||||
|
|
||||||
Chrly uses some volumes to persist storage for capes and Redis database. The configuration above mounts them to
|
Chrly uses some volumes to persist storage for capes and Redis database. The configuration above mounts them to
|
||||||
the host machine to do not lose data on container recreations.
|
the host machine to do not lose data on container recreations.
|
||||||
|
|
||||||
@ -48,11 +55,10 @@ the host machine to do not lose data on container recreations.
|
|||||||
|
|
||||||
Application's configuration is based on the environment variables. You can adjust config by modifying `environment` key
|
Application's configuration is based on the environment variables. You can adjust config by modifying `environment` key
|
||||||
inside your `docker-compose.yml` file. After value will have been changed, container should be stopped and recreated.
|
inside your `docker-compose.yml` file. After value will have been changed, container should be stopped and recreated.
|
||||||
If environment variables have been changed, Docker will automatically recreate the container, so you only need to `stop`
|
If environment variables have been changed, Docker will automatically recreate the container, so you only need to `up`
|
||||||
and `up` it:
|
it again:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
docker-compose stop app
|
|
||||||
docker-compose up -d app
|
docker-compose up -d app
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -182,7 +188,7 @@ If something goes wrong, you can always access logs by executing `docker-compose
|
|||||||
|
|
||||||
## Endpoints
|
## Endpoints
|
||||||
|
|
||||||
Each endpoint that accepts `username` as a part of an url takes it case insensitive. `.png` part can be omitted too.
|
Each endpoint that accepts `username` as a part of an url takes it case-insensitive. The `.png` postfix can be omitted.
|
||||||
|
|
||||||
#### `GET /skins/{username}.png`
|
#### `GET /skins/{username}.png`
|
||||||
|
|
||||||
@ -220,11 +226,71 @@ That request is handy in case when your server implements authentication for a g
|
|||||||
operation) and you have to respond with hasJoined request with an actual user textures. You have to simply send request
|
operation) and you have to respond with hasJoined request with an actual user textures. You have to simply send request
|
||||||
to the Chrly server and put the result in your hasJoined response.
|
to the Chrly server and put the result in your hasJoined response.
|
||||||
|
|
||||||
|
#### `GET /profile/{username}`
|
||||||
|
|
||||||
|
This endpoint behaves exactly like the
|
||||||
|
[Mojang's UUID -> Profile + Skin/Cape endpoint](https://wiki.vg/Mojang_API#UUID_-.3E_Profile_.2B_Skin.2FCape), but using
|
||||||
|
a username instead of the UUID. Just like in the Mojang's API, you can append `?unsigned=false` part to URL to sign
|
||||||
|
the `textures` property. If the textures for the requested username aren't found, it'll request them through the
|
||||||
|
Mojang's API, but the Mojang's signature will be discarded and the textures will be re-signed using the signature key
|
||||||
|
for your Chrly instance.
|
||||||
|
|
||||||
|
Response example:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "0f657aa8bfbe415db7005750090d3af3",
|
||||||
|
"name": "username",
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "textures",
|
||||||
|
"signature": "signature value",
|
||||||
|
"value": "base64 encoded value"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "chrly",
|
||||||
|
"value": "how do you tame a horse in Minecraft?"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The base64 `value` string for the `textures` property decoded:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"timestamp": 1614387238630,
|
||||||
|
"profileId": "0f657aa8bfbe415db7005750090d3af3",
|
||||||
|
"profileName": "username",
|
||||||
|
"textures": {
|
||||||
|
"SKIN": {
|
||||||
|
"url": "http://example.com/skin.png"
|
||||||
|
},
|
||||||
|
"CAPE": {
|
||||||
|
"url": "http://example.com/cape.png"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If username can't be found locally and can't be obtained from the Mojang's API, empty response with `204` status code
|
||||||
|
will be sent.
|
||||||
|
|
||||||
|
Note that this endpoint will try to use the UUID for the stored profile in the database. This is an edge case, related
|
||||||
|
to the situation where the user is available in the database but has no textures, which caused them to be retrieved
|
||||||
|
from the Mojang's API.
|
||||||
|
|
||||||
|
#### `GET /signature-verification-key`
|
||||||
|
|
||||||
|
This endpoint returns a public key that can be used to verify textures signatures. The key is provided in `DER` format,
|
||||||
|
so it can be used directly in the Authlib, without modifying the signature checking algorithm.
|
||||||
|
|
||||||
#### `GET /textures/signed/{username}`
|
#### `GET /textures/signed/{username}`
|
||||||
|
|
||||||
Actually, it's [Ely.by](http://ely.by) feature called [Server Skins System](http://ely.by/server-skins-system), but if
|
Actually, this is the [Ely.by](https://ely.by)'s feature called
|
||||||
you have your own source of Mojang's signatures, then you can pass it with textures and it'll be displayed in response
|
[Server Skins System](https://ely.by/server-skins-system), but if you have your own source of Mojang's signatures,
|
||||||
of this endpoint. Received response should be directly sent to the client without any modification via game server API.
|
then you can pass it with textures and it'll be displayed in response of this endpoint. Received response should be
|
||||||
|
directly sent to the client without any modification via game server API.
|
||||||
|
|
||||||
Response example:
|
Response example:
|
||||||
|
|
||||||
|
@ -20,11 +20,9 @@ var signer = di.Options(
|
|||||||
)
|
)
|
||||||
|
|
||||||
func newTexturesSigner(config *viper.Viper) (*Signer, error) {
|
func newTexturesSigner(config *viper.Viper) (*Signer, error) {
|
||||||
// TODO: add CHANGELOG and README entries about this variable
|
keyStr := config.GetString("chrly.signing.key")
|
||||||
// TODO: rename param variable
|
|
||||||
keyStr := config.GetString("textures.signer.pem")
|
|
||||||
if keyStr == "" {
|
if keyStr == "" {
|
||||||
return nil, errors.New("texturesSigner.pem must be set in order to sign textures")
|
return nil, errors.New("chrly.signing.key must be set in order to sign textures")
|
||||||
}
|
}
|
||||||
|
|
||||||
var keyBytes []byte
|
var keyBytes []byte
|
||||||
|
@ -71,7 +71,7 @@ func (ctx *Skinsystem) Handler() *mux.Router {
|
|||||||
router.HandleFunc("/skins", ctx.skinGetHandler).Methods(http.MethodGet)
|
router.HandleFunc("/skins", ctx.skinGetHandler).Methods(http.MethodGet)
|
||||||
router.HandleFunc("/cloaks", ctx.capeGetHandler).Methods(http.MethodGet)
|
router.HandleFunc("/cloaks", ctx.capeGetHandler).Methods(http.MethodGet)
|
||||||
// Utils
|
// Utils
|
||||||
router.HandleFunc("/signing-key", ctx.signingKeyHandler).Methods(http.MethodGet)
|
router.HandleFunc("/signature-verification-key", ctx.signatureVerificationKeyHandler).Methods(http.MethodGet)
|
||||||
|
|
||||||
return router
|
return router
|
||||||
}
|
}
|
||||||
@ -102,7 +102,6 @@ func (ctx *Skinsystem) skinGetHandler(response http.ResponseWriter, request *htt
|
|||||||
ctx.skinHandler(response, request)
|
ctx.skinHandler(response, request)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: write CHANGELOG about breaking change in this method
|
|
||||||
func (ctx *Skinsystem) capeHandler(response http.ResponseWriter, request *http.Request) {
|
func (ctx *Skinsystem) capeHandler(response http.ResponseWriter, request *http.Request) {
|
||||||
profile, err := ctx.getProfile(request, true)
|
profile, err := ctx.getProfile(request, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -182,7 +181,6 @@ func (ctx *Skinsystem) signedTexturesHandler(response http.ResponseWriter, reque
|
|||||||
_, _ = response.Write(responseJson)
|
_, _ = response.Write(responseJson)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: add README entry about this method
|
|
||||||
func (ctx *Skinsystem) profileHandler(response http.ResponseWriter, request *http.Request) {
|
func (ctx *Skinsystem) profileHandler(response http.ResponseWriter, request *http.Request) {
|
||||||
profile, err := ctx.getProfile(request, true)
|
profile, err := ctx.getProfile(request, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -235,8 +233,7 @@ func (ctx *Skinsystem) profileHandler(response http.ResponseWriter, request *htt
|
|||||||
_, _ = response.Write(responseJson)
|
_, _ = response.Write(responseJson)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: add README entry about this method
|
func (ctx *Skinsystem) signatureVerificationKeyHandler(response http.ResponseWriter, request *http.Request) {
|
||||||
func (ctx *Skinsystem) signingKeyHandler(response http.ResponseWriter, request *http.Request) {
|
|
||||||
publicKey, err := ctx.TexturesSigner.GetPublicKey()
|
publicKey, err := ctx.TexturesSigner.GetPublicKey()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@ -1108,12 +1108,12 @@ var signingKeyTestsCases = []*skinsystemTestCase{
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *skinsystemTestSuite) TestSigningKey() {
|
func (suite *skinsystemTestSuite) TestSignatureVerificationKey() {
|
||||||
for _, testCase := range signingKeyTestsCases {
|
for _, testCase := range signingKeyTestsCases {
|
||||||
suite.RunSubTest(testCase.Name, func() {
|
suite.RunSubTest(testCase.Name, func() {
|
||||||
testCase.BeforeTest(suite)
|
testCase.BeforeTest(suite)
|
||||||
|
|
||||||
req := httptest.NewRequest("GET", "http://chrly/signing-key", nil)
|
req := httptest.NewRequest("GET", "http://chrly/signature-verification-key", nil)
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
|
|
||||||
if testCase.PanicErr != "" {
|
if testCase.PanicErr != "" {
|
||||||
|
Loading…
Reference in New Issue
Block a user