mirror of
				https://github.com/elyby/chrly.git
				synced 2025-05-31 14:11:51 +05:30 
			
		
		
		
	Rename the signature key param.
Rename the signature verification key endpoint. Update CHANGELOG and README files
This commit is contained in:
		| @@ -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 != "" { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user