原始提交
This commit is contained in:
40
model/authentication_session.go
Normal file
40
model/authentication_session.go
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) 2022. Gardel <sunxinao@hotmail.com> and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package model
|
||||
|
||||
import "time"
|
||||
|
||||
type AuthenticationSession struct {
|
||||
ServerId string
|
||||
Token Token
|
||||
Ip string
|
||||
createAt int64
|
||||
}
|
||||
|
||||
func NewAuthenticationSession(serverId string, token *Token, ip string) (session AuthenticationSession) {
|
||||
session.ServerId = serverId
|
||||
session.Token = *token
|
||||
session.Ip = ip
|
||||
session.createAt = time.Now().UnixMilli()
|
||||
return session
|
||||
}
|
||||
|
||||
func (s *AuthenticationSession) HasExpired() bool {
|
||||
d := time.Now().Sub(time.UnixMilli(s.createAt))
|
||||
return d > 30*time.Second
|
||||
}
|
130
model/profile.go
Normal file
130
model/profile.go
Normal file
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright (C) 2022. Gardel <sunxinao@hotmail.com> and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/google/uuid"
|
||||
"time"
|
||||
"yggdrasil-go/util"
|
||||
)
|
||||
|
||||
type Profile struct {
|
||||
Id uuid.UUID
|
||||
Name string
|
||||
ModelType ModelType
|
||||
Textures map[string]string
|
||||
}
|
||||
|
||||
type ModelType string
|
||||
|
||||
const (
|
||||
STEVE ModelType = "default"
|
||||
ALEX = "slim"
|
||||
)
|
||||
|
||||
type MetadataType map[string]interface{}
|
||||
|
||||
type SkinTexture struct {
|
||||
Url string `json:"url"`
|
||||
Metadata *MetadataType `json:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
type CapeTexture struct {
|
||||
Url string `json:"url"`
|
||||
}
|
||||
|
||||
type TexturesType struct {
|
||||
SKIN *SkinTexture `json:"SKIN,omitempty"`
|
||||
CAPE *CapeTexture `json:"CAPE,omitempty"`
|
||||
}
|
||||
|
||||
func NewProfile(id uuid.UUID, name string, modelType ModelType, serializedTextures string) (this Profile) {
|
||||
this.Id = id
|
||||
this.Name = name
|
||||
this.ModelType = modelType
|
||||
if len(serializedTextures) < 2 {
|
||||
serializedTextures = "{}"
|
||||
}
|
||||
err := json.Unmarshal([]byte(serializedTextures), &this.Textures)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
type ProfileResponse struct {
|
||||
Name string `json:"name" binding:"required"`
|
||||
Id string `json:"id" binding:"required"`
|
||||
}
|
||||
|
||||
func (p *Profile) ToSimpleResponse() ProfileResponse {
|
||||
return ProfileResponse{
|
||||
Id: util.UnsignedString(p.Id),
|
||||
Name: p.Name,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Profile) ToCompleteResponse(signed bool, textureBaseUrl string) (map[string]interface{}, error) {
|
||||
textures := TexturesType{}
|
||||
if hash, ok := p.Textures["SKIN"]; ok {
|
||||
skin := SkinTexture{
|
||||
Url: textureBaseUrl + "/" + hash,
|
||||
}
|
||||
if p.ModelType == ALEX {
|
||||
m := MetadataType{
|
||||
"model": ALEX,
|
||||
}
|
||||
skin.Metadata = &m
|
||||
}
|
||||
textures.SKIN = &skin
|
||||
}
|
||||
if hash, ok := p.Textures["CAPE"]; ok {
|
||||
cape := CapeTexture{
|
||||
Url: textureBaseUrl + "/" + hash,
|
||||
}
|
||||
textures.CAPE = &cape
|
||||
}
|
||||
texturesStr, err := util.EncodeBase64(util.Property{
|
||||
Name: "timestamp", Value: time.Now().UnixMilli(),
|
||||
}, util.Property{
|
||||
Name: "profileId", Value: util.UnsignedString(p.Id),
|
||||
}, util.Property{
|
||||
Name: "profileName", Value: p.Name,
|
||||
}, util.Property{
|
||||
Name: "textures", Value: textures,
|
||||
}, util.Property{
|
||||
Name: "signatureRequired", Value: signed,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
properties := util.Properties(signed,
|
||||
util.StringProperty{Name: "textures", Value: texturesStr},
|
||||
util.StringProperty{Name: "uploadableTextures", Value: "skin,cape"},
|
||||
)
|
||||
return map[string]interface{}{
|
||||
"id": util.UnsignedString(p.Id),
|
||||
"name": p.Name,
|
||||
"properties": properties,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *Profile) Equals(another *Profile) bool {
|
||||
return p == another || p.Id == another.Id
|
||||
}
|
72
model/texture.go
Normal file
72
model/texture.go
Normal file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (C) 2022. Gardel <sunxinao@hotmail.com> and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"image"
|
||||
"image/color"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Texture struct {
|
||||
Hash string `gorm:"size:64;primaryKey"`
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
Data []byte `gorm:"not null"`
|
||||
Used uint `gorm:"not null"`
|
||||
}
|
||||
|
||||
func ComputeTextureId(img image.Image) string {
|
||||
digest := sha256.New()
|
||||
bound := img.Bounds()
|
||||
width := bound.Dx()
|
||||
height := bound.Dy()
|
||||
var buf [4096]byte
|
||||
|
||||
putInt(buf[:], int32(width))
|
||||
putInt(buf[4:], int32(height))
|
||||
var pos = 8
|
||||
for x := 0; x < width; x++ {
|
||||
for y := 0; y < height; y++ {
|
||||
rgba := color.NRGBAModel.Convert(img.At(x, y)).(color.NRGBA)
|
||||
if rgba.A == 0 {
|
||||
copy(buf[pos:], []byte{0, 0, 0, 0})
|
||||
} else {
|
||||
copy(buf[pos:], []byte{rgba.A, rgba.R, rgba.G, rgba.B})
|
||||
}
|
||||
pos += 4
|
||||
if pos == len(buf) {
|
||||
pos = 0
|
||||
digest.Write(buf[:])
|
||||
}
|
||||
}
|
||||
}
|
||||
if pos > 0 {
|
||||
digest.Write(buf[:pos])
|
||||
}
|
||||
return hex.EncodeToString(digest.Sum(nil))
|
||||
}
|
||||
|
||||
func putInt(buf []byte, n int32) {
|
||||
buf[0] = byte(n >> 24 & 0xff)
|
||||
buf[1] = byte(n >> 16 & 0xff)
|
||||
buf[2] = byte(n >> 8 & 0xff)
|
||||
buf[3] = byte(n & 0xff)
|
||||
}
|
62
model/token.go
Normal file
62
model/token.go
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (C) 2022. Gardel <sunxinao@hotmail.com> and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"time"
|
||||
"yggdrasil-go/util"
|
||||
)
|
||||
|
||||
type Token struct {
|
||||
createAt int64
|
||||
ClientToken string
|
||||
AccessToken string
|
||||
SelectedProfile Profile
|
||||
}
|
||||
|
||||
type AvailableLevel uint
|
||||
|
||||
const (
|
||||
Valid AvailableLevel = iota
|
||||
NeedRefresh
|
||||
Invalid
|
||||
)
|
||||
|
||||
func NewToken(accessToken string, clientToken *string, selectedProfile *Profile) (this Token) {
|
||||
this.createAt = time.Now().UnixMilli()
|
||||
|
||||
if clientToken == nil || (len(*clientToken) == 0) {
|
||||
this.ClientToken = util.RandomUUID()
|
||||
} else {
|
||||
this.ClientToken = *clientToken
|
||||
}
|
||||
this.AccessToken = accessToken
|
||||
this.SelectedProfile = *selectedProfile
|
||||
return this
|
||||
}
|
||||
|
||||
func (t *Token) GetAvailableLevel() AvailableLevel {
|
||||
d := time.Now().Sub(time.UnixMilli(t.createAt))
|
||||
if d > time.Hour*24*30 {
|
||||
return Invalid
|
||||
} else if d > time.Hour*24*15 {
|
||||
return NeedRefresh
|
||||
} else {
|
||||
return Valid
|
||||
}
|
||||
}
|
87
model/user.go
Normal file
87
model/user.go
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (C) 2022. Gardel <sunxinao@hotmail.com> and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/google/uuid"
|
||||
"time"
|
||||
"yggdrasil-go/util"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
ID uuid.UUID `gorm:"column:id;type:bytes;size:36;primaryKey"`
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
Email string `gorm:"size:64;uniqueIndex:email_idx"`
|
||||
Password string `gorm:"size:255"`
|
||||
ProfileName string `gorm:"size:64;uniqueIndex:profile_name_idx"`
|
||||
ProfileModelType string `gorm:"size:8;default:STEVE"`
|
||||
SerializedTextures string `gorm:"type:TEXT NULL"`
|
||||
profile *Profile `gorm:"-"`
|
||||
}
|
||||
|
||||
func (u *User) Profile() (*Profile, error) {
|
||||
if len(u.ProfileName) == 0 {
|
||||
return nil, util.NewIllegalArgumentError("Do not have profile")
|
||||
}
|
||||
if u.profile != nil {
|
||||
return u.profile, nil
|
||||
} else {
|
||||
var modelType ModelType
|
||||
if u.ProfileModelType == "ALEX" {
|
||||
modelType = ALEX
|
||||
} else {
|
||||
modelType = STEVE
|
||||
}
|
||||
profile := NewProfile(u.ID, u.ProfileName, modelType, u.SerializedTextures)
|
||||
return &profile, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (u *User) SetProfile(p *Profile) {
|
||||
u.profile = p
|
||||
u.ProfileName = p.Name
|
||||
switch p.ModelType {
|
||||
case ALEX:
|
||||
u.ProfileModelType = "ALEX"
|
||||
break
|
||||
case STEVE:
|
||||
u.ProfileModelType = "STEVE"
|
||||
break
|
||||
}
|
||||
serialized, err := json.Marshal(p.Textures)
|
||||
if err != nil {
|
||||
panic("Can not serialize texture")
|
||||
}
|
||||
u.SerializedTextures = string(serialized)
|
||||
}
|
||||
|
||||
type UserResponse struct {
|
||||
Username string `json:"username,omitempty"`
|
||||
Properties []util.StringProperty `json:"properties"`
|
||||
Id string `json:"id,omitempty"`
|
||||
}
|
||||
|
||||
func (u *User) ToResponse() UserResponse {
|
||||
return UserResponse{
|
||||
Id: util.UnsignedString(u.ID),
|
||||
Username: u.ProfileName,
|
||||
Properties: make([]util.StringProperty, 0),
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user