tripwire/session.go
2022-06-25 18:09:27 -04:00

180 lines
4.1 KiB
Go

package main
import (
"encoding/base64"
"encoding/json"
"net/http"
"os"
"strings"
"github.com/gorilla/mux"
)
func profileEndpoint(w http.ResponseWriter, r *http.Request) {
uuid := mux.Vars(r)["uuid"]
query := r.URL.Query()
if len(uuid) == 32 {
uuid = growUUID(uuid)
}
if len(uuid) != 36 {
sendError(w, YggError{
Code: 400,
Error: "Bad Request",
ErrorMessage: "Invalid UUID.",
})
return
}
exists, err := playerExistsByUUID(uuid)
if err != nil {
handleError(w, err)
return
}
if !exists {
sendError(w, YggError{
Code: 400,
Error: "Bad Request",
ErrorMessage: "The user does not exist.",
})
return
}
player, err := getPlayerByUUID(uuid)
if err != nil {
handleError(w, err)
return
}
signed := query.Has("unsigned") && query.Get("unsigned") == "false"
response, err := generateProfileResponse(uuid, player.Username, signed)
if err != nil {
handleError(w, err)
return
}
sendJSON(w, response)
}
func hasJoinedEndpoint(w http.ResponseWriter, r *http.Request) {
params := r.URL.Query()
if !params.Has("username") || !params.Has("serverId") {
sendJSON(w, &Empty{})
return
}
player, err := getPlayerByUsername(params.Get("username"))
if err != nil {
handleError(w, err)
return
}
response, err := generateProfileResponse(player.UUID, params.Get("username"), true)
if err != nil {
handleError(w, err)
return
}
sendJSON(w, response)
}
func joinEndpoint(w http.ResponseWriter, r *http.Request) {
var payload JoinPayload
unmarshalTo(r, &payload)
if payload.AccessToken == "" || payload.SelectedProfile == "" || payload.ServerId == "" {
sendError(w, YggError{
Code: 400,
Error: "IllegalArgumentException",
ErrorMessage: "A required field is not present.",
})
return
}
player, err := getPlayerByAuthToken(payload.AccessToken)
if err != nil {
handleError(w, err)
return
}
if payload.SelectedProfile != shrinkUUID(player.UUID) {
sendError(w, YggError{
Code: 400,
Error: "Bad Request",
ErrorMessage: "The request data is malformed.",
})
return
}
sendEmpty(w)
}
func registerSessionEndpoints(r *mux.Router) {
prefix := "/sessionserver"
r.HandleFunc(prefix+"/session/minecraft/profile/{uuid}", profileEndpoint).Methods("GET")
r.HandleFunc(prefix+"/session/minecraft/join", joinEndpoint).Methods("POST")
r.HandleFunc(prefix+"/session/minecraft/hasJoined", hasJoinedEndpoint).Methods("GET")
}
func generateProfileResponse(uuid string, username string, signed bool) (ProfileResponse, error) {
// todo: make this more visually appealing if possible
skin := SkinTexture{}
skin.Url = config.Protocol + config.BaseUrl + "/getTexture/" + uuid + "?type=skin"
skin.Metadata = SkinMetadata{}
skin.Metadata.Model = "default"
textures := ProfileTextureMetadata{}
textures.Id = shrinkUUID(uuid)
textures.Name = username
textures.Textures = ProfileTextures{}
textures.Textures.Skin = skin
_, err := os.Stat("capes/" + uuid + ".png")
if err == nil {
cape := &Texture{}
cape.Url = config.Protocol + config.BaseUrl + "/getTexture/" + uuid + "?type=cape"
textures.Textures.Cape = cape
}
marshalledTextures, err := json.Marshal(textures)
if err != nil {
return ProfileResponse{}, err
}
encodedTextures := base64.StdEncoding.EncodeToString(marshalledTextures)
response := ProfileResponse{}
response.Id = shrinkUUID(uuid)
response.Name = username
response.Properties = []Property{
{
Name: "textures",
Value: encodedTextures,
},
}
if signed && privateKey != nil {
signedTextures, err := signWithPrivateKey(string(encodedTextures))
if err != nil {
return ProfileResponse{}, err
}
b64 := make([]byte, base64.StdEncoding.EncodedLen(len(signedTextures)))
base64.StdEncoding.Encode(b64, signedTextures)
stringified := string(b64)
response.Properties[0].Signature = &stringified
}
return response, nil
}
func shrinkUUID(uuid string) string {
return strings.Join(strings.Split(uuid, "-"), "")
}
func growUUID(uuid string) string {
if len(uuid) == 32 {
return uuid[0:8] + "-" + uuid[8:12] + "-" + uuid[12:16] + "-" + uuid[16:20] + "-" + uuid[20:32]
}
return ""
}