auth/internal/domain/me.go

189 lines
3.9 KiB
Go
Raw Normal View History

2021-12-03 01:58:37 +00:00
package domain
import (
"fmt"
"net"
2021-12-25 18:53:49 +00:00
"net/url"
2021-12-29 20:08:30 +00:00
"strconv"
2021-12-03 01:58:37 +00:00
"strings"
"testing"
http "github.com/valyala/fasthttp"
)
2021-12-25 18:53:49 +00:00
// Me is a URL user identifier.
2021-12-03 01:58:37 +00:00
type Me struct {
id *http.URI
2021-12-03 01:58:37 +00:00
}
2022-01-29 17:50:45 +00:00
// ParseMe parse string as me URL identifier.
//nolint: funlen, cyclop
2022-01-29 14:54:37 +00:00
func ParseMe(raw string) (*Me, error) {
id := http.AcquireURI()
if err := id.Parse(nil, []byte(raw)); err != nil {
return nil, NewError(
ErrorCodeInvalidRequest,
err.Error(),
"https://indieauth.net/source/#user-profile-url",
"",
)
2021-12-03 01:58:37 +00:00
}
scheme := string(id.Scheme())
2021-12-03 01:58:37 +00:00
if scheme != "http" && scheme != "https" {
return nil, NewError(
ErrorCodeInvalidRequest,
"profile URL MUST have either an https or http scheme",
"https://indieauth.net/source/#user-profile-url",
"",
)
2021-12-03 01:58:37 +00:00
}
path := string(id.PathOriginal())
2021-12-03 01:58:37 +00:00
if path == "" || strings.Contains(path, "/.") || strings.Contains(path, "/..") {
return nil, NewError(
ErrorCodeInvalidRequest,
"profile URL MUST contain a path component (/ is a valid path), MUST NOT contain single-dot "+
"or double-dot path segments",
"https://indieauth.net/source/#user-profile-url",
"",
)
2021-12-03 01:58:37 +00:00
}
if id.Hash() != nil {
return nil, NewError(
ErrorCodeInvalidRequest,
"profile URL MUST NOT contain a fragment component",
"https://indieauth.net/source/#user-profile-url",
"",
)
2021-12-03 01:58:37 +00:00
}
if id.Username() != nil || id.Password() != nil {
return nil, NewError(
ErrorCodeInvalidRequest,
"profile URL MUST NOT contain a username or password component",
"https://indieauth.net/source/#user-profile-url",
"",
)
2021-12-03 01:58:37 +00:00
}
domain := string(id.Host())
2021-12-25 18:53:49 +00:00
if domain == "" {
return nil, NewError(
ErrorCodeInvalidRequest,
"profile host name MUST be a domain name",
"https://indieauth.net/source/#user-profile-url",
"",
)
2021-12-03 01:58:37 +00:00
}
2021-12-25 18:53:49 +00:00
if _, port, _ := net.SplitHostPort(domain); port != "" {
return nil, NewError(
ErrorCodeInvalidRequest,
"profile MUST NOT contain a port",
"https://indieauth.net/source/#user-profile-url",
"",
)
2021-12-03 01:58:37 +00:00
}
2021-12-25 18:53:49 +00:00
if net.ParseIP(domain) != nil {
return nil, NewError(
ErrorCodeInvalidRequest,
"profile MUST NOT be ipv4 or ipv6 addresses",
"https://indieauth.net/source/#user-profile-url",
"",
)
2021-12-03 01:58:37 +00:00
}
return &Me{id: id}, nil
2021-12-25 18:53:49 +00:00
}
2021-12-03 01:58:37 +00:00
2022-01-29 17:50:45 +00:00
// TestMe returns valid random generated me for tests.
func TestMe(tb testing.TB, src string) *Me {
2021-12-25 18:53:49 +00:00
tb.Helper()
2022-01-29 14:54:37 +00:00
me, err := ParseMe(src)
2022-01-30 17:49:25 +00:00
if err != nil {
tb.Fatal(err)
2022-01-30 17:49:25 +00:00
}
2021-12-25 18:53:49 +00:00
return me
2021-12-03 01:58:37 +00:00
}
2022-01-29 17:50:45 +00:00
// UnmarshalForm implements custom unmarshler for form values.
2021-12-29 20:08:30 +00:00
func (m *Me) UnmarshalForm(v []byte) error {
2022-01-29 14:54:37 +00:00
me, err := ParseMe(string(v))
2021-12-25 18:53:49 +00:00
if err != nil {
return fmt.Errorf("Me: UnmarshalForm: %w", err)
2021-12-03 01:58:37 +00:00
}
2021-12-29 20:08:30 +00:00
*m = *me
return nil
}
2022-01-29 17:50:45 +00:00
// UnmarshalJSON implements custom unmarshler for JSON.
2021-12-29 20:08:30 +00:00
func (m *Me) UnmarshalJSON(v []byte) error {
src, err := strconv.Unquote(string(v))
if err != nil {
return fmt.Errorf("Me: UnmarshalJSON: %w", err)
2021-12-29 20:08:30 +00:00
}
2022-01-29 14:54:37 +00:00
me, err := ParseMe(src)
2021-12-29 20:08:30 +00:00
if err != nil {
return fmt.Errorf("Me: UnmarshalJSON: %w", err)
2021-12-29 20:08:30 +00:00
}
*m = *me
2021-12-25 18:53:49 +00:00
return nil
2021-12-03 01:58:37 +00:00
}
2022-01-29 17:50:45 +00:00
// MarshalJSON implements custom marshler for JSON.
2022-01-12 18:04:40 +00:00
func (m Me) MarshalJSON() ([]byte, error) {
return []byte(strconv.Quote(m.String())), nil
}
2022-01-29 17:50:45 +00:00
// URI returns copy of parsed me in *fasthttp.URI representation.
2021-12-03 01:58:37 +00:00
// This copy MUST be released via fasthttp.ReleaseURI.
2022-01-12 18:04:40 +00:00
func (m Me) URI() *http.URI {
if m.id == nil {
2021-12-29 20:08:30 +00:00
return nil
}
2021-12-03 01:58:37 +00:00
u := http.AcquireURI()
m.id.CopyTo(u)
2021-12-03 01:58:37 +00:00
return u
}
2022-01-29 17:50:45 +00:00
// URL returns copy of parsed me in *url.URL representation.
2022-01-12 18:04:40 +00:00
func (m Me) URL() *url.URL {
if m.id == nil {
2021-12-29 20:08:30 +00:00
return nil
}
2021-12-25 18:53:49 +00:00
return &url.URL{
ForceQuery: false,
Fragment: string(m.id.Hash()),
Host: string(m.id.Host()),
Opaque: "",
Path: string(m.id.Path()),
RawFragment: "",
RawPath: string(m.id.PathOriginal()),
RawQuery: string(m.id.QueryString()),
Scheme: string(m.id.Scheme()),
User: nil,
2021-12-25 18:53:49 +00:00
}
2021-12-03 01:58:37 +00:00
}
2022-01-29 17:50:45 +00:00
// String returns string representation of me.
2022-01-12 18:04:40 +00:00
func (m Me) String() string {
if m.id == nil {
2021-12-29 20:08:30 +00:00
return ""
}
return m.id.String()
2021-12-03 01:58:37 +00:00
}