Skip to content

Commit

Permalink
feat: userpass auth
Browse files Browse the repository at this point in the history
  • Loading branch information
tobyxdd committed Aug 6, 2023
1 parent 601ad6b commit 7307eea
Show file tree
Hide file tree
Showing 8 changed files with 241 additions and 3 deletions.
8 changes: 7 additions & 1 deletion app/cmd/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,13 @@ func (c *clientConfig) URI() string {
}
var user *url.Userinfo
if c.Auth != "" {
user = url.User(c.Auth)
// We need to handle the special case of user:pass pairs
rs := strings.SplitN(c.Auth, ":", 2)
if len(rs) == 2 {
user = url.UserPassword(rs[0], rs[1])
} else {
user = url.User(c.Auth)
}
}
u := url.URL{
Scheme: "hysteria2",
Expand Down
8 changes: 8 additions & 0 deletions app/cmd/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,14 @@ func TestClientConfigURI(t *testing.T) {
Auth: "god",
},
},
{
uri: "hysteria2://john:[email protected]/",
uriOK: true,
config: &clientConfig{
Server: "continental.org",
Auth: "john:wick",
},
},
{
uri: "hysteria2://noauth.com/?insecure=1&obfs=salamander&obfs-password=66ccff&sni=crap.cc",
uriOK: true,
Expand Down
11 changes: 9 additions & 2 deletions app/cmd/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,9 @@ type serverConfigBandwidth struct {
}

type serverConfigAuth struct {
Type string `mapstructure:"type"`
Password string `mapstructure:"password"`
Type string `mapstructure:"type"`
Password string `mapstructure:"password"`
UserPass map[string]string `mapstructure:"userpass"`
}

type serverConfigResolverTCP struct {
Expand Down Expand Up @@ -380,6 +381,12 @@ func (c *serverConfig) fillAuthenticator(hyConfig *server.Config) error {
}
hyConfig.Authenticator = &auth.PasswordAuthenticator{Password: c.Auth.Password}
return nil
case "userpass":
if len(c.Auth.UserPass) == 0 {
return configError{Field: "auth.userpass", Err: errors.New("empty auth userpass")}
}
hyConfig.Authenticator = &auth.UserPassAuthenticator{Users: c.Auth.UserPass}
return nil
default:
return configError{Field: "auth.type", Err: errors.New("unsupported auth type")}
}
Expand Down
5 changes: 5 additions & 0 deletions app/cmd/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ func TestServerConfig(t *testing.T) {
Auth: serverConfigAuth{
Type: "password",
Password: "goofy_ahh_password",
UserPass: map[string]string{
"yolo": "swag",
"lol": "kek",
"foo": "bar",
},
},
Resolver: serverConfigResolver{
Type: "udp",
Expand Down
4 changes: 4 additions & 0 deletions app/cmd/server_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ udpIdleTimeout: 120s
auth:
type: password
password: goofy_ahh_password
userpass:
yolo: swag
lol: kek
foo: bar

resolver:
type: udp
Expand Down
65 changes: 65 additions & 0 deletions extras/auth/password_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package auth

import (
"net"
"testing"
)

func TestPasswordAuthenticator(t *testing.T) {
type fields struct {
Password string
}
type args struct {
addr net.Addr
auth string
tx uint64
}
tests := []struct {
name string
fields fields
args args
wantOk bool
wantId string
}{
{
name: "correct",
fields: fields{
Password: "yes,yes",
},
args: args{
addr: nil,
auth: "yes,yes",
tx: 0,
},
wantOk: true,
wantId: "user",
},
{
name: "incorrect",
fields: fields{
Password: "something_somehow",
},
args: args{
addr: nil,
auth: "random",
tx: 0,
},
wantOk: false,
wantId: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
a := &PasswordAuthenticator{
Password: tt.fields.Password,
}
gotOk, gotId := a.Authenticate(tt.args.addr, tt.args.auth, tt.args.tx)
if gotOk != tt.wantOk {
t.Errorf("Authenticate() gotOk = %v, want %v", gotOk, tt.wantOk)
}
if gotId != tt.wantId {
t.Errorf("Authenticate() gotId = %v, want %v", gotId, tt.wantId)
}
})
}
}
40 changes: 40 additions & 0 deletions extras/auth/userpass.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package auth

import (
"net"
"strings"

"github.com/apernet/hysteria/core/server"
)

const (
userPassSeparator = ":"
)

var _ server.Authenticator = &UserPassAuthenticator{}

// UserPassAuthenticator checks the provided auth string against a map of username/password pairs.
// The format of the auth string must be "username:password".
type UserPassAuthenticator struct {
Users map[string]string
}

func (a *UserPassAuthenticator) Authenticate(addr net.Addr, auth string, tx uint64) (ok bool, id string) {
u, p, ok := splitUserPass(auth)
if !ok {
return false, ""
}
rp, ok := a.Users[u]
if !ok || rp != p {
return false, ""
}
return true, u
}

func splitUserPass(auth string) (user, pass string, ok bool) {
rs := strings.SplitN(auth, userPassSeparator, 2)
if len(rs) != 2 {
return "", "", false
}
return rs[0], rs[1], true
}
103 changes: 103 additions & 0 deletions extras/auth/userpass_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package auth

import (
"net"
"testing"
)

func TestUserPassAuthenticator(t *testing.T) {
type fields struct {
Users map[string]string
}
type args struct {
addr net.Addr
auth string
tx uint64
}
tests := []struct {
name string
fields fields
args args
wantOk bool
wantId string
}{
{
name: "correct 1",
fields: fields{
Users: map[string]string{
"saul": "goodman",
"wang": "123",
},
},
args: args{
addr: nil,
auth: "wang:123",
tx: 0,
},
wantOk: true,
wantId: "wang",
},
{
name: "correct 2",
fields: fields{
Users: map[string]string{
"gawr": "gura",
"fubuki": "shirakami",
},
},
args: args{
addr: nil,
auth: "gawr:gura",
tx: 0,
},
wantOk: true,
wantId: "gawr",
},
{
name: "incorrect 1",
fields: fields{
Users: map[string]string{
"gawr": "gura",
"fubuki": "shirakami",
},
},
args: args{
addr: nil,
auth: "random:stranger",
tx: 0,
},
wantOk: false,
wantId: "",
},
{
name: "incorrect 2",
fields: fields{
Users: map[string]string{
"gawr": "gura",
"fubuki": "shirakami",
},
},
args: args{
addr: nil,
auth: "poop",
tx: 0,
},
wantOk: false,
wantId: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
a := &UserPassAuthenticator{
Users: tt.fields.Users,
}
gotOk, gotId := a.Authenticate(tt.args.addr, tt.args.auth, tt.args.tx)
if gotOk != tt.wantOk {
t.Errorf("Authenticate() gotOk = %v, want %v", gotOk, tt.wantOk)
}
if gotId != tt.wantId {
t.Errorf("Authenticate() gotId = %v, want %v", gotId, tt.wantId)
}
})
}
}

0 comments on commit 7307eea

Please sign in to comment.