diff --git a/internal/auth/delivery/http/auth_http_test.go b/internal/auth/delivery/http/auth_http_test.go index 4012742..255c0b2 100644 --- a/internal/auth/delivery/http/auth_http_test.go +++ b/internal/auth/delivery/http/auth_http_test.go @@ -31,11 +31,11 @@ type Dependencies struct { authService auth.UseCase clients client.Repository clientService client.UseCase - config *domain.Config matcher language.Matcher profiles profile.Repository sessions session.Repository users user.Repository + config *domain.Config } func TestAuthorize(t *testing.T) { @@ -113,7 +113,7 @@ func NewDependencies(tb testing.TB) Dependencies { users := userrepo.NewMemoryUserRepository() sessions := sessionrepo.NewMemorySessionRepository(*config) profiles := profilerepo.NewMemoryProfileRepository() - authService := ucase.NewAuthUseCase(sessions, profiles, config) + authService := ucase.NewAuthUseCase(sessions, profiles, *config) clientService := clientucase.NewClientUseCase(clients) return Dependencies{ diff --git a/internal/auth/usecase/auth_ucase.go b/internal/auth/usecase/auth_ucase.go index 0dd4b83..3eed5f9 100644 --- a/internal/auth/usecase/auth_ucase.go +++ b/internal/auth/usecase/auth_ucase.go @@ -12,13 +12,13 @@ import ( ) type authUseCase struct { - config *domain.Config sessions session.Repository profiles profile.Repository + config domain.Config } // NewAuthUseCase creates a new authentication use case. -func NewAuthUseCase(sessions session.Repository, profiles profile.Repository, config *domain.Config) auth.UseCase { +func NewAuthUseCase(sessions session.Repository, profiles profile.Repository, config domain.Config) auth.UseCase { return &authUseCase{ config: config, sessions: sessions, diff --git a/internal/client/delivery/http/client_http_test.go b/internal/client/delivery/http/client_http_test.go index faaa47a..f03ecc2 100644 --- a/internal/client/delivery/http/client_http_test.go +++ b/internal/client/delivery/http/client_http_test.go @@ -58,7 +58,7 @@ func NewDependencies(tb testing.TB) Dependencies { tokens := tokenrepo.NewMemoryTokenRepository() profiles := profilerepo.NewMemoryProfileRepository() tokenService := tokenucase.NewTokenUseCase(tokenucase.Config{ - Config: config, + Config: *config, Profiles: profiles, Sessions: sessions, Tokens: tokens, diff --git a/internal/domain/ticket.go b/internal/domain/ticket.go index 06ccc4f..7e1db9a 100644 --- a/internal/domain/ticket.go +++ b/internal/domain/ticket.go @@ -10,7 +10,7 @@ type Ticket struct { Resource *url.URL // The access token should be used when acting on behalf of this URL. - Subject *Me + Subject Me // A random string that can be redeemed for an access token. Ticket string @@ -22,7 +22,7 @@ func TestTicket(tb testing.TB) *Ticket { return &Ticket{ Resource: &url.URL{Scheme: "https", Host: "alice.example.com", Path: "/private/"}, - Subject: TestMe(tb, "https://bob.example.com/"), + Subject: *TestMe(tb, "https://bob.example.com/"), Ticket: "32985723984723985792834", } } diff --git a/internal/ticket/delivery/http/ticket_http.go b/internal/ticket/delivery/http/ticket_http.go deleted file mode 100644 index fe6ddaa..0000000 --- a/internal/ticket/delivery/http/ticket_http.go +++ /dev/null @@ -1,198 +0,0 @@ -package http - -import ( - "fmt" - "net/http" - - "github.com/goccy/go-json" - "github.com/lestrrat-go/jwx/v2/jwa" - "golang.org/x/text/language" - "golang.org/x/text/message" - - "source.toby3d.me/toby3d/auth/internal/common" - "source.toby3d.me/toby3d/auth/internal/domain" - "source.toby3d.me/toby3d/auth/internal/middleware" - "source.toby3d.me/toby3d/auth/internal/random" - "source.toby3d.me/toby3d/auth/internal/ticket" - "source.toby3d.me/toby3d/auth/internal/urlutil" - "source.toby3d.me/toby3d/auth/web" -) - -type Handler struct { - matcher language.Matcher - tickets ticket.UseCase - config domain.Config -} - -func NewHandler(tickets ticket.UseCase, matcher language.Matcher, config domain.Config) *Handler { - return &Handler{ - config: config, - matcher: matcher, - tickets: tickets, - } -} - -func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { - //nolint:exhaustivestruct - chain := middleware.Chain{ - middleware.CSRFWithConfig(middleware.CSRFConfig{ - Skipper: func(_ http.ResponseWriter, r *http.Request) bool { - head, _ := urlutil.ShiftPath(r.URL.Path) - - return r.Method == http.MethodPost && head == "ticket" - }, - CookieMaxAge: 0, - CookieSameSite: http.SameSiteStrictMode, - ContextKey: "csrf", - CookieDomain: h.config.Server.Domain, - CookieName: "__Secure-csrf", - CookiePath: "/ticket", - TokenLookup: "form:_csrf", - TokenLength: 0, - CookieSecure: true, - CookieHTTPOnly: true, - }), - middleware.JWTWithConfig(middleware.JWTConfig{ - AuthScheme: "Bearer", - BeforeFunc: nil, - Claims: nil, - ContextKey: "token", - ErrorHandler: nil, - ErrorHandlerWithContext: nil, - ParseTokenFunc: nil, - SigningKey: []byte(h.config.JWT.Secret), - SigningKeys: nil, - SigningMethod: jwa.SignatureAlgorithm(h.config.JWT.Algorithm), - Skipper: middleware.DefaultSkipper, - SuccessHandler: nil, - TokenLookup: "header:" + common.HeaderAuthorization + - ",cookie:__Secure-auth-token", - }), - } - - chain.Handler(h.handleFunc).ServeHTTP(w, r) -} - -func (h *Handler) handleFunc(w http.ResponseWriter, r *http.Request) { - var head string - head, r.URL.Path = urlutil.ShiftPath(r.URL.Path) - - switch r.Method { - default: - http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed) - case "", http.MethodGet: - if head != "" { - http.NotFound(w, r) - - return - } - - h.handleRender(w, r) - case http.MethodPost: - switch head { - default: - http.NotFound(w, r) - case "": - h.handleRedeem(w, r) - case "send": - h.handleSend(w, r) - } - } -} - -func (h *Handler) handleRender(w http.ResponseWriter, r *http.Request) { - w.Header().Set(common.HeaderContentType, common.MIMETextHTMLCharsetUTF8) - - tags, _, _ := language.ParseAcceptLanguage(r.Header.Get(common.HeaderAcceptLanguage)) - tag, _, _ := h.matcher.Match(tags...) - baseOf := web.BaseOf{ - Config: &h.config, - Language: tag, - Printer: message.NewPrinter(tag), - } - - csrf, _ := r.Context().Value("csrf").([]byte) - web.WriteTemplate(w, &web.TicketPage{ - BaseOf: baseOf, - CSRF: csrf, - }) -} - -func (h *Handler) handleSend(w http.ResponseWriter, r *http.Request) { - w.Header().Set(common.HeaderAccessControlAllowOrigin, h.config.Server.Domain) - w.Header().Set(common.HeaderContentType, common.MIMEApplicationJSONCharsetUTF8) - - encoder := json.NewEncoder(w) - - req := new(TicketGenerateRequest) - if err := req.bind(r); err != nil { - w.WriteHeader(http.StatusBadRequest) - - _ = encoder.Encode(err) - - return - } - - ticket := &domain.Ticket{ - Ticket: "", - Resource: req.Resource.URL, - Subject: &req.Subject, - } - - var err error - if ticket.Ticket, err = random.String(h.config.TicketAuth.Length); err != nil { - w.WriteHeader(http.StatusInternalServerError) - - _ = encoder.Encode(domain.NewError(domain.ErrorCodeServerError, err.Error(), "")) - - return - } - - if err = h.tickets.Generate(r.Context(), *ticket); err != nil { - w.WriteHeader(http.StatusInternalServerError) - - _ = encoder.Encode(domain.NewError(domain.ErrorCodeServerError, err.Error(), "")) - - return - } - - w.WriteHeader(http.StatusOK) -} - -func (h *Handler) handleRedeem(w http.ResponseWriter, r *http.Request) { - w.Header().Set(common.HeaderContentType, common.MIMEApplicationJSONCharsetUTF8) - - encoder := json.NewEncoder(w) - - req := new(TicketExchangeRequest) - if err := req.bind(r); err != nil { - w.WriteHeader(http.StatusBadRequest) - - _ = encoder.Encode(err) - - return - } - - token, err := h.tickets.Redeem(r.Context(), domain.Ticket{ - Ticket: req.Ticket, - Resource: req.Resource.URL, - Subject: &req.Subject, - }) - if err != nil { - w.WriteHeader(http.StatusBadRequest) - - _ = encoder.Encode(domain.NewError(domain.ErrorCodeServerError, err.Error(), "")) - - return - } - - // TODO(toby3d): print the result as part of the debugging. Instead, we - // need to send or save the token to the recipient for later use. - fmt.Fprintf(w, `{ - "access_token": "%s", - "token_type": "Bearer", - "scope": "%s", - "me": "%s" - }`, token.AccessToken, token.Scope.String(), token.Me.String()) - w.WriteHeader(http.StatusOK) -} diff --git a/internal/ticket/delivery/http/ticket_http_schema.go b/internal/ticket/delivery/http/ticket_http_schema.go deleted file mode 100644 index eb58b29..0000000 --- a/internal/ticket/delivery/http/ticket_http_schema.go +++ /dev/null @@ -1,90 +0,0 @@ -package http - -import ( - "errors" - "net/http" - - "source.toby3d.me/toby3d/auth/internal/domain" - "source.toby3d.me/toby3d/form" -) - -type ( - TicketGenerateRequest struct { - // The access token should be used when acting on behalf of this URL. - Subject domain.Me `form:"subject"` - - // The access token will work at this URL. - Resource domain.URL `form:"resource"` - } - - TicketExchangeRequest struct { - // The access token should be used when acting on behalf of this URL. - Subject domain.Me `form:"subject"` - - // The access token will work at this URL. - Resource domain.URL `form:"resource"` - - // A random string that can be redeemed for an access token. - Ticket string `form:"ticket"` - } -) - -func (r *TicketGenerateRequest) bind(req *http.Request) error { - indieAuthError := new(domain.Error) - - if err := req.ParseForm(); err != nil { - return domain.NewError( - domain.ErrorCodeInvalidRequest, - err.Error(), - "https://indieauth.net/source/#authorization-request", - ) - } - - if err := form.Unmarshal([]byte(req.PostForm.Encode()), r); err != nil { - if errors.As(err, indieAuthError) { - return indieAuthError - } - - return domain.NewError( - domain.ErrorCodeInvalidRequest, - err.Error(), - "https://indieweb.org/IndieAuth_Ticket_Auth#Create_the_IndieAuth_ticket", - ) - } - - return nil -} - -func (r *TicketExchangeRequest) bind(req *http.Request) error { - indieAuthError := new(domain.Error) - - if err := req.ParseForm(); err != nil { - return domain.NewError( - domain.ErrorCodeInvalidRequest, - err.Error(), - "https://indieauth.net/source/#authorization-request", - ) - } - - if err := form.Unmarshal([]byte(req.PostForm.Encode()), r); err != nil { - if errors.As(err, indieAuthError) { - return indieAuthError - } - - return domain.NewError( - domain.ErrorCodeInvalidRequest, - err.Error(), - "https://indieweb.org/IndieAuth_Ticket_Auth#Create_the_IndieAuth_ticket", - ) - } - - if r.Ticket == "" { - return domain.NewError( - domain.ErrorCodeInvalidRequest, - "ticket parameter is required", - "https://indieweb.org/IndieAuth_Ticket_Auth#Create_the_IndieAuth_ticket", - ) - } - - return nil -} diff --git a/internal/ticket/delivery/http/ticket_http_test.go b/internal/ticket/delivery/http/ticket_http_test.go deleted file mode 100644 index 718966d..0000000 --- a/internal/ticket/delivery/http/ticket_http_test.go +++ /dev/null @@ -1,121 +0,0 @@ -package http_test - -/* TODO(toby3d): move CSRF middleware into main -import ( - "fmt" - "io" - "net/http" - "net/http/httptest" - "strings" - "sync" - "testing" - - "golang.org/x/text/language" - "golang.org/x/text/message" - - "source.toby3d.me/toby3d/auth/internal/common" - "source.toby3d.me/toby3d/auth/internal/domain" - "source.toby3d.me/toby3d/auth/internal/ticket" - delivery "source.toby3d.me/toby3d/auth/internal/ticket/delivery/http" - ticketrepo "source.toby3d.me/toby3d/auth/internal/ticket/repository/memory" - ucase "source.toby3d.me/toby3d/auth/internal/ticket/usecase" -) - -type Dependencies struct { - server *httptest.Server - client *http.Client - config *domain.Config - matcher language.Matcher - store *sync.Map - ticket *domain.Ticket - tickets ticket.Repository - ticketService ticket.UseCase - token *domain.Token -} - -func TestUpdate(t *testing.T) { - t.Parallel() - - deps := NewDependencies(t) - t.Cleanup(deps.server.Close) - - req := httptest.NewRequest(http.MethodPost, "https://example.com/", strings.NewReader( - `ticket=`+deps.ticket.Ticket+ - `&resource=`+deps.ticket.Resource.String()+ - `&subject=`+deps.ticket.Subject.String(), - )) - req.Header.Set(common.HeaderContentType, common.MIMEApplicationForm) - deps.token.SetAuthHeader(req) - - w := httptest.NewRecorder() - delivery.NewHandler(deps.ticketService, deps.matcher, *deps.config). - Handler(). - ServeHTTP(w, req) - - domain.TestToken(t).SetAuthHeader(req) - - resp := w.Result() - - if resp.StatusCode != http.StatusOK && - resp.StatusCode != http.StatusAccepted { - t.Errorf("%s %s = %d, want %d or %d", req.Method, req.RequestURI, resp.StatusCode, http.StatusOK, - http.StatusAccepted) - } - - // TODO(toby3d): print the result as part of the debugging. Instead, you - // need to send or save the token to the recipient for later use. - if resp.Body == nil { - t.Errorf("%s %s = nil, want not nil", req.Method, req.RequestURI) - } -} - -func NewDependencies(tb testing.TB) Dependencies { - tb.Helper() - - config := domain.TestConfig(tb) - matcher := language.NewMatcher(message.DefaultCatalog.Languages()) - store := new(sync.Map) - ticket := domain.TestTicket(tb) - token := domain.TestToken(tb) - - mux := http.NewServeMux() - // NOTE(toby3d): private resource - mux.HandleFunc(ticket.Resource.Path, func(w http.ResponseWriter, r *http.Request) { - w.Header().Set(common.HeaderContentType, common.MIMETextHTMLCharsetUTF8) - fmt.Fprintf(w, ``) - }) - // NOTE(toby3d): token endpoint - mux.HandleFunc("/token", func(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodPost { - http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed) - - return - } - - w.Header().Set(common.HeaderContentType, common.MIMEApplicationJSONCharsetUTF8) - fmt.Fprintf(w, `{ - "access_token": "`+token.AccessToken+`", - "me": "`+token.Me.String()+`", - "scope": "`+token.Scope.String()+`", - "token_type": "Bearer" - }`) - }) - - server := httptest.NewServer(mux) - client := server.Client() - tickets := ticketrepo.NewMemoryTicketRepository(store, config) - ticketService := ucase.NewTicketUseCase(tickets, client, config) - - return Dependencies{ - server: server, - client: client, - config: config, - matcher: matcher, - store: store, - ticket: ticket, - tickets: tickets, - ticketService: ticketService, - token: token, - } -} -*/ diff --git a/internal/ticket/repository.go b/internal/ticket/repository.go deleted file mode 100644 index 5e85b52..0000000 --- a/internal/ticket/repository.go +++ /dev/null @@ -1,15 +0,0 @@ -package ticket - -import ( - "context" - - "source.toby3d.me/toby3d/auth/internal/domain" -) - -type Repository interface { - Create(ctx context.Context, ticket domain.Ticket) error - GetAndDelete(ctx context.Context, ticket string) (*domain.Ticket, error) - GC() -} - -var ErrNotExist error = domain.NewError(domain.ErrorCodeInvalidRequest, "ticket not exist or expired", "") diff --git a/internal/ticket/repository/memory/memory_ticket.go b/internal/ticket/repository/memory/memory_ticket.go deleted file mode 100644 index c114b52..0000000 --- a/internal/ticket/repository/memory/memory_ticket.go +++ /dev/null @@ -1,86 +0,0 @@ -package memory - -import ( - "context" - "sync" - "time" - - "source.toby3d.me/toby3d/auth/internal/domain" - "source.toby3d.me/toby3d/auth/internal/ticket" -) - -type ( - Ticket struct { - CreatedAt time.Time - domain.Ticket - } - - memoryTicketRepository struct { - mutex *sync.RWMutex - tickets map[string]Ticket - config domain.Config - } -) - -func NewMemoryTicketRepository(config domain.Config) ticket.Repository { - return &memoryTicketRepository{ - config: config, - mutex: new(sync.RWMutex), - tickets: make(map[string]Ticket), - } -} - -func (repo *memoryTicketRepository) Create(_ context.Context, t domain.Ticket) error { - repo.mutex.Lock() - defer repo.mutex.Unlock() - - repo.tickets[t.Ticket] = Ticket{ - CreatedAt: time.Now().UTC(), - Ticket: t, - } - - return nil -} - -func (repo *memoryTicketRepository) GetAndDelete(_ context.Context, t string) (*domain.Ticket, error) { - repo.mutex.RLock() - - out, ok := repo.tickets[t] - if !ok { - repo.mutex.RUnlock() - - return nil, ticket.ErrNotExist - } - - repo.mutex.RUnlock() - repo.mutex.Lock() - delete(repo.tickets, t) - repo.mutex.Unlock() - - return &out.Ticket, nil -} - -func (repo *memoryTicketRepository) GC() { - ticker := time.NewTicker(time.Second) - defer ticker.Stop() - - for ts := range ticker.C { - ts = ts.UTC() - - repo.mutex.RLock() - - for _, t := range repo.tickets { - if t.CreatedAt.Add(repo.config.Code.Expiry).After(ts) { - continue - } - - repo.mutex.RUnlock() - repo.mutex.Lock() - delete(repo.tickets, t.Ticket.Ticket) - repo.mutex.Unlock() - repo.mutex.RLock() - } - - repo.mutex.RUnlock() - } -} diff --git a/internal/ticket/repository/sqlite3/sqlite3_ticket.go b/internal/ticket/repository/sqlite3/sqlite3_ticket.go deleted file mode 100644 index 7d259e1..0000000 --- a/internal/ticket/repository/sqlite3/sqlite3_ticket.go +++ /dev/null @@ -1,122 +0,0 @@ -package sqlite3 - -import ( - "context" - "database/sql" - "errors" - "fmt" - "net/url" - "time" - - "github.com/jmoiron/sqlx" - - "source.toby3d.me/toby3d/auth/internal/domain" - "source.toby3d.me/toby3d/auth/internal/ticket" -) - -type ( - Ticket struct { - Resource string `db:"resource"` - Subject string `db:"subject"` - Ticket string `db:"ticket"` - CreatedAt sql.NullTime `db:"created_at"` - } - - sqlite3TicketRepository struct { - config *domain.Config - db *sqlx.DB - } -) - -const ( - QueryTable string = `CREATE TABLE IF NOT EXISTS tickets ( - created_at DATETIME NOT NULL, - resource TEXT NOT NULL, - subject TEXT NOT NULL, - ticket TEXT UNIQUE PRIMARY KEY NOT NULL - );` - - QueryGet string = `SELECT * - FROM tickets - WHERE ticket=$1;` - - QueryCreate string = `INSERT INTO tickets (created_at, resource, subject, ticket) - VALUES (:created_at, :resource, :subject, :ticket);` - - QueryDelete string = `DELETE FROM tickets - WHERE ticket=$1;` -) - -func NewSQLite3TicketRepository(db *sqlx.DB, config *domain.Config) ticket.Repository { - db.MustExec(QueryTable) - - return &sqlite3TicketRepository{ - config: config, - db: db, - } -} - -func (repo *sqlite3TicketRepository) Create(ctx context.Context, t domain.Ticket) error { - if _, err := repo.db.NamedExecContext(ctx, QueryCreate, NewTicket(&t)); err != nil { - return fmt.Errorf("cannot create token record in db: %w", err) - } - - return nil -} - -func (repo *sqlite3TicketRepository) GetAndDelete(ctx context.Context, rawTicket string) (*domain.Ticket, error) { - tx, err := repo.db.Beginx() - if err != nil { - _ = tx.Rollback() - - return nil, fmt.Errorf("failed to begin transaction: %w", err) - } - - tkt := new(Ticket) - if err = tx.GetContext(ctx, tkt, QueryGet, rawTicket); err != nil { - //nolint:errcheck // deffered method - defer tx.Rollback() - - if errors.Is(err, sql.ErrNoRows) { - return nil, ticket.ErrNotExist - } - - return nil, fmt.Errorf("cannot find ticket in db: %w", err) - } - - if _, err = tx.ExecContext(ctx, QueryDelete, rawTicket); err != nil { - _ = tx.Rollback() - - return nil, fmt.Errorf("cannot remove ticket from db: %w", err) - } - - if err = tx.Commit(); err != nil { - return nil, fmt.Errorf("failed to commit transaction: %w", err) - } - - result := new(domain.Ticket) - - tkt.Populate(result) - - return result, nil -} - -func (repo *sqlite3TicketRepository) GC() {} - -func NewTicket(src *domain.Ticket) *Ticket { - return &Ticket{ - CreatedAt: sql.NullTime{ - Time: time.Now().UTC(), - Valid: true, - }, - Resource: src.Resource.String(), - Subject: src.Subject.String(), - Ticket: src.Ticket, - } -} - -func (t *Ticket) Populate(dst *domain.Ticket) { - dst.Ticket = t.Ticket - dst.Subject, _ = domain.ParseMe(t.Subject) - dst.Resource, _ = url.Parse(t.Resource) -} diff --git a/internal/ticket/repository/sqlite3/sqlite3_ticket_test.go b/internal/ticket/repository/sqlite3/sqlite3_ticket_test.go deleted file mode 100644 index ca2a426..0000000 --- a/internal/ticket/repository/sqlite3/sqlite3_ticket_test.go +++ /dev/null @@ -1,82 +0,0 @@ -package sqlite3_test - -import ( - "context" - "regexp" - "testing" - - "github.com/DATA-DOG/go-sqlmock" - - "source.toby3d.me/toby3d/auth/internal/domain" - "source.toby3d.me/toby3d/auth/internal/testing/sqltest" - repository "source.toby3d.me/toby3d/auth/internal/ticket/repository/sqlite3" -) - -// nolint: gochecknoglobals // slices cannot be contants -var tableColumns = []string{"created_at", "resource", "subject", "ticket"} - -func TestCreate(t *testing.T) { - t.Parallel() - - ticket := domain.TestTicket(t) - model := repository.NewTicket(ticket) - db, mock, cleanup := sqltest.Open(t) - t.Cleanup(cleanup) - - createTable(t, mock) - mock.ExpectExec(regexp.QuoteMeta(`INSERT INTO tickets`)). - WithArgs( - sqltest.Time{}, - model.Resource, - model.Subject, - model.Ticket, - ). - WillReturnResult(sqlmock.NewResult(1, 1)) - - if err := repository.NewSQLite3TicketRepository(db, domain.TestConfig(t)). - Create(context.Background(), *ticket); err != nil { - t.Error(err) - } -} - -func TestGetAndDelete(t *testing.T) { - t.Parallel() - - ticket := domain.TestTicket(t) - model := repository.NewTicket(ticket) - db, mock, cleanup := sqltest.Open(t) - t.Cleanup(cleanup) - - createTable(t, mock) - mock.ExpectBegin() - mock.ExpectQuery(regexp.QuoteMeta(`SELECT * FROM tickets`)). - WithArgs(model.Ticket). - WillReturnRows(sqlmock.NewRows(tableColumns). - AddRow( - model.CreatedAt.Time, - model.Resource, - model.Subject, - model.Ticket, - )) - mock.ExpectExec(regexp.QuoteMeta(`DELETE FROM tickets`)). - WithArgs(model.Ticket). - WillReturnResult(sqlmock.NewResult(1, 1)) - mock.ExpectCommit() - - result, err := repository.NewSQLite3TicketRepository(db, domain.TestConfig(t)). - GetAndDelete(context.Background(), ticket.Ticket) - if err != nil { - t.Fatal(err) - } - - if result.Ticket != ticket.Ticket { - t.Errorf("GetAndDelete(%s) = %+v, want %+v", ticket.Ticket, result, ticket) - } -} - -func createTable(tb testing.TB, mock sqlmock.Sqlmock) { - tb.Helper() - - mock.ExpectExec(regexp.QuoteMeta(repository.QueryTable)). - WillReturnResult(sqlmock.NewResult(1, 1)) -} diff --git a/internal/ticket/usecase.go b/internal/ticket/usecase.go deleted file mode 100644 index 7687bae..0000000 --- a/internal/ticket/usecase.go +++ /dev/null @@ -1,24 +0,0 @@ -package ticket - -import ( - "context" - - "source.toby3d.me/toby3d/auth/internal/domain" -) - -type UseCase interface { - Generate(ctx context.Context, ticket domain.Ticket) error - - // Redeem transform received ticket into access token. - Redeem(ctx context.Context, ticket domain.Ticket) (*domain.Token, error) - Exchange(ctx context.Context, ticket string) (*domain.Token, error) -} - -var ( - ErrTicketEndpointNotExist error = domain.NewError( - domain.ErrorCodeServerError, "ticket_endpoint not found on ticket resource", "", - ) - ErrTokenEndpointNotExist error = domain.NewError( - domain.ErrorCodeServerError, "token_endpoint not found on ticket resource", "", - ) -) diff --git a/internal/ticket/usecase/ticket_ucase.go b/internal/ticket/usecase/ticket_ucase.go deleted file mode 100644 index c8ccbe6..0000000 --- a/internal/ticket/usecase/ticket_ucase.go +++ /dev/null @@ -1,175 +0,0 @@ -package usecase - -import ( - "bytes" - "context" - "fmt" - "io" - "net/http" - "net/url" - "time" - - "github.com/goccy/go-json" - - "source.toby3d.me/toby3d/auth/internal/common" - "source.toby3d.me/toby3d/auth/internal/domain" - "source.toby3d.me/toby3d/auth/internal/httputil" - "source.toby3d.me/toby3d/auth/internal/ticket" -) - -type ( - //nolint:tagliatelle // https://indieauth.net/source/#access-token-response - AccessToken struct { - Me *domain.Me `json:"me"` - Profile *Profile `json:"profile,omitempty"` - AccessToken string `json:"access_token"` - RefreshToken string `json:"refresh_token"` - ExpiresIn int64 `json:"expires_in,omitempty"` - } - - Profile struct { - Email *domain.Email `json:"email,omitempty"` - Photo *domain.URL `json:"photo,omitempty"` - URL *domain.URL `json:"url,omitempty"` - Name string `json:"name,omitempty"` - } - - ticketUseCase struct { - config *domain.Config - client *http.Client - tickets ticket.Repository - } -) - -func NewTicketUseCase(tickets ticket.Repository, client *http.Client, config *domain.Config) ticket.UseCase { - return &ticketUseCase{ - client: client, - tickets: tickets, - config: config, - } -} - -func (useCase *ticketUseCase) Generate(ctx context.Context, tkt domain.Ticket) error { - resp, err := useCase.client.Get(tkt.Subject.String()) - if err != nil { - return fmt.Errorf("cannot discovery ticket subject: %w", err) - } - - body, err := io.ReadAll(resp.Body) - if err != nil { - return fmt.Errorf("cannot read response body: %w", err) - } - - buf := bytes.NewReader(body) - ticketEndpoint := new(url.URL) - - // NOTE(toby3d): find metadata first - metadata, err := httputil.ExtractFromMetadata(useCase.client, tkt.Subject.String()) - if err == nil && metadata != nil { - ticketEndpoint = metadata.TicketEndpoint - } else { // NOTE(toby3d): fallback to old links searching - endpoints := httputil.ExtractEndpoints(buf, tkt.Subject.URL(), resp.Header.Get(common.HeaderLink), - "ticket_endpoint") - if len(endpoints) > 0 { - ticketEndpoint = endpoints[len(endpoints)-1] - } - } - - if ticketEndpoint == nil { - return ticket.ErrTicketEndpointNotExist - } - - if err = useCase.tickets.Create(ctx, tkt); err != nil { - return fmt.Errorf("cannot save ticket in store: %w", err) - } - - payload := make(url.Values) - payload.Set("ticket", tkt.Ticket) - payload.Set("subject", tkt.Subject.String()) - payload.Set("resource", tkt.Resource.String()) - - if _, err = useCase.client.PostForm(ticketEndpoint.String(), payload); err != nil { - return fmt.Errorf("cannot send ticket to subject ticket_endpoint: %w", err) - } - - return nil -} - -func (useCase *ticketUseCase) Redeem(ctx context.Context, tkt domain.Ticket) (*domain.Token, error) { - resp, err := useCase.client.Get(tkt.Resource.String()) - if err != nil { - return nil, fmt.Errorf("cannot discovery ticket resource: %w", err) - } - - body, err := io.ReadAll(resp.Body) - if err != nil { - return nil, fmt.Errorf("cannot read response body: %w", err) - } - - buf := bytes.NewReader(body) - tokenEndpoint := new(url.URL) - - // NOTE(toby3d): find metadata first - metadata, err := httputil.ExtractFromMetadata(useCase.client, tkt.Resource.String()) - if err == nil && metadata != nil { - tokenEndpoint = metadata.TokenEndpoint - } else { // NOTE(toby3d): fallback to old links searching - endpoints := httputil.ExtractEndpoints(buf, tkt.Resource, resp.Header.Get(common.HeaderLink), - "token_endpoint") - if len(endpoints) > 0 { - tokenEndpoint = endpoints[len(endpoints)-1] - } - } - - if tokenEndpoint == nil || tokenEndpoint.String() == "" { - return nil, ticket.ErrTokenEndpointNotExist - } - - payload := make(url.Values) - payload.Set("grant_type", domain.GrantTypeTicket.String()) - payload.Set("ticket", tkt.Ticket) - - resp, err = useCase.client.PostForm(tokenEndpoint.String(), payload) - if err != nil { - return nil, fmt.Errorf("cannot exchange ticket on token_endpoint: %w", err) - } - - data := new(AccessToken) - if err := json.NewDecoder(resp.Body).Decode(data); err != nil { - return nil, fmt.Errorf("cannot unmarshal access token response: %w", err) - } - - return &domain.Token{ - CreatedAt: time.Now().UTC(), - Expiry: time.Unix(data.ExpiresIn, 0), - Scope: nil, // TODO(toby3d) - // TODO(toby3d): should this also include client_id? - // https://github.com/indieweb/indieauth/issues/85 - ClientID: domain.ClientID{}, - Me: *data.Me, - AccessToken: data.AccessToken, - RefreshToken: "", // TODO(toby3d) - }, nil -} - -func (useCase *ticketUseCase) Exchange(ctx context.Context, ticket string) (*domain.Token, error) { - tkt, err := useCase.tickets.GetAndDelete(ctx, ticket) - if err != nil { - return nil, fmt.Errorf("cannot find provided ticket: %w", err) - } - - token, err := domain.NewToken(domain.NewTokenOptions{ - Expiration: useCase.config.JWT.Expiry, - Scope: domain.Scopes{domain.ScopeRead}, - Issuer: domain.ClientID{}, - Subject: *tkt.Subject, - Secret: []byte(useCase.config.JWT.Secret), - Algorithm: useCase.config.JWT.Algorithm, - NonceLength: useCase.config.JWT.NonceLength, - }) - if err != nil { - return nil, fmt.Errorf("cannot generate a new access token: %w", err) - } - - return token, nil -} diff --git a/internal/ticket/usecase/ticket_ucase_test.go b/internal/ticket/usecase/ticket_ucase_test.go deleted file mode 100644 index 880b84e..0000000 --- a/internal/ticket/usecase/ticket_ucase_test.go +++ /dev/null @@ -1,56 +0,0 @@ -package usecase_test - -import ( - "context" - "fmt" - "net/http" - "net/http/httptest" - "net/url" - "testing" - - "source.toby3d.me/toby3d/auth/internal/common" - "source.toby3d.me/toby3d/auth/internal/domain" - ucase "source.toby3d.me/toby3d/auth/internal/ticket/usecase" -) - -func TestRedeem(t *testing.T) { - t.Parallel() - - token := domain.TestToken(t) - ticket := domain.TestTicket(t) - - tokenServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodPost { - http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed) - - return - } - - w.Header().Set(common.HeaderContentType, common.MIMEApplicationJSONCharsetUTF8) - fmt.Fprintf(w, `{ - "token_type": "Bearer", - "access_token": "%s", - "scope": "%s", - "me": "%s" - }`, token.AccessToken, token.Scope.String(), token.Me.String()) - })) - t.Cleanup(tokenServer.Close) - - subjectServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - w.Header().Set(common.HeaderContentType, common.MIMETextHTMLCharsetUTF8) - fmt.Fprint(w, ``) - })) - t.Cleanup(subjectServer.Close) - - ticket.Resource, _ = url.Parse(subjectServer.URL + "/") - - result, err := ucase.NewTicketUseCase(nil, subjectServer.Client(), domain.TestConfig(t)). - Redeem(context.Background(), *ticket) - if err != nil { - t.Fatal(err) - } - - if result.String() != token.String() { - t.Errorf("Redeem(%+v) = %s, want %s", ticket, result, token) - } -} diff --git a/internal/token/delivery/http/token_http.go b/internal/token/delivery/http/token_http.go index ee0e995..7325398 100644 --- a/internal/token/delivery/http/token_http.go +++ b/internal/token/delivery/http/token_http.go @@ -9,22 +9,19 @@ import ( "source.toby3d.me/toby3d/auth/internal/common" "source.toby3d.me/toby3d/auth/internal/domain" "source.toby3d.me/toby3d/auth/internal/middleware" - "source.toby3d.me/toby3d/auth/internal/ticket" "source.toby3d.me/toby3d/auth/internal/token" "source.toby3d.me/toby3d/auth/internal/urlutil" ) type Handler struct { - config *domain.Config - tokens token.UseCase - tickets ticket.UseCase + config domain.Config + tokens token.UseCase } -func NewHandler(tokens token.UseCase, tickets ticket.UseCase, config *domain.Config) *Handler { +func NewHandler(tokens token.UseCase, config domain.Config) *Handler { return &Handler{ - config: config, - tokens: tokens, - tickets: tickets, + config: config, + tokens: tokens, } } @@ -144,13 +141,10 @@ func (h *Handler) handleAction(w http.ResponseWriter, r *http.Request) { switch action { case domain.ActionRevoke: h.handleRevokation(w, r) - case domain.ActionTicket: - h.handleTicket(w, r) } } } -//nolint:funlen func (h *Handler) handleExchange(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed) @@ -190,35 +184,10 @@ func (h *Handler) handleExchange(w http.ResponseWriter, r *http.Request) { AccessToken: token.AccessToken, ExpiresIn: token.Expiry.Unix(), Me: token.Me.String(), - Profile: nil, + Profile: NewTokenProfileResponse(profile), RefreshToken: "", // TODO(toby3d) } - if profile == nil { - _ = encoder.Encode(resp) - - return - } - - resp.Profile = &TokenProfileResponse{ - Name: profile.GetName(), - URL: "", - Photo: "", - Email: "", - } - - if url := profile.GetURL(); url != nil { - resp.Profile.URL = url.String() - } - - if photo := profile.GetPhoto(); photo != nil { - resp.Profile.Photo = photo.String() - } - - if email := profile.GetEmail(); email != nil { - resp.Profile.Email = email.String() - } - _ = encoder.Encode(resp) w.WriteHeader(http.StatusOK) @@ -256,44 +225,3 @@ func (h *Handler) handleRevokation(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) } - -func (h *Handler) handleTicket(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodPost { - http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed) - - return - } - - w.Header().Set(common.HeaderContentType, common.MIMEApplicationJSONCharsetUTF8) - - encoder := json.NewEncoder(w) - - req := new(TokenTicketRequest) - if err := req.bind(r); err != nil { - w.WriteHeader(http.StatusBadRequest) - - _ = encoder.Encode(err) - - return - } - - tkn, err := h.tickets.Exchange(r.Context(), req.Ticket) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - - _ = encoder.Encode(domain.NewError(domain.ErrorCodeInvalidRequest, err.Error(), - "https://indieauth.net/source/#request")) - - return - } - - _ = encoder.Encode(&TokenExchangeResponse{ - AccessToken: tkn.AccessToken, - Me: tkn.Me.String(), - Profile: nil, - ExpiresIn: tkn.Expiry.Unix(), - RefreshToken: "", // TODO(toby3d) - }) - - w.WriteHeader(http.StatusOK) -} diff --git a/internal/token/delivery/http/token_http_schema.go b/internal/token/delivery/http/token_http_schema.go index 5449aa6..90bac91 100644 --- a/internal/token/delivery/http/token_http_schema.go +++ b/internal/token/delivery/http/token_http_schema.go @@ -37,11 +37,6 @@ type ( Token string `form:"token"` } - TokenTicketRequest struct { - Action domain.Action `form:"action"` - Ticket string `form:"ticket"` - } - TokenIntrospectRequest struct { Token string `form:"token"` } @@ -113,6 +108,35 @@ type ( TokenRevocationResponse struct{} ) +func NewTokenProfileResponse(in *domain.Profile) *TokenProfileResponse { + out := &TokenProfileResponse{ + Name: "", + URL: "", + Photo: "", + Email: "", + } + + if in == nil { + return out + } + + out.Name = in.Name + + if in.URL != nil { + out.URL = in.URL.String() + } + + if in.Photo != nil { + out.Photo = in.Photo.String() + } + + if in.Email != nil { + out.Email = in.Email.String() + } + + return out +} + func (r *TokenExchangeRequest) bind(req *http.Request) error { indieAuthError := new(domain.Error) @@ -165,24 +189,6 @@ func (r *TokenRevocationRequest) bind(req *http.Request) error { return nil } -func (r *TokenTicketRequest) bind(req *http.Request) error { - indieAuthError := new(domain.Error) - - if err := form.Unmarshal([]byte(req.URL.Query().Encode()), r); err != nil { - if errors.As(err, indieAuthError) { - return indieAuthError - } - - return domain.NewError( - domain.ErrorCodeInvalidRequest, - err.Error(), - "https://indieauth.net/source/#request", - ) - } - - return nil -} - func (r *TokenIntrospectRequest) bind(req *http.Request) error { indieAuthError := new(domain.Error) diff --git a/internal/token/delivery/http/token_http_test.go b/internal/token/delivery/http/token_http_test.go index be09fce..6079bff 100644 --- a/internal/token/delivery/http/token_http_test.go +++ b/internal/token/delivery/http/token_http_test.go @@ -17,9 +17,6 @@ import ( profilerepo "source.toby3d.me/toby3d/auth/internal/profile/repository/memory" "source.toby3d.me/toby3d/auth/internal/session" sessionrepo "source.toby3d.me/toby3d/auth/internal/session/repository/memory" - "source.toby3d.me/toby3d/auth/internal/ticket" - ticketrepo "source.toby3d.me/toby3d/auth/internal/ticket/repository/memory" - ticketucase "source.toby3d.me/toby3d/auth/internal/ticket/usecase" "source.toby3d.me/toby3d/auth/internal/token" delivery "source.toby3d.me/toby3d/auth/internal/token/delivery/http" tokenrepo "source.toby3d.me/toby3d/auth/internal/token/repository/memory" @@ -27,15 +24,13 @@ import ( ) type Dependencies struct { - client *http.Client - config *domain.Config - profiles profile.Repository - sessions session.Repository - tickets ticket.Repository - ticketService ticket.UseCase - token *domain.Token - tokens token.Repository - tokenService token.UseCase + client *http.Client + config *domain.Config + profiles profile.Repository + sessions session.Repository + token *domain.Token + tokens token.Repository + tokenService token.UseCase } /* TODO(toby3d) @@ -55,7 +50,7 @@ func TestIntrospection(t *testing.T) { req.Header.Set(common.HeaderContentType, common.MIMEApplicationForm) w := httptest.NewRecorder() - delivery.NewHandler(deps.tokenService, deps.ticketService, deps.config). + delivery.NewHandler(deps.tokenService, *deps.config). ServeHTTP(w, req) resp := w.Result() @@ -89,7 +84,7 @@ func TestRevocation(t *testing.T) { req.Header.Set(common.HeaderAccept, common.MIMEApplicationJSON) w := httptest.NewRecorder() - delivery.NewHandler(deps.tokenService, deps.ticketService, deps.config). + delivery.NewHandler(deps.tokenService, *deps.config). ServeHTTP(w, req) resp := w.Result() @@ -126,25 +121,21 @@ func NewDependencies(tb testing.TB) Dependencies { token := domain.TestToken(tb) profiles := profilerepo.NewMemoryProfileRepository() sessions := sessionrepo.NewMemorySessionRepository(*config) - tickets := ticketrepo.NewMemoryTicketRepository(*config) tokens := tokenrepo.NewMemoryTokenRepository() - ticketService := ticketucase.NewTicketUseCase(tickets, client, config) tokenService := tokenucase.NewTokenUseCase(tokenucase.Config{ - Config: config, + Config: *config, Profiles: profiles, Sessions: sessions, Tokens: tokens, }) return Dependencies{ - client: client, - config: config, - profiles: profiles, - sessions: sessions, - tickets: tickets, - ticketService: ticketService, - token: token, - tokens: tokens, - tokenService: tokenService, + client: client, + config: config, + profiles: profiles, + sessions: sessions, + token: token, + tokens: tokens, + tokenService: tokenService, } } diff --git a/internal/token/usecase/token_ucase.go b/internal/token/usecase/token_ucase.go index e73f087..ce72e6c 100644 --- a/internal/token/usecase/token_ucase.go +++ b/internal/token/usecase/token_ucase.go @@ -16,17 +16,17 @@ import ( type ( Config struct { - Config *domain.Config Profiles profile.Repository Sessions session.Repository Tokens token.Repository + Config domain.Config } tokenUseCase struct { - config *domain.Config profiles profile.Repository sessions session.Repository tokens token.Repository + config domain.Config } ) @@ -132,7 +132,7 @@ func (uc *tokenUseCase) Verify(ctx context.Context, accessToken string) (*domain return result, nil, nil //nolint:nilerr // it's okay to return result without profile } - if !result.Scope.Has(domain.ScopeEmail) && len(profile.Email) > 0 { + if !result.Scope.Has(domain.ScopeEmail) && profile.Email != nil { profile.Email = nil } diff --git a/internal/token/usecase/token_ucase_test.go b/internal/token/usecase/token_ucase_test.go index 63bfb2c..6860061 100644 --- a/internal/token/usecase/token_ucase_test.go +++ b/internal/token/usecase/token_ucase_test.go @@ -45,7 +45,7 @@ func TestExchange(t *testing.T) { } tkn, userInfo, err := usecase.NewTokenUseCase(usecase.Config{ - Config: deps.config, + Config: *deps.config, Profiles: deps.profiles, Sessions: deps.sessions, Tokens: deps.tokens, @@ -68,7 +68,7 @@ func TestVerify(t *testing.T) { deps := NewDependencies(t) ucase := usecase.NewTokenUseCase(usecase.Config{ - Config: deps.config, + Config: *deps.config, Profiles: deps.profiles, Sessions: deps.sessions, Tokens: deps.tokens, @@ -115,7 +115,7 @@ func TestRevoke(t *testing.T) { deps := NewDependencies(t) if err := usecase.NewTokenUseCase(usecase.Config{ - Config: deps.config, + Config: *deps.config, Profiles: deps.profiles, Sessions: deps.sessions, Tokens: deps.tokens, diff --git a/internal/user/delivery/http/user_http.go b/internal/user/delivery/http/user_http.go index 7f01050..45ab570 100644 --- a/internal/user/delivery/http/user_http.go +++ b/internal/user/delivery/http/user_http.go @@ -14,11 +14,11 @@ import ( ) type Handler struct { - config *domain.Config + config domain.Config tokens token.UseCase } -func NewHandler(tokens token.UseCase, config *domain.Config) *Handler { +func NewHandler(tokens token.UseCase, config domain.Config) *Handler { return &Handler{ tokens: tokens, config: config, @@ -78,8 +78,7 @@ func (h *Handler) handleFunc(w http.ResponseWriter, r *http.Request) { } //nolint:errchkjson - _ = encoder.Encode(NewUserInformationResponse(userInfo, - userInfo.HasEmail() && tkn.Scope.Has(domain.ScopeEmail))) + _ = encoder.Encode(NewUserInformationResponse(userInfo, tkn.Scope.Has(domain.ScopeEmail))) w.WriteHeader(http.StatusOK) } diff --git a/internal/user/delivery/http/user_http_schema.go b/internal/user/delivery/http/user_http_schema.go index c6a27ff..c10e00e 100644 --- a/internal/user/delivery/http/user_http_schema.go +++ b/internal/user/delivery/http/user_http_schema.go @@ -3,10 +3,10 @@ package http import "source.toby3d.me/toby3d/auth/internal/domain" type UserInformationResponse struct { - URL *domain.URL `json:"url,omitempty"` - Photo *domain.URL `json:"photo,omitempty"` - Email *domain.Email `json:"email,omitempty"` - Name string `json:"name,omitempty"` + URL string `json:"url,omitempty"` + Photo string `json:"photo,omitempty"` + Email string `json:"email,omitempty"` + Name string `json:"name,omitempty"` } func NewUserInformationResponse(in *domain.Profile, hasEmail bool) *UserInformationResponse { @@ -16,20 +16,18 @@ func NewUserInformationResponse(in *domain.Profile, hasEmail bool) *UserInformat return out } - if in.HasName() { - out.Name = in.GetName() + out.Name = in.Name + + if in.URL != nil { + out.URL = in.URL.String() } - if in.HasURL() { - out.URL = &domain.URL{URL: in.GetURL()} + if in.Photo != nil { + out.Photo = in.Photo.String() } - if in.HasPhoto() { - out.Photo = &domain.URL{URL: in.GetPhoto()} - } - - if hasEmail { - out.Email = in.GetEmail() + if hasEmail && in.Email != nil { + out.Email = in.Email.String() } return out diff --git a/internal/user/delivery/http/user_http_test.go b/internal/user/delivery/http/user_http_test.go index 33526e2..acfeb7d 100644 --- a/internal/user/delivery/http/user_http_test.go +++ b/internal/user/delivery/http/user_http_test.go @@ -43,11 +43,10 @@ func TestUserInfo(t *testing.T) { req.Header.Set(common.HeaderAuthorization, "Bearer "+deps.token.AccessToken) w := httptest.NewRecorder() - delivery.NewHandler(deps.tokenService, deps.config). + delivery.NewHandler(deps.tokenService, *deps.config). ServeHTTP(w, req) resp := w.Result() - if exp := http.StatusOK; resp.StatusCode != exp { t.Errorf("%s %s = %d, want %d", req.Method, req.RequestURI, resp.StatusCode, exp) } @@ -57,13 +56,7 @@ func TestUserInfo(t *testing.T) { t.Fatal(err) } - exp := &delivery.UserInformationResponse{ - Name: deps.profile.GetName(), - URL: &domain.URL{URL: deps.profile.GetURL()}, - Photo: &domain.URL{URL: deps.profile.GetPhoto()}, - Email: deps.profile.GetEmail(), - } - + exp := delivery.NewUserInformationResponse(deps.profile, true) if diff := cmp.Diff(result, exp, cmp.AllowUnexported(domain.URL{}, domain.Email{})); diff != "" { t.Errorf("%s %s = %+v, want %+v", req.Method, req.RequestURI, result, exp) } @@ -85,7 +78,7 @@ func NewDependencies(tb testing.TB) Dependencies { token: domain.TestToken(tb), tokens: tokens, tokenService: tokenucase.NewTokenUseCase(tokenucase.Config{ - Config: config, + Config: *config, Profiles: profiles, Sessions: sessions, Tokens: tokens, diff --git a/main.go b/main.go index 76fa691..e7c26c6 100644 --- a/main.go +++ b/main.go @@ -47,11 +47,6 @@ import ( sessionmemoryrepo "source.toby3d.me/toby3d/auth/internal/session/repository/memory" sessionsqlite3repo "source.toby3d.me/toby3d/auth/internal/session/repository/sqlite3" sessionucase "source.toby3d.me/toby3d/auth/internal/session/usecase" - "source.toby3d.me/toby3d/auth/internal/ticket" - tickethttpdelivery "source.toby3d.me/toby3d/auth/internal/ticket/delivery/http" - ticketmemoryrepo "source.toby3d.me/toby3d/auth/internal/ticket/repository/memory" - ticketsqlite3repo "source.toby3d.me/toby3d/auth/internal/ticket/repository/sqlite3" - ticketucase "source.toby3d.me/toby3d/auth/internal/ticket/usecase" "source.toby3d.me/toby3d/auth/internal/token" tokenhttpdelivery "source.toby3d.me/toby3d/auth/internal/token/delivery/http" tokenmemoryrepo "source.toby3d.me/toby3d/auth/internal/token/repository/memory" @@ -67,7 +62,6 @@ type ( clients client.UseCase matcher language.Matcher sessions session.UseCase - tickets ticket.UseCase profiles profile.UseCase tokens token.UseCase static fs.FS @@ -77,7 +71,6 @@ type ( Client *http.Client Clients client.Repository Sessions session.Repository - Tickets ticket.Repository Tokens token.Repository Profiles profile.Repository Static fs.FS @@ -159,7 +152,6 @@ func main() { default: opts.Tokens = tokenmemoryrepo.NewMemoryTokenRepository() opts.Sessions = sessionmemoryrepo.NewMemorySessionRepository(*config) - opts.Tickets = ticketmemoryrepo.NewMemoryTicketRepository(*config) case "sqlite3": store, err := sqlx.Open("sqlite", config.Database.Path) if err != nil { @@ -172,7 +164,6 @@ func main() { opts.Tokens = tokensqlite3repo.NewSQLite3TokenRepository(store) opts.Sessions = sessionsqlite3repo.NewSQLite3SessionRepository(store) - opts.Tickets = ticketsqlite3repo.NewSQLite3TicketRepository(store, config) } go opts.Sessions.GC() @@ -251,14 +242,13 @@ func main() { func NewApp(opts NewAppOptions) *App { return &App{ static: opts.Static, - auth: authucase.NewAuthUseCase(opts.Sessions, opts.Profiles, config), + auth: authucase.NewAuthUseCase(opts.Sessions, opts.Profiles, *config), clients: clientucase.NewClientUseCase(opts.Clients), matcher: language.NewMatcher(message.DefaultCatalog.Languages()), profiles: profileucase.NewProfileUseCase(opts.Profiles), sessions: sessionucase.NewSessionUseCase(opts.Sessions), - tickets: ticketucase.NewTicketUseCase(opts.Tickets, opts.Client, config), tokens: tokenucase.NewTokenUseCase(tokenucase.Config{ - Config: config, + Config: *config, Profiles: opts.Profiles, Sessions: opts.Sessions, Tokens: opts.Tokens, @@ -327,15 +317,14 @@ func (app *App) Handler() http.Handler { Matcher: app.matcher, Profiles: app.profiles, }) - token := tokenhttpdelivery.NewHandler(app.tokens, app.tickets, config) + token := tokenhttpdelivery.NewHandler(app.tokens, *config) client := clienthttpdelivery.NewHandler(clienthttpdelivery.NewHandlerOptions{ Client: *indieAuthClient, Config: *config, Matcher: app.matcher, Tokens: app.tokens, }) - user := userhttpdelivery.NewHandler(app.tokens, config) - ticket := tickethttpdelivery.NewHandler(app.tickets, app.matcher, *config) + user := userhttpdelivery.NewHandler(app.tokens, *config) staticHandler := http.FileServer(http.FS(app.static)) return http.HandlerFunc(middleware.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -368,10 +357,6 @@ func (app *App) Handler() http.Handler { r.URL.Path = tail user.ServeHTTP(w, r) - case "ticket": - r.URL.Path = tail - - ticket.ServeHTTP(w, r) } }).Intercept(middleware.LogFmt())) }