mirror of
https://github.com/elyby/chrly.git
synced 2024-11-23 05:33:18 +05:30
Replace /signature-verification-key endpoint with extension-specific ones /signature-verification-key.{der|pem}.
Fix Content-Disposition header
This commit is contained in:
parent
7cf5ae13be
commit
32a9fee3e6
@ -8,7 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
### Added
|
### Added
|
||||||
- `/profile/{username}` endpoint, which returns a profile and its textures, equivalent of the Mojang's
|
- `/profile/{username}` endpoint, which returns a profile and its textures, equivalent of the Mojang's
|
||||||
[UUID -> Profile + Skin/Cape endpoint](https://wiki.vg/Mojang_API#UUID_-.3E_Profile_.2B_Skin.2FCape).
|
[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.
|
- `/signature-verification-key.der` and `/signature-verification-key.pem` endpoints, which returns the public key in
|
||||||
|
`DER` or `PEM` formats for signature verification.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- [#28](https://github.com/elyby/chrly/issues/28): Added handling of corrupted data from the Mojang's username to UUID
|
- [#28](https://github.com/elyby/chrly/issues/28): Added handling of corrupted data from the Mojang's username to UUID
|
||||||
|
@ -280,11 +280,15 @@ Note that this endpoint will try to use the UUID for the stored profile in the d
|
|||||||
to the situation where the user is available in the database but has no textures, which caused them to be retrieved
|
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.
|
from the Mojang's API.
|
||||||
|
|
||||||
#### `GET /signature-verification-key`
|
#### `GET /signature-verification-key.der`
|
||||||
|
|
||||||
This endpoint returns a public key that can be used to verify textures signatures. The key is provided in `DER` format,
|
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.
|
so it can be used directly in the Authlib, without modifying the signature checking algorithm.
|
||||||
|
|
||||||
|
#### `GET /signature-verification-key.pem`
|
||||||
|
|
||||||
|
The same endpoint as the previous one, except that it returns the key in `PEM` format.
|
||||||
|
|
||||||
#### `GET /textures/signed/{username}`
|
#### `GET /textures/signed/{username}`
|
||||||
|
|
||||||
Actually, this is the [Ely.by](https://ely.by)'s feature called
|
Actually, this is the [Ely.by](https://ely.by)'s feature called
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"encoding/pem"
|
||||||
"github.com/elyby/chrly/utils"
|
"github.com/elyby/chrly/utils"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -71,7 +72,8 @@ 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("/signature-verification-key", ctx.signatureVerificationKeyHandler).Methods(http.MethodGet)
|
router.HandleFunc("/signature-verification-key.der", ctx.signatureVerificationKeyHandler).Methods(http.MethodGet)
|
||||||
|
router.HandleFunc("/signature-verification-key.pem", ctx.signatureVerificationKeyHandler).Methods(http.MethodGet)
|
||||||
|
|
||||||
return router
|
return router
|
||||||
}
|
}
|
||||||
@ -244,9 +246,21 @@ func (ctx *Skinsystem) signatureVerificationKeyHandler(response http.ResponseWri
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _ = response.Write(asn1Bytes)
|
if strings.HasSuffix(request.URL.Path, ".pem") {
|
||||||
response.Header().Set("Content-Type", "application/octet-stream")
|
publicKeyBlock := pem.Block{
|
||||||
response.Header().Set("Content-Disposition", "attachment; filename=\"yggdrasil_session_pubkey.der\"")
|
Type: "PUBLIC KEY",
|
||||||
|
Bytes: asn1Bytes,
|
||||||
|
}
|
||||||
|
|
||||||
|
publicKeyPemBytes := pem.EncodeToMemory(&publicKeyBlock)
|
||||||
|
|
||||||
|
response.Header().Set("Content-Disposition", "attachment; filename=\"yggdrasil_session_pubkey.pem\"")
|
||||||
|
_, _ = response.Write(publicKeyPemBytes)
|
||||||
|
} else {
|
||||||
|
response.Header().Set("Content-Type", "application/octet-stream")
|
||||||
|
response.Header().Set("Content-Disposition", "attachment; filename=\"yggdrasil_session_pubkey.der\"")
|
||||||
|
_, _ = response.Write(asn1Bytes)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: in v5 should be extracted into some ProfileProvider interface,
|
// TODO: in v5 should be extracted into some ProfileProvider interface,
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -1083,9 +1084,18 @@ func (suite *skinsystemTestSuite) TestProfile() {
|
|||||||
* Get profile tests cases *
|
* Get profile tests cases *
|
||||||
***************************/
|
***************************/
|
||||||
|
|
||||||
var signingKeyTestsCases = []*skinsystemTestCase{
|
type signingKeyTestCase struct {
|
||||||
|
Name string
|
||||||
|
KeyFormat string
|
||||||
|
BeforeTest func(suite *skinsystemTestSuite)
|
||||||
|
PanicErr string
|
||||||
|
AfterTest func(suite *skinsystemTestSuite, response *http.Response)
|
||||||
|
}
|
||||||
|
|
||||||
|
var signingKeyTestsCases = []*signingKeyTestCase{
|
||||||
{
|
{
|
||||||
Name: "Get public key",
|
Name: "Get public key in DER format",
|
||||||
|
KeyFormat: "DER",
|
||||||
BeforeTest: func(suite *skinsystemTestSuite) {
|
BeforeTest: func(suite *skinsystemTestSuite) {
|
||||||
pubPem, _ := pem.Decode([]byte("-----BEGIN PUBLIC KEY-----\nMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANbUpVCZkMKpfvYZ08W3lumdAaYxLBnm\nUDlzHBQH3DpYef5WCO32TDU6feIJ58A0lAywgtZ4wwi2dGHOz/1hAvcCAwEAAQ==\n-----END PUBLIC KEY-----"))
|
pubPem, _ := pem.Decode([]byte("-----BEGIN PUBLIC KEY-----\nMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANbUpVCZkMKpfvYZ08W3lumdAaYxLBnm\nUDlzHBQH3DpYef5WCO32TDU6feIJ58A0lAywgtZ4wwi2dGHOz/1hAvcCAwEAAQ==\n-----END PUBLIC KEY-----"))
|
||||||
publicKey, _ := x509.ParsePKIXPublicKey(pubPem.Bytes)
|
publicKey, _ := x509.ParsePKIXPublicKey(pubPem.Bytes)
|
||||||
@ -1095,12 +1105,31 @@ var signingKeyTestsCases = []*skinsystemTestCase{
|
|||||||
AfterTest: func(suite *skinsystemTestSuite, response *http.Response) {
|
AfterTest: func(suite *skinsystemTestSuite, response *http.Response) {
|
||||||
suite.Equal(200, response.StatusCode)
|
suite.Equal(200, response.StatusCode)
|
||||||
suite.Equal("application/octet-stream", response.Header.Get("Content-Type"))
|
suite.Equal("application/octet-stream", response.Header.Get("Content-Type"))
|
||||||
|
suite.Equal("attachment; filename=\"yggdrasil_session_pubkey.der\"", response.Header.Get("Content-Disposition"))
|
||||||
body, _ := ioutil.ReadAll(response.Body)
|
body, _ := ioutil.ReadAll(response.Body)
|
||||||
suite.Equal([]byte{48, 92, 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0, 3, 75, 0, 48, 72, 2, 65, 0, 214, 212, 165, 80, 153, 144, 194, 169, 126, 246, 25, 211, 197, 183, 150, 233, 157, 1, 166, 49, 44, 25, 230, 80, 57, 115, 28, 20, 7, 220, 58, 88, 121, 254, 86, 8, 237, 246, 76, 53, 58, 125, 226, 9, 231, 192, 52, 148, 12, 176, 130, 214, 120, 195, 8, 182, 116, 97, 206, 207, 253, 97, 2, 247, 2, 3, 1, 0, 1}, body)
|
suite.Equal([]byte{48, 92, 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0, 3, 75, 0, 48, 72, 2, 65, 0, 214, 212, 165, 80, 153, 144, 194, 169, 126, 246, 25, 211, 197, 183, 150, 233, 157, 1, 166, 49, 44, 25, 230, 80, 57, 115, 28, 20, 7, 220, 58, 88, 121, 254, 86, 8, 237, 246, 76, 53, 58, 125, 226, 9, 231, 192, 52, 148, 12, 176, 130, 214, 120, 195, 8, 182, 116, 97, 206, 207, 253, 97, 2, 247, 2, 3, 1, 0, 1}, body)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "Error while obtaining public key",
|
Name: "Get public key in PEM format",
|
||||||
|
KeyFormat: "PEM",
|
||||||
|
BeforeTest: func(suite *skinsystemTestSuite) {
|
||||||
|
pubPem, _ := pem.Decode([]byte("-----BEGIN PUBLIC KEY-----\nMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANbUpVCZkMKpfvYZ08W3lumdAaYxLBnm\nUDlzHBQH3DpYef5WCO32TDU6feIJ58A0lAywgtZ4wwi2dGHOz/1hAvcCAwEAAQ==\n-----END PUBLIC KEY-----"))
|
||||||
|
publicKey, _ := x509.ParsePKIXPublicKey(pubPem.Bytes)
|
||||||
|
|
||||||
|
suite.TexturesSigner.On("GetPublicKey").Return(publicKey, nil)
|
||||||
|
},
|
||||||
|
AfterTest: func(suite *skinsystemTestSuite, response *http.Response) {
|
||||||
|
suite.Equal(200, response.StatusCode)
|
||||||
|
suite.Equal("text/plain; charset=utf-8", response.Header.Get("Content-Type"))
|
||||||
|
suite.Equal("attachment; filename=\"yggdrasil_session_pubkey.pem\"", response.Header.Get("Content-Disposition"))
|
||||||
|
body, _ := ioutil.ReadAll(response.Body)
|
||||||
|
suite.Equal("-----BEGIN PUBLIC KEY-----\nMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANbUpVCZkMKpfvYZ08W3lumdAaYxLBnm\nUDlzHBQH3DpYef5WCO32TDU6feIJ58A0lAywgtZ4wwi2dGHOz/1hAvcCAwEAAQ==\n-----END PUBLIC KEY-----\n", string(body))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Error while obtaining public key",
|
||||||
|
KeyFormat: "DER",
|
||||||
BeforeTest: func(suite *skinsystemTestSuite) {
|
BeforeTest: func(suite *skinsystemTestSuite) {
|
||||||
suite.TexturesSigner.On("GetPublicKey").Return(nil, errors.New("textures signer error"))
|
suite.TexturesSigner.On("GetPublicKey").Return(nil, errors.New("textures signer error"))
|
||||||
},
|
},
|
||||||
@ -1113,7 +1142,7 @@ func (suite *skinsystemTestSuite) TestSignatureVerificationKey() {
|
|||||||
suite.RunSubTest(testCase.Name, func() {
|
suite.RunSubTest(testCase.Name, func() {
|
||||||
testCase.BeforeTest(suite)
|
testCase.BeforeTest(suite)
|
||||||
|
|
||||||
req := httptest.NewRequest("GET", "http://chrly/signature-verification-key", nil)
|
req := httptest.NewRequest("GET", "http://chrly/signature-verification-key."+strings.ToLower(testCase.KeyFormat), nil)
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
|
|
||||||
if testCase.PanicErr != "" {
|
if testCase.PanicErr != "" {
|
||||||
|
Loading…
Reference in New Issue
Block a user