parent
dd7953b452
commit
6155f28671
|
@ -1,2 +1,2 @@
|
||||||
.idea/
|
.idea/
|
||||||
/mcm-register*
|
/mineauth*
|
|
@ -0,0 +1,24 @@
|
||||||
|
# mineauth
|
||||||
|
|
||||||
|
Users can log into a server (which validates their account) and you can run hooks after.
|
||||||
|
|
||||||
|
```text
|
||||||
|
Usage of mineauth:
|
||||||
|
-community string
|
||||||
|
URL to community
|
||||||
|
-debug
|
||||||
|
Debug Logging
|
||||||
|
-hook value
|
||||||
|
Hook to run
|
||||||
|
-ping string
|
||||||
|
Message for the server list (default "Login to authenticate!")
|
||||||
|
-port int
|
||||||
|
Port to listen on (default 25565)
|
||||||
|
-timeout int
|
||||||
|
HTTP timeout (default 15)
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
[MIT](LICENSE)
|
|
@ -0,0 +1,76 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
_ "embed"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed webhook.txt
|
||||||
|
var webhookURL string
|
||||||
|
|
||||||
|
type profile struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
IP string `json:"ip"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *profile) UUID() string {
|
||||||
|
return fmt.Sprintf("%s-%s-%s-%s-%s", p.ID[:8], p.ID[8:12], p.ID[12:16], p.ID[16:20], p.ID[20:32])
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var profile profile
|
||||||
|
if err := json.NewDecoder(os.Stdin).Decode(&profile); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
payloadHook := webhook{
|
||||||
|
Username: profile.Name,
|
||||||
|
Embeds: []embed{
|
||||||
|
{
|
||||||
|
Fields: []field{
|
||||||
|
{
|
||||||
|
Name: "UUID",
|
||||||
|
Value: profile.UUID(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "IP",
|
||||||
|
Value: profile.IP,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
payload, err := json.Marshal(payloadHook)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := http.Post(webhookURL, "application/json", bytes.NewReader(payload))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.StatusCode != http.StatusNoContent {
|
||||||
|
panic(fmt.Errorf("received non-200 status: %s", res.Status))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
type webhook struct {
|
||||||
|
Username string `json:"username"`
|
||||||
|
Embeds []embed `json:"embeds"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type embed struct {
|
||||||
|
Fields []field `json:"fields"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type field struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
https://discord.com/api/webhooks/<id>/<token>
|
2
go.mod
2
go.mod
|
@ -1,4 +1,4 @@
|
||||||
module git.canopymc.net/Etzelia/mcm-register
|
module git.canopymc.net/Etzelia/mineauth
|
||||||
|
|
||||||
go 1.16
|
go 1.16
|
||||||
|
|
||||||
|
|
27
main.go
27
main.go
|
@ -2,19 +2,27 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"github.com/peterbourgon/ff/v3"
|
"git.canopymc.net/Etzelia/mineauth/server"
|
||||||
"github.com/peterbourgon/ff/v3/fftoml"
|
|
||||||
"go.jolheiser.com/beaver"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/peterbourgon/ff/v3"
|
||||||
|
"github.com/peterbourgon/ff/v3/fftoml"
|
||||||
|
"go.jolheiser.com/beaver"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
fs := flag.NewFlagSet("afk", flag.ExitOnError)
|
fs := flag.NewFlagSet("mineauth", flag.ExitOnError)
|
||||||
portFlag := fs.Int("port", 25565, "Port to listen on")
|
portFlag := fs.Int("port", 25565, "Port to listen on")
|
||||||
timeoutFlag := fs.Int("timeout", 15, "HTTP timeout")
|
timeoutFlag := fs.Int("timeout", 15, "HTTP timeout")
|
||||||
discordFlag := fs.String("discord", "", "Discord invite link")
|
pingMessageFlag := fs.String("ping", "Login to authenticate!", "Message for the server list")
|
||||||
|
communityFlag := fs.String("community", "", "URL to community")
|
||||||
|
hooksFlag := make([]string, 0)
|
||||||
|
fs.Func("hook", "Hook to run", func(hook string) error {
|
||||||
|
hooksFlag = append(hooksFlag, hook)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
debugFlag := fs.Bool("debug", false, "Debug Logging")
|
debugFlag := fs.Bool("debug", false, "Debug Logging")
|
||||||
if err := ff.Parse(fs, os.Args[1:],
|
if err := ff.Parse(fs, os.Args[1:],
|
||||||
ff.WithEnvVarPrefix("MCM_REGISTER"),
|
ff.WithEnvVarPrefix("MCM_REGISTER"),
|
||||||
|
@ -31,13 +39,18 @@ func main() {
|
||||||
|
|
||||||
http.DefaultClient.Timeout = time.Second * time.Duration(*timeoutFlag)
|
http.DefaultClient.Timeout = time.Second * time.Duration(*timeoutFlag)
|
||||||
|
|
||||||
server, err := NewServer(*discordFlag)
|
serv, err := server.New(server.Options{
|
||||||
|
PingMessage: *pingMessageFlag,
|
||||||
|
CommunityURL: *communityFlag,
|
||||||
|
Hooks: hooksFlag,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
beaver.Error(err)
|
beaver.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := server.Start(*portFlag); err != nil {
|
beaver.Infof("Listening on http://localhost:%d", *portFlag)
|
||||||
|
if err := serv.Start(*portFlag); err != nil {
|
||||||
beaver.Error(err)
|
beaver.Error(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package main
|
package server
|
||||||
|
|
||||||
import "crypto/cipher"
|
import "crypto/cipher"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package main
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/sha1"
|
"crypto/sha1"
|
|
@ -1,4 +1,4 @@
|
||||||
package main
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
@ -9,11 +9,12 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/Tnze/go-mc/net"
|
|
||||||
pk "github.com/Tnze/go-mc/net/packet"
|
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/Tnze/go-mc/net"
|
||||||
|
pk "github.com/Tnze/go-mc/net/packet"
|
||||||
)
|
)
|
||||||
|
|
||||||
var hasJoinedURL = func() *url.URL {
|
var hasJoinedURL = func() *url.URL {
|
||||||
|
@ -37,6 +38,7 @@ func (s *Server) encryptionRequest(conn net.Conn) ([]byte, error) {
|
||||||
type profile struct {
|
type profile struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
IP string `json:"ip"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *profile) UUID() string {
|
func (p *profile) UUID() string {
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
@ -1,9 +1,10 @@
|
||||||
package main
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/Tnze/go-mc/bot"
|
"github.com/Tnze/go-mc/bot"
|
||||||
"github.com/Tnze/go-mc/chat"
|
"github.com/Tnze/go-mc/chat"
|
||||||
"github.com/Tnze/go-mc/net"
|
"github.com/Tnze/go-mc/net"
|
||||||
|
@ -22,7 +23,7 @@ func (s *Server) acceptListPing(conn net.Conn) {
|
||||||
|
|
||||||
switch p.ID {
|
switch p.ID {
|
||||||
case 0x00:
|
case 0x00:
|
||||||
err = conn.WritePacket(pk.Marshal(0x00, pk.String(listResp())))
|
err = conn.WritePacket(pk.Marshal(0x00, pk.String(s.listResp())))
|
||||||
case 0x01:
|
case 0x01:
|
||||||
err = conn.WritePacket(p)
|
err = conn.WritePacket(p)
|
||||||
}
|
}
|
||||||
|
@ -38,7 +39,7 @@ type player struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// listResp return server status as JSON string
|
// listResp return server status as JSON string
|
||||||
func listResp() string {
|
func (s *Server) listResp() string {
|
||||||
var list struct {
|
var list struct {
|
||||||
Version struct {
|
Version struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
@ -58,7 +59,7 @@ func listResp() string {
|
||||||
list.Players.Max = 0
|
list.Players.Max = 0
|
||||||
list.Players.Online = 0
|
list.Players.Online = 0
|
||||||
list.Players.Sample = []player{}
|
list.Players.Sample = []player{}
|
||||||
list.Description = chat.Message{Text: "Login to register!"}
|
list.Description = chat.Message{Text: s.opts.PingMessage}
|
||||||
list.FavIcon = fmt.Sprintf("data:image/png;base64,%s", base64.StdEncoding.EncodeToString(favicon))
|
list.FavIcon = fmt.Sprintf("data:image/png;base64,%s", base64.StdEncoding.EncodeToString(favicon))
|
||||||
|
|
||||||
data, err := json.Marshal(list)
|
data, err := json.Marshal(list)
|
|
@ -1,16 +1,21 @@
|
||||||
package main
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
_ "embed"
|
_ "embed"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/Tnze/go-mc/chat"
|
"github.com/Tnze/go-mc/chat"
|
||||||
"github.com/Tnze/go-mc/net"
|
"github.com/Tnze/go-mc/net"
|
||||||
pk "github.com/Tnze/go-mc/net/packet"
|
pk "github.com/Tnze/go-mc/net/packet"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"go.jolheiser.com/beaver"
|
"go.jolheiser.com/beaver"
|
||||||
|
gonet "net"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed favicon.png
|
//go:embed favicon.png
|
||||||
|
@ -19,10 +24,16 @@ var favicon []byte
|
||||||
type Server struct {
|
type Server struct {
|
||||||
privateKey *rsa.PrivateKey
|
privateKey *rsa.PrivateKey
|
||||||
publicKey []byte
|
publicKey []byte
|
||||||
discordInvite string
|
opts Options
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServer(discordInvite string) (*Server, error) {
|
type Options struct {
|
||||||
|
CommunityURL string
|
||||||
|
PingMessage string
|
||||||
|
Hooks []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(opts Options) (*Server, error) {
|
||||||
private, err := rsa.GenerateKey(rand.Reader, 1024)
|
private, err := rsa.GenerateKey(rand.Reader, 1024)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error generate private key: %v", err)
|
return nil, fmt.Errorf("error generate private key: %v", err)
|
||||||
|
@ -38,12 +49,11 @@ func NewServer(discordInvite string) (*Server, error) {
|
||||||
return &Server{
|
return &Server{
|
||||||
privateKey: private,
|
privateKey: private,
|
||||||
publicKey: public,
|
publicKey: public,
|
||||||
discordInvite: discordInvite,
|
opts: opts,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) Start(port int) error {
|
func (s *Server) Start(port int) error {
|
||||||
beaver.Infof("Listening on http://localhost:%d", port)
|
|
||||||
l, err := net.ListenMC(fmt.Sprintf(":%d", port))
|
l, err := net.ListenMC(fmt.Sprintf(":%d", port))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("listen error: %v", err)
|
return fmt.Errorf("listen error: %v", err)
|
||||||
|
@ -96,7 +106,34 @@ func (s *Server) handlePlaying(conn net.Conn) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Register
|
profile.IP, _, err = gonet.SplitHostPort(conn.Socket.RemoteAddr().String())
|
||||||
|
if err != nil {
|
||||||
|
beaver.Errorf("could not get user IP: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
payload, err := json.Marshal(profile)
|
||||||
|
if err != nil {
|
||||||
|
beaver.Errorf("could not marshal payload: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, hook := range s.opts.Hooks {
|
||||||
|
go func(h string) {
|
||||||
|
s := strings.Split(h, " ")
|
||||||
|
var args []string
|
||||||
|
if len(s) > 1 {
|
||||||
|
args = s[1:]
|
||||||
|
}
|
||||||
|
cmd := exec.Command(s[0], args...)
|
||||||
|
cmd.Stdin = bytes.NewReader(payload)
|
||||||
|
out, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
beaver.Errorf("could not run hook `%s`: %v", h, err)
|
||||||
|
beaver.Warn(string(out))
|
||||||
|
}
|
||||||
|
}(hook)
|
||||||
|
}
|
||||||
|
|
||||||
econn, err := encryptedConn(conn, secret)
|
econn, err := encryptedConn(conn, secret)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -104,9 +141,9 @@ func (s *Server) handlePlaying(conn net.Conn) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
msg := fmt.Sprintf("Thanks for registering, %s!", profile.Name)
|
msg := fmt.Sprintf("Thanks for authenticating, %s!", profile.Name)
|
||||||
if s.discordInvite != "" {
|
if s.opts.CommunityURL != "" {
|
||||||
msg += fmt.Sprintf("\n\nJoin the Discord\n%s", s.discordInvite)
|
msg += fmt.Sprintf("\n\nJoin the community\n%s", s.opts.CommunityURL)
|
||||||
}
|
}
|
||||||
packet := pk.Marshal(0x00,
|
packet := pk.Marshal(0x00,
|
||||||
chat.Message{Text: msg},
|
chat.Message{Text: msg},
|
Loading…
Reference in New Issue