mirror of
https://github.com/elyby/chrly.git
synced 2024-11-23 05:33:18 +05:30
Fixes CHRLY-B. Handle the case when the textures property is not presented in Mojang's response
This commit is contained in:
parent
8001eab9db
commit
05c68c6ba6
@ -12,6 +12,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- New configuration params: `MOJANG_API_BASE_URL` and `MOJANG_SESSION_SERVER_BASE_URL`, that allow you to spoof
|
- New configuration params: `MOJANG_API_BASE_URL` and `MOJANG_SESSION_SERVER_BASE_URL`, that allow you to spoof
|
||||||
Mojang API base addresses.
|
Mojang API base addresses.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Handle the case when there is no textures property in Mojang's response.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- `ely.skinsystem.{hostname}.app.mojang_textures.usernames.round_time` timer will not be recorded if the iteration was
|
- `ely.skinsystem.{hostname}.app.mojang_textures.usernames.round_time` timer will not be recorded if the iteration was
|
||||||
empty.
|
empty.
|
||||||
|
@ -3,11 +3,11 @@ package mojang
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -19,14 +19,17 @@ var HttpClient = &http.Client{
|
|||||||
}
|
}
|
||||||
|
|
||||||
type SignedTexturesResponse struct {
|
type SignedTexturesResponse struct {
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Props []*Property `json:"properties"`
|
Props []*Property `json:"properties"`
|
||||||
|
|
||||||
|
once sync.Once
|
||||||
decodedTextures *TexturesProp
|
decodedTextures *TexturesProp
|
||||||
|
decodedErr error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *SignedTexturesResponse) DecodeTextures() (*TexturesProp, error) {
|
func (t *SignedTexturesResponse) DecodeTextures() (*TexturesProp, error) {
|
||||||
if t.decodedTextures == nil {
|
t.once.Do(func() {
|
||||||
var texturesProp string
|
var texturesProp string
|
||||||
for _, prop := range t.Props {
|
for _, prop := range t.Props {
|
||||||
if prop.Name == "textures" {
|
if prop.Name == "textures" {
|
||||||
@ -36,18 +39,18 @@ func (t *SignedTexturesResponse) DecodeTextures() (*TexturesProp, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if texturesProp == "" {
|
if texturesProp == "" {
|
||||||
return nil, errors.New("unable to find the textures property")
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
decodedTextures, err := DecodeTextures(texturesProp)
|
decodedTextures, err := DecodeTextures(texturesProp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
t.decodedErr = err
|
||||||
|
} else {
|
||||||
|
t.decodedTextures = decodedTextures
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
|
||||||
t.decodedTextures = decodedTextures
|
return t.decodedTextures, t.decodedErr
|
||||||
}
|
|
||||||
|
|
||||||
return t.decodedTextures, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Property struct {
|
type Property struct {
|
||||||
|
@ -32,7 +32,7 @@ func TestSignedTexturesResponse(t *testing.T) {
|
|||||||
Props: []*Property{},
|
Props: []*Property{},
|
||||||
}
|
}
|
||||||
textures, err := obj.DecodeTextures()
|
textures, err := obj.DecodeTextures()
|
||||||
testify.Errorf(t, err, "unable to find the textures property")
|
testify.Nil(t, err)
|
||||||
testify.Nil(t, textures)
|
testify.Nil(t, textures)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ package http
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
@ -67,6 +66,11 @@ func (ctx *Skinsystem) skinHandler(response http.ResponseWriter, request *http.R
|
|||||||
}
|
}
|
||||||
|
|
||||||
texturesProp, _ := mojangTextures.DecodeTextures()
|
texturesProp, _ := mojangTextures.DecodeTextures()
|
||||||
|
if texturesProp == nil {
|
||||||
|
response.WriteHeader(http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
skin := texturesProp.Textures.Skin
|
skin := texturesProp.Textures.Skin
|
||||||
if skin == nil {
|
if skin == nil {
|
||||||
response.WriteHeader(http.StatusNotFound)
|
response.WriteHeader(http.StatusNotFound)
|
||||||
@ -105,6 +109,11 @@ func (ctx *Skinsystem) capeHandler(response http.ResponseWriter, request *http.R
|
|||||||
}
|
}
|
||||||
|
|
||||||
texturesProp, _ := mojangTextures.DecodeTextures()
|
texturesProp, _ := mojangTextures.DecodeTextures()
|
||||||
|
if texturesProp == nil {
|
||||||
|
response.WriteHeader(http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
cape := texturesProp.Textures.Cape
|
cape := texturesProp.Textures.Cape
|
||||||
if cape == nil {
|
if cape == nil {
|
||||||
response.WriteHeader(http.StatusNotFound)
|
response.WriteHeader(http.StatusNotFound)
|
||||||
@ -164,8 +173,7 @@ func (ctx *Skinsystem) texturesHandler(response http.ResponseWriter, request *ht
|
|||||||
|
|
||||||
texturesProp, _ := mojangTextures.DecodeTextures()
|
texturesProp, _ := mojangTextures.DecodeTextures()
|
||||||
if texturesProp == nil {
|
if texturesProp == nil {
|
||||||
ctx.Emit("skinsystem:error", errors.New("unable to find textures property"))
|
response.WriteHeader(http.StatusNoContent)
|
||||||
apiServerError(response)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,7 +166,7 @@ var skinsTestsCases = []*skinsystemTestCase{
|
|||||||
Name: "Username doesn't exists on the local storage, but exists on Mojang and has textures",
|
Name: "Username doesn't exists on the local storage, but exists on Mojang and has textures",
|
||||||
BeforeTest: func(suite *skinsystemTestSuite) {
|
BeforeTest: func(suite *skinsystemTestSuite) {
|
||||||
suite.SkinsRepository.On("FindSkinByUsername", "mock_username").Return(nil, nil)
|
suite.SkinsRepository.On("FindSkinByUsername", "mock_username").Return(nil, nil)
|
||||||
suite.MojangTexturesProvider.On("GetForUsername", "mock_username").Return(createMojangResponse(true, false), nil)
|
suite.MojangTexturesProvider.On("GetForUsername", "mock_username").Return(createMojangResponseWithTextures(true, false), nil)
|
||||||
},
|
},
|
||||||
AfterTest: func(suite *skinsystemTestSuite, response *http.Response) {
|
AfterTest: func(suite *skinsystemTestSuite, response *http.Response) {
|
||||||
suite.Equal(301, response.StatusCode)
|
suite.Equal(301, response.StatusCode)
|
||||||
@ -174,10 +174,20 @@ var skinsTestsCases = []*skinsystemTestCase{
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "Username doesn't exists on the local storage, but exists on Mojang and has no textures",
|
Name: "Username doesn't exists on the local storage, but exists on Mojang and has no skin texture",
|
||||||
BeforeTest: func(suite *skinsystemTestSuite) {
|
BeforeTest: func(suite *skinsystemTestSuite) {
|
||||||
suite.SkinsRepository.On("FindSkinByUsername", "mock_username").Return(nil, nil)
|
suite.SkinsRepository.On("FindSkinByUsername", "mock_username").Return(nil, nil)
|
||||||
suite.MojangTexturesProvider.On("GetForUsername", "mock_username").Return(createMojangResponse(false, false), nil)
|
suite.MojangTexturesProvider.On("GetForUsername", "mock_username").Return(createMojangResponseWithTextures(false, false), nil)
|
||||||
|
},
|
||||||
|
AfterTest: func(suite *skinsystemTestSuite, response *http.Response) {
|
||||||
|
suite.Equal(404, response.StatusCode)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Username doesn't exists on the local storage, but exists on Mojang and has an empty properties",
|
||||||
|
BeforeTest: func(suite *skinsystemTestSuite) {
|
||||||
|
suite.SkinsRepository.On("FindSkinByUsername", "mock_username").Return(nil, nil)
|
||||||
|
suite.MojangTexturesProvider.On("GetForUsername", "mock_username").Return(createEmptyMojangResponse(), nil)
|
||||||
},
|
},
|
||||||
AfterTest: func(suite *skinsystemTestSuite, response *http.Response) {
|
AfterTest: func(suite *skinsystemTestSuite, response *http.Response) {
|
||||||
suite.Equal(404, response.StatusCode)
|
suite.Equal(404, response.StatusCode)
|
||||||
@ -270,7 +280,7 @@ var capesTestsCases = []*skinsystemTestCase{
|
|||||||
Name: "Username doesn't exists on the local storage, but exists on Mojang and has textures",
|
Name: "Username doesn't exists on the local storage, but exists on Mojang and has textures",
|
||||||
BeforeTest: func(suite *skinsystemTestSuite) {
|
BeforeTest: func(suite *skinsystemTestSuite) {
|
||||||
suite.CapesRepository.On("FindCapeByUsername", "mock_username").Return(nil, nil)
|
suite.CapesRepository.On("FindCapeByUsername", "mock_username").Return(nil, nil)
|
||||||
suite.MojangTexturesProvider.On("GetForUsername", "mock_username").Return(createMojangResponse(true, true), nil)
|
suite.MojangTexturesProvider.On("GetForUsername", "mock_username").Return(createMojangResponseWithTextures(true, true), nil)
|
||||||
},
|
},
|
||||||
AfterTest: func(suite *skinsystemTestSuite, response *http.Response) {
|
AfterTest: func(suite *skinsystemTestSuite, response *http.Response) {
|
||||||
suite.Equal(301, response.StatusCode)
|
suite.Equal(301, response.StatusCode)
|
||||||
@ -278,10 +288,20 @@ var capesTestsCases = []*skinsystemTestCase{
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "Username doesn't exists on the local storage, but exists on Mojang and has no textures",
|
Name: "Username doesn't exists on the local storage, but exists on Mojang and has no cape texture",
|
||||||
BeforeTest: func(suite *skinsystemTestSuite) {
|
BeforeTest: func(suite *skinsystemTestSuite) {
|
||||||
suite.CapesRepository.On("FindCapeByUsername", "mock_username").Return(nil, nil)
|
suite.CapesRepository.On("FindCapeByUsername", "mock_username").Return(nil, nil)
|
||||||
suite.MojangTexturesProvider.On("GetForUsername", "mock_username").Return(createMojangResponse(false, false), nil)
|
suite.MojangTexturesProvider.On("GetForUsername", "mock_username").Return(createMojangResponseWithTextures(false, false), nil)
|
||||||
|
},
|
||||||
|
AfterTest: func(suite *skinsystemTestSuite, response *http.Response) {
|
||||||
|
suite.Equal(404, response.StatusCode)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Username doesn't exists on the local storage, but exists on Mojang and has an empty properties",
|
||||||
|
BeforeTest: func(suite *skinsystemTestSuite) {
|
||||||
|
suite.CapesRepository.On("FindCapeByUsername", "mock_username").Return(nil, nil)
|
||||||
|
suite.MojangTexturesProvider.On("GetForUsername", "mock_username").Return(createEmptyMojangResponse(), nil)
|
||||||
},
|
},
|
||||||
AfterTest: func(suite *skinsystemTestSuite, response *http.Response) {
|
AfterTest: func(suite *skinsystemTestSuite, response *http.Response) {
|
||||||
suite.Equal(404, response.StatusCode)
|
suite.Equal(404, response.StatusCode)
|
||||||
@ -439,7 +459,7 @@ var texturesTestsCases = []*skinsystemTestCase{
|
|||||||
BeforeTest: func(suite *skinsystemTestSuite) {
|
BeforeTest: func(suite *skinsystemTestSuite) {
|
||||||
suite.SkinsRepository.On("FindSkinByUsername", "mock_username").Return(nil, nil)
|
suite.SkinsRepository.On("FindSkinByUsername", "mock_username").Return(nil, nil)
|
||||||
suite.CapesRepository.On("FindCapeByUsername", "mock_username").Return(nil, nil)
|
suite.CapesRepository.On("FindCapeByUsername", "mock_username").Return(nil, nil)
|
||||||
suite.MojangTexturesProvider.On("GetForUsername", "mock_username").Once().Return(createMojangResponse(true, true), nil)
|
suite.MojangTexturesProvider.On("GetForUsername", "mock_username").Once().Return(createMojangResponseWithTextures(true, true), nil)
|
||||||
},
|
},
|
||||||
AfterTest: func(suite *skinsystemTestSuite, response *http.Response) {
|
AfterTest: func(suite *skinsystemTestSuite, response *http.Response) {
|
||||||
suite.Equal(200, response.StatusCode)
|
suite.Equal(200, response.StatusCode)
|
||||||
@ -456,11 +476,22 @@ var texturesTestsCases = []*skinsystemTestCase{
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "Username not exists, but Mojang profile available, but there is no textures",
|
Name: "Username not exists, but Mojang profile available, but there is an empty skin and cape textures",
|
||||||
BeforeTest: func(suite *skinsystemTestSuite) {
|
BeforeTest: func(suite *skinsystemTestSuite) {
|
||||||
suite.SkinsRepository.On("FindSkinByUsername", "mock_username").Return(nil, nil)
|
suite.SkinsRepository.On("FindSkinByUsername", "mock_username").Return(nil, nil)
|
||||||
suite.CapesRepository.On("FindCapeByUsername", "mock_username").Return(nil, nil)
|
suite.CapesRepository.On("FindCapeByUsername", "mock_username").Return(nil, nil)
|
||||||
suite.MojangTexturesProvider.On("GetForUsername", "mock_username").Once().Return(createMojangResponse(false, false), nil)
|
suite.MojangTexturesProvider.On("GetForUsername", "mock_username").Once().Return(createMojangResponseWithTextures(false, false), nil)
|
||||||
|
},
|
||||||
|
AfterTest: func(suite *skinsystemTestSuite, response *http.Response) {
|
||||||
|
suite.Equal(204, response.StatusCode)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Username not exists, but Mojang profile available, but there is an empty properties",
|
||||||
|
BeforeTest: func(suite *skinsystemTestSuite) {
|
||||||
|
suite.SkinsRepository.On("FindSkinByUsername", "mock_username").Return(nil, nil)
|
||||||
|
suite.CapesRepository.On("FindCapeByUsername", "mock_username").Return(nil, nil)
|
||||||
|
suite.MojangTexturesProvider.On("GetForUsername", "mock_username").Once().Return(createEmptyMojangResponse(), nil)
|
||||||
},
|
},
|
||||||
AfterTest: func(suite *skinsystemTestSuite, response *http.Response) {
|
AfterTest: func(suite *skinsystemTestSuite, response *http.Response) {
|
||||||
suite.Equal(204, response.StatusCode)
|
suite.Equal(204, response.StatusCode)
|
||||||
@ -567,7 +598,7 @@ var signedTexturesTestsCases = []*signedTexturesTestCase{
|
|||||||
AllowProxy: true,
|
AllowProxy: true,
|
||||||
BeforeTest: func(suite *skinsystemTestSuite) {
|
BeforeTest: func(suite *skinsystemTestSuite) {
|
||||||
suite.SkinsRepository.On("FindSkinByUsername", "mock_username").Return(nil, nil)
|
suite.SkinsRepository.On("FindSkinByUsername", "mock_username").Return(nil, nil)
|
||||||
suite.MojangTexturesProvider.On("GetForUsername", "mock_username").Return(createMojangResponse(true, false), nil)
|
suite.MojangTexturesProvider.On("GetForUsername", "mock_username").Return(createMojangResponseWithTextures(true, false), nil)
|
||||||
},
|
},
|
||||||
AfterTest: func(suite *skinsystemTestSuite, response *http.Response) {
|
AfterTest: func(suite *skinsystemTestSuite, response *http.Response) {
|
||||||
suite.Equal(200, response.StatusCode)
|
suite.Equal(200, response.StatusCode)
|
||||||
@ -666,7 +697,15 @@ func createCapeModel() *model.Cape {
|
|||||||
return &model.Cape{File: bytes.NewReader(createCape())}
|
return &model.Cape{File: bytes.NewReader(createCape())}
|
||||||
}
|
}
|
||||||
|
|
||||||
func createMojangResponse(includeSkin bool, includeCape bool) *mojang.SignedTexturesResponse {
|
func createEmptyMojangResponse() *mojang.SignedTexturesResponse {
|
||||||
|
return &mojang.SignedTexturesResponse{
|
||||||
|
Id: "00000000000000000000000000000000",
|
||||||
|
Name: "mock_username",
|
||||||
|
Props: []*mojang.Property{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createMojangResponseWithTextures(includeSkin bool, includeCape bool) *mojang.SignedTexturesResponse {
|
||||||
timeZone, _ := time.LoadLocation("Europe/Minsk")
|
timeZone, _ := time.LoadLocation("Europe/Minsk")
|
||||||
textures := &mojang.TexturesProp{
|
textures := &mojang.TexturesProp{
|
||||||
Timestamp: time.Date(2019, 4, 27, 23, 56, 12, 0, timeZone).Unix(),
|
Timestamp: time.Date(2019, 4, 27, 23, 56, 12, 0, timeZone).Unix(),
|
||||||
@ -687,16 +726,11 @@ func createMojangResponse(includeSkin bool, includeCape bool) *mojang.SignedText
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
response := &mojang.SignedTexturesResponse{
|
response := createEmptyMojangResponse()
|
||||||
Id: "00000000000000000000000000000000",
|
response.Props = append(response.Props, &mojang.Property{
|
||||||
Name: "mock_username",
|
Name: "textures",
|
||||||
Props: []*mojang.Property{
|
Value: mojang.EncodeTextures(textures),
|
||||||
{
|
})
|
||||||
Name: "textures",
|
|
||||||
Value: mojang.EncodeTextures(textures),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
return response
|
return response
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,9 @@
|
|||||||
package mojangtextures
|
package mojangtextures
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/getsentry/raven-go"
|
|
||||||
|
|
||||||
"github.com/elyby/chrly/api/mojang"
|
"github.com/elyby/chrly/api/mojang"
|
||||||
|
|
||||||
"github.com/tevino/abool"
|
"github.com/tevino/abool"
|
||||||
@ -80,19 +77,6 @@ func (s *InMemoryTexturesStorage) StoreTextures(uuid string, textures *mojang.Si
|
|||||||
if textures != nil {
|
if textures != nil {
|
||||||
decoded, err := textures.DecodeTextures()
|
decoded, err := textures.DecodeTextures()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tags := map[string]string{
|
|
||||||
"textures.id": textures.Id,
|
|
||||||
"textures.name": textures.Name,
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, prop := range textures.Props {
|
|
||||||
tags[fmt.Sprintf("textures.props[%d].name", i)] = prop.Name
|
|
||||||
tags[fmt.Sprintf("textures.props[%d].value", i)] = prop.Value
|
|
||||||
tags[fmt.Sprintf("textures.props[%d].signature", i)] = prop.Signature
|
|
||||||
}
|
|
||||||
|
|
||||||
raven.CaptureErrorAndWait(err, tags)
|
|
||||||
|
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,12 +111,18 @@ func TestInMemoryTexturesStorage_StoreTextures(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("should panic if textures prop is not decoded", func(t *testing.T) {
|
t.Run("should panic if textures prop is not decoded", func(t *testing.T) {
|
||||||
toStore := &mojang.SignedTexturesResponse{
|
toStore := &mojang.SignedTexturesResponse{
|
||||||
Id: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
|
Id: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
|
||||||
Name: "mock",
|
Name: "mock",
|
||||||
Props: []*mojang.Property{},
|
Props: []*mojang.Property{
|
||||||
|
{
|
||||||
|
Name: "textures",
|
||||||
|
Value: "totally not base64 encoded json",
|
||||||
|
Signature: "totally not base64 encoded signature",
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.PanicsWithError(t, "unable to find the textures property", func() {
|
assert.Panics(t, func() {
|
||||||
storage := NewInMemoryTexturesStorage()
|
storage := NewInMemoryTexturesStorage()
|
||||||
storage.StoreTextures("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", toStore)
|
storage.StoreTextures("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", toStore)
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user