Rename the signature key param.

Rename the signature verification key endpoint.
Update CHANGELOG and README files
This commit is contained in:
ErickSkrauch 2021-02-27 02:37:59 +01:00
parent 6f148a8791
commit 2bc9f8eb57
5 changed files with 87 additions and 23 deletions

View File

@ -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

View File

@ -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:

View File

@ -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

View File

@ -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)

View File

@ -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 != "" {