remove user creation and login endpoints
This commit is contained in:
+1
-27
@@ -5,32 +5,6 @@
|
||||
"path": "/auth",
|
||||
"entrypoint": "auth",
|
||||
"authentication": [],
|
||||
"apis": [
|
||||
{
|
||||
"name": "login",
|
||||
"method": "POST",
|
||||
"title": "User login",
|
||||
"parameters": {
|
||||
"post": [
|
||||
{ "type": "string", "name": "username", "required": true, "title": "Username or email" },
|
||||
{ "type": "string", "name": "password", "required": true, "title": "Password for user" }
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "create",
|
||||
"path": "/create",
|
||||
"method": "POST",
|
||||
"title": "Create new user",
|
||||
"parameters": {
|
||||
"post": [
|
||||
{ "type": "string", "name": "name", "required": true, "title": "Display name" },
|
||||
{ "type": "string", "name": "email", "required": true, "title": "Email" },
|
||||
{ "type": "string", "name": "username", "required": true, "title": "Username" },
|
||||
{ "type": "string", "name": "password", "required": true, "title": "Password" }
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
"apis": []
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/crusttech/crust/auth/rest/request"
|
||||
"github.com/crusttech/crust/auth/service"
|
||||
"github.com/crusttech/crust/auth/types"
|
||||
"github.com/crusttech/crust/internal/auth"
|
||||
)
|
||||
|
||||
var _ = errors.Wrap
|
||||
|
||||
type (
|
||||
Auth struct {
|
||||
user service.UserService
|
||||
token auth.TokenEncoder
|
||||
}
|
||||
)
|
||||
|
||||
func (Auth) New(credValidator service.UserService, tknEncoder auth.TokenEncoder) *Auth {
|
||||
return &Auth{
|
||||
credValidator,
|
||||
tknEncoder,
|
||||
}
|
||||
}
|
||||
|
||||
func (ctrl *Auth) Login(ctx context.Context, r *request.AuthLogin) (interface{}, error) {
|
||||
return ctrl.tokenize(ctrl.user.With(ctx).ValidateCredentials(r.Username, r.Password))
|
||||
}
|
||||
|
||||
func (ctrl *Auth) Create(ctx context.Context, r *request.AuthCreate) (interface{}, error) {
|
||||
user := &types.User{Username: r.Username}
|
||||
user.GeneratePassword(r.Password)
|
||||
return ctrl.tokenize(ctrl.user.With(ctx).Create(user))
|
||||
}
|
||||
|
||||
// Wraps user return value and appends JWT
|
||||
func (ctrl *Auth) tokenize(user *types.User, err error) (interface{}, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return struct {
|
||||
JWT string
|
||||
User *types.User `json:"user"`
|
||||
}{
|
||||
JWT: ctrl.token.Encode(user),
|
||||
User: user,
|
||||
}, nil
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
package handlers
|
||||
|
||||
/*
|
||||
Hello! This file is auto-generated from `docs/src/spec.json`.
|
||||
|
||||
For development:
|
||||
In order to update the generated files, edit this file under the location,
|
||||
add your struct fields, imports, API definitions and whatever you want, and:
|
||||
|
||||
1. run [spec](https://github.com/titpetric/spec) in the same folder,
|
||||
2. run `./_gen.php` in this folder.
|
||||
|
||||
You may edit `auth.go`, `auth.util.go` or `auth_test.go` to
|
||||
implement your API calls, helper functions and tests. The file `auth.go`
|
||||
is only generated the first time, and will not be overwritten if it exists.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/go-chi/chi"
|
||||
"net/http"
|
||||
|
||||
"github.com/titpetric/factory/resputil"
|
||||
|
||||
"github.com/crusttech/crust/auth/rest/request"
|
||||
)
|
||||
|
||||
// Internal API interface
|
||||
type AuthAPI interface {
|
||||
Login(context.Context, *request.AuthLogin) (interface{}, error)
|
||||
Create(context.Context, *request.AuthCreate) (interface{}, error)
|
||||
}
|
||||
|
||||
// HTTP API interface
|
||||
type Auth struct {
|
||||
Login func(http.ResponseWriter, *http.Request)
|
||||
Create func(http.ResponseWriter, *http.Request)
|
||||
}
|
||||
|
||||
func NewAuth(ah AuthAPI) *Auth {
|
||||
return &Auth{
|
||||
Login: func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
params := request.NewAuthLogin()
|
||||
resputil.JSON(w, params.Fill(r), func() (interface{}, error) {
|
||||
return ah.Login(r.Context(), params)
|
||||
})
|
||||
},
|
||||
Create: func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
params := request.NewAuthCreate()
|
||||
resputil.JSON(w, params.Fill(r), func() (interface{}, error) {
|
||||
return ah.Create(r.Context(), params)
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (ah *Auth) MountRoutes(r chi.Router, middlewares ...func(http.Handler) http.Handler) {
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(middlewares...)
|
||||
r.Route("/auth", func(r chi.Router) {
|
||||
r.Post("/login", ah.Login)
|
||||
r.Post("/create", ah.Create)
|
||||
})
|
||||
})
|
||||
}
|
||||
+2
-2
@@ -36,7 +36,7 @@ type (
|
||||
|
||||
jwtEncodeCookieSetter interface {
|
||||
auth.TokenEncoder
|
||||
SetToCookie(w http.ResponseWriter, r *http.Request, identity auth.Identifiable)
|
||||
SetCookie(w http.ResponseWriter, r *http.Request, identity auth.Identifiable)
|
||||
}
|
||||
)
|
||||
|
||||
@@ -162,7 +162,7 @@ func (c *openIdConnect) HandleOAuth2Callback(w http.ResponseWriter, r *http.Requ
|
||||
resputil.JSON(w, err)
|
||||
return
|
||||
} else {
|
||||
c.jwt.SetToCookie(w, r, user)
|
||||
c.jwt.SetCookie(w, r, user)
|
||||
}
|
||||
|
||||
http.Redirect(w, r, c.appURL, http.StatusSeeOther)
|
||||
|
||||
@@ -1,141 +0,0 @@
|
||||
package request
|
||||
|
||||
/*
|
||||
Hello! This file is auto-generated from `docs/src/spec.json`.
|
||||
|
||||
For development:
|
||||
In order to update the generated files, edit this file under the location,
|
||||
add your struct fields, imports, API definitions and whatever you want, and:
|
||||
|
||||
1. run [spec](https://github.com/titpetric/spec) in the same folder,
|
||||
2. run `./_gen.php` in this folder.
|
||||
|
||||
You may edit `auth.go`, `auth.util.go` or `auth_test.go` to
|
||||
implement your API calls, helper functions and tests. The file `auth.go`
|
||||
is only generated the first time, and will not be overwritten if it exists.
|
||||
*/
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/jmoiron/sqlx/types"
|
||||
"github.com/pkg/errors"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var _ = chi.URLParam
|
||||
var _ = types.JSONText{}
|
||||
var _ = multipart.FileHeader{}
|
||||
|
||||
// Auth login request parameters
|
||||
type AuthLogin struct {
|
||||
Username string
|
||||
Password string
|
||||
}
|
||||
|
||||
func NewAuthLogin() *AuthLogin {
|
||||
return &AuthLogin{}
|
||||
}
|
||||
|
||||
func (a *AuthLogin) Fill(r *http.Request) error {
|
||||
var err error
|
||||
|
||||
if strings.ToLower(r.Header.Get("content-type")) == "application/json" {
|
||||
err = json.NewDecoder(r.Body).Decode(a)
|
||||
|
||||
switch {
|
||||
case err == io.EOF:
|
||||
err = nil
|
||||
case err != nil:
|
||||
return errors.Wrap(err, "error parsing http request body")
|
||||
}
|
||||
}
|
||||
|
||||
r.ParseForm()
|
||||
get := map[string]string{}
|
||||
post := map[string]string{}
|
||||
urlQuery := r.URL.Query()
|
||||
for name, param := range urlQuery {
|
||||
get[name] = string(param[0])
|
||||
}
|
||||
postVars := r.Form
|
||||
for name, param := range postVars {
|
||||
post[name] = string(param[0])
|
||||
}
|
||||
|
||||
if val, ok := post["username"]; ok {
|
||||
|
||||
a.Username = val
|
||||
}
|
||||
if val, ok := post["password"]; ok {
|
||||
|
||||
a.Password = val
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
var _ RequestFiller = NewAuthLogin()
|
||||
|
||||
// Auth create request parameters
|
||||
type AuthCreate struct {
|
||||
Name string
|
||||
Email string
|
||||
Username string
|
||||
Password string
|
||||
}
|
||||
|
||||
func NewAuthCreate() *AuthCreate {
|
||||
return &AuthCreate{}
|
||||
}
|
||||
|
||||
func (a *AuthCreate) Fill(r *http.Request) error {
|
||||
var err error
|
||||
|
||||
if strings.ToLower(r.Header.Get("content-type")) == "application/json" {
|
||||
err = json.NewDecoder(r.Body).Decode(a)
|
||||
|
||||
switch {
|
||||
case err == io.EOF:
|
||||
err = nil
|
||||
case err != nil:
|
||||
return errors.Wrap(err, "error parsing http request body")
|
||||
}
|
||||
}
|
||||
|
||||
r.ParseForm()
|
||||
get := map[string]string{}
|
||||
post := map[string]string{}
|
||||
urlQuery := r.URL.Query()
|
||||
for name, param := range urlQuery {
|
||||
get[name] = string(param[0])
|
||||
}
|
||||
postVars := r.Form
|
||||
for name, param := range postVars {
|
||||
post[name] = string(param[0])
|
||||
}
|
||||
|
||||
if val, ok := post["name"]; ok {
|
||||
|
||||
a.Name = val
|
||||
}
|
||||
if val, ok := post["email"]; ok {
|
||||
|
||||
a.Email = val
|
||||
}
|
||||
if val, ok := post["username"]; ok {
|
||||
|
||||
a.Username = val
|
||||
}
|
||||
if val, ok := post["password"]; ok {
|
||||
|
||||
a.Password = val
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
var _ RequestFiller = NewAuthCreate()
|
||||
@@ -1,10 +0,0 @@
|
||||
package request
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// RequestFiller is an interface for typed request parameters
|
||||
type RequestFiller interface {
|
||||
Fill(r *http.Request) error
|
||||
}
|
||||
+32
-9
@@ -6,17 +6,24 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/crusttech/crust/auth/repository"
|
||||
"github.com/crusttech/crust/auth/types"
|
||||
"github.com/crusttech/crust/internal/auth"
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/titpetric/factory/resputil"
|
||||
|
||||
"github.com/crusttech/crust/auth/rest/handlers"
|
||||
"github.com/crusttech/crust/auth/service"
|
||||
"github.com/crusttech/crust/internal/config"
|
||||
)
|
||||
|
||||
type (
|
||||
checkResponse struct {
|
||||
JWT string `json:"jwt"`
|
||||
User *types.User `json:"user"`
|
||||
}
|
||||
)
|
||||
|
||||
func MountRoutes(oidcConfig *config.OIDC, jwtAuth jwtEncodeCookieSetter) func(chi.Router) {
|
||||
var userSvc = service.User()
|
||||
|
||||
var ctx = context.Background()
|
||||
|
||||
oidc, err := OpenIdConnect(ctx, oidcConfig, userSvc, jwtAuth, repository.NewSettings(ctx))
|
||||
@@ -26,8 +33,6 @@ func MountRoutes(oidcConfig *config.OIDC, jwtAuth jwtEncodeCookieSetter) func(ch
|
||||
|
||||
// Initialize handers & controllers.
|
||||
return func(r chi.Router) {
|
||||
handlers.NewAuth(Auth{}.New(userSvc, jwtAuth)).MountRoutes(r)
|
||||
|
||||
if oidc != nil {
|
||||
r.Route("/oidc", func(r chi.Router) {
|
||||
r.Get("/", oidc.HandleRedirect)
|
||||
@@ -35,12 +40,30 @@ func MountRoutes(oidcConfig *config.OIDC, jwtAuth jwtEncodeCookieSetter) func(ch
|
||||
})
|
||||
}
|
||||
|
||||
r.Get("/jwt", func(w http.ResponseWriter, r *http.Request) {
|
||||
if c, err := r.Cookie("jwt"); err != nil {
|
||||
resputil.JSON(w, "")
|
||||
} else {
|
||||
resputil.JSON(w, c.Value)
|
||||
r.Get("/check", func(w http.ResponseWriter, r *http.Request) {
|
||||
if c, err := r.Cookie("jwt"); err == nil {
|
||||
ctx := r.Context()
|
||||
|
||||
if identity := auth.GetIdentityFromContext(ctx); identity != nil && identity.Valid() {
|
||||
if user, err := service.DefaultUser.With(ctx).FindByID(identity.Identity()); err == nil {
|
||||
resputil.JSON(w, checkResponse{
|
||||
JWT: c.Value,
|
||||
User: user,
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Did not send response, assuming invalid cookie
|
||||
jwtAuth.SetCookie(w, r, nil)
|
||||
}
|
||||
|
||||
resputil.JSON(w, "")
|
||||
})
|
||||
|
||||
r.Delete("/check", func(w http.ResponseWriter, r *http.Request) {
|
||||
jwtAuth.SetCookie(w, r, nil)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
"github.com/SentimensRG/ctx/sigctx"
|
||||
"github.com/crusttech/crust/auth/rest"
|
||||
"github.com/crusttech/crust/auth/service"
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/go-chi/cors"
|
||||
"github.com/pkg/errors"
|
||||
@@ -46,6 +47,8 @@ func Init() error {
|
||||
},
|
||||
})
|
||||
|
||||
service.Init()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -3,7 +3,7 @@ set -e
|
||||
|
||||
function gofmt {
|
||||
echo "=== fmt all folders ==="
|
||||
GOPATHS=$(find -name '*.go' | grep -v vendor/ | xargs -n1 dirname | sort | uniq)
|
||||
GOPATHS=$(find . -name '*.go' | grep -v vendor/ | xargs -n1 dirname | sort | uniq)
|
||||
for FOLDER in $GOPATHS; do
|
||||
#echo "== go fmt $FOLDER =="
|
||||
cd $FOLDER
|
||||
@@ -26,4 +26,4 @@ for SPEC in $SPECS; do
|
||||
codegen/codegen.php $(basename $SRC) | tee -a /dev/stderr
|
||||
done
|
||||
|
||||
gofmt
|
||||
gofmt
|
||||
|
||||
+13
-9
@@ -69,16 +69,20 @@ func (t *jwt) Authenticator() func(http.Handler) http.Handler {
|
||||
}
|
||||
|
||||
// Extracts and authenticates JWT from context, validates claims
|
||||
func (t *jwt) SetToCookie(w http.ResponseWriter, r *http.Request, identity Identifiable) {
|
||||
// Store state to cookie as well
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: "jwt",
|
||||
Value: t.Encode(identity),
|
||||
|
||||
func (t *jwt) SetCookie(w http.ResponseWriter, r *http.Request, identity Identifiable) {
|
||||
cookie := &http.Cookie{
|
||||
Name: "jwt",
|
||||
Expires: time.Now().Add(time.Duration(t.expiry) * time.Minute),
|
||||
Secure: r.URL.Scheme == "https",
|
||||
Domain: t.cookieDomain,
|
||||
Path: "/",
|
||||
}
|
||||
|
||||
Secure: r.URL.Scheme == "https",
|
||||
Path: "/",
|
||||
})
|
||||
if identity == nil {
|
||||
cookie.Expires = time.Unix(0, 0)
|
||||
} else {
|
||||
cookie.Value = t.Encode(identity)
|
||||
}
|
||||
|
||||
http.SetCookie(w, cookie)
|
||||
}
|
||||
|
||||
+1
-38
@@ -614,41 +614,4 @@ The following event types may be sent with a message event:
|
||||
| Parameter | Type | Method | Description | Default | Required? |
|
||||
| --------- | ---- | ------ | ----------- | ------- | --------- |
|
||||
| userID | uint64 | PATH | User ID | N/A | YES |
|
||||
| message | string | POST | Message contents (markdown) | N/A | YES |
|
||||
|
||||
|
||||
|
||||
|
||||
# Authentication
|
||||
|
||||
## User login
|
||||
|
||||
#### Method
|
||||
|
||||
| URI | Protocol | Method | Authentication |
|
||||
| --- | -------- | ------ | -------------- |
|
||||
| `/auth/login` | HTTP/S | POST | |
|
||||
|
||||
#### Request parameters
|
||||
|
||||
| Parameter | Type | Method | Description | Default | Required? |
|
||||
| --------- | ---- | ------ | ----------- | ------- | --------- |
|
||||
| username | string | POST | Username or email | N/A | YES |
|
||||
| password | string | POST | Password for user | N/A | YES |
|
||||
|
||||
## Create new user
|
||||
|
||||
#### Method
|
||||
|
||||
| URI | Protocol | Method | Authentication |
|
||||
| --- | -------- | ------ | -------------- |
|
||||
| `/auth/create` | HTTP/S | POST | |
|
||||
|
||||
#### Request parameters
|
||||
|
||||
| Parameter | Type | Method | Description | Default | Required? |
|
||||
| --------- | ---- | ------ | ----------- | ------- | --------- |
|
||||
| name | string | POST | Display name | N/A | YES |
|
||||
| email | string | POST | Email | N/A | YES |
|
||||
| username | string | POST | Username | N/A | YES |
|
||||
| password | string | POST | Password | N/A | YES |
|
||||
| message | string | POST | Message contents (markdown) | N/A | YES |
|
||||
@@ -533,39 +533,5 @@
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Authentication",
|
||||
"package": "sam",
|
||||
"path": "/auth",
|
||||
"entrypoint": "auth",
|
||||
"authentication": [],
|
||||
"apis": [
|
||||
{
|
||||
"name": "login",
|
||||
"method": "POST",
|
||||
"title": "User login",
|
||||
"parameters": {
|
||||
"post": [
|
||||
{ "type": "string", "name": "username", "required": true, "title": "Username or email" },
|
||||
{ "type": "string", "name": "password", "required": true, "title": "Password for user" }
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "create",
|
||||
"path": "/create",
|
||||
"method": "POST",
|
||||
"title": "Create new user",
|
||||
"parameters": {
|
||||
"post": [
|
||||
{ "type": "string", "name": "name", "required": true, "title": "Display name" },
|
||||
{ "type": "string", "name": "email", "required": true, "title": "Email" },
|
||||
{ "type": "string", "name": "username", "required": true, "title": "Username" },
|
||||
{ "type": "string", "name": "password", "required": true, "title": "Password" }
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
{
|
||||
"Title": "Authentication",
|
||||
"Package": "sam",
|
||||
"Interface": "Auth",
|
||||
"Struct": null,
|
||||
"Parameters": null,
|
||||
"Protocol": "",
|
||||
"Authentication": [],
|
||||
"Path": "/auth",
|
||||
"APIs": [
|
||||
{
|
||||
"Name": "login",
|
||||
"Method": "POST",
|
||||
"Title": "User login",
|
||||
"Path": "/login",
|
||||
"Parameters": {
|
||||
"post": [
|
||||
{
|
||||
"name": "username",
|
||||
"required": true,
|
||||
"title": "Username or email",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "password",
|
||||
"required": true,
|
||||
"title": "Password for user",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "create",
|
||||
"Method": "POST",
|
||||
"Title": "Create new user",
|
||||
"Path": "/create",
|
||||
"Parameters": {
|
||||
"post": [
|
||||
{
|
||||
"name": "name",
|
||||
"required": true,
|
||||
"title": "Display name",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "email",
|
||||
"required": true,
|
||||
"title": "Email",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "username",
|
||||
"required": true,
|
||||
"title": "Username",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "password",
|
||||
"required": true,
|
||||
"title": "Password",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -18,8 +18,8 @@ type (
|
||||
)
|
||||
|
||||
var (
|
||||
_db *factory.DB
|
||||
_ctx context.Context
|
||||
_db *factory.DB
|
||||
_ctx context.Context
|
||||
)
|
||||
|
||||
// DB returns a repository-wide singleton DB handle
|
||||
@@ -56,7 +56,6 @@ func (r *repository) Context() context.Context {
|
||||
return r.ctx
|
||||
}
|
||||
|
||||
|
||||
// db returns context-aware db handle
|
||||
func (r *repository) db() *factory.DB {
|
||||
return r.dbh(r.ctx)
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/crusttech/crust/auth/service"
|
||||
"github.com/crusttech/crust/auth/types"
|
||||
"github.com/crusttech/crust/internal/auth"
|
||||
"github.com/crusttech/crust/sam/rest/request"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var _ = errors.Wrap
|
||||
|
||||
type (
|
||||
Auth struct {
|
||||
svc struct {
|
||||
user service.UserService
|
||||
token auth.TokenEncoder
|
||||
}
|
||||
}
|
||||
|
||||
authPayload struct {
|
||||
JWT string
|
||||
User *types.User `json:"user"`
|
||||
}
|
||||
|
||||
authUserBasics interface {
|
||||
ValidateCredentials(ctx context.Context, username, password string) (*types.User, error)
|
||||
Create(ctx context.Context, input *types.User) (user *types.User, err error)
|
||||
}
|
||||
)
|
||||
|
||||
func (Auth) New(tknEncoder auth.TokenEncoder) *Auth {
|
||||
ctrl := &Auth{}
|
||||
ctrl.svc.user = service.DefaultUser
|
||||
ctrl.svc.token = tknEncoder
|
||||
|
||||
return ctrl
|
||||
}
|
||||
|
||||
func (ctrl *Auth) Login(ctx context.Context, r *request.AuthLogin) (interface{}, error) {
|
||||
return ctrl.tokenize(ctrl.svc.user.ValidateCredentials(r.Username, r.Password))
|
||||
}
|
||||
|
||||
func (ctrl *Auth) Create(ctx context.Context, r *request.AuthCreate) (interface{}, error) {
|
||||
user := &types.User{Username: r.Username}
|
||||
user.GeneratePassword(r.Password)
|
||||
|
||||
return ctrl.tokenize(ctrl.svc.user.With(ctx).Create(user))
|
||||
}
|
||||
|
||||
// Wraps user return value and appends JWT
|
||||
func (ctrl *Auth) tokenize(user *types.User, err error) (interface{}, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &authPayload{
|
||||
JWT: ctrl.svc.token.Encode(user),
|
||||
User: user,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (ap authPayload) Token() string {
|
||||
return ap.JWT
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
package handlers
|
||||
|
||||
/*
|
||||
Hello! This file is auto-generated from `docs/src/spec.json`.
|
||||
|
||||
For development:
|
||||
In order to update the generated files, edit this file under the location,
|
||||
add your struct fields, imports, API definitions and whatever you want, and:
|
||||
|
||||
1. run [spec](https://github.com/titpetric/spec) in the same folder,
|
||||
2. run `./_gen.php` in this folder.
|
||||
|
||||
You may edit `auth.go`, `auth.util.go` or `auth_test.go` to
|
||||
implement your API calls, helper functions and tests. The file `auth.go`
|
||||
is only generated the first time, and will not be overwritten if it exists.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/go-chi/chi"
|
||||
"net/http"
|
||||
|
||||
"github.com/titpetric/factory/resputil"
|
||||
|
||||
"github.com/crusttech/crust/sam/rest/request"
|
||||
)
|
||||
|
||||
// Internal API interface
|
||||
type AuthAPI interface {
|
||||
Login(context.Context, *request.AuthLogin) (interface{}, error)
|
||||
Create(context.Context, *request.AuthCreate) (interface{}, error)
|
||||
}
|
||||
|
||||
// HTTP API interface
|
||||
type Auth struct {
|
||||
Login func(http.ResponseWriter, *http.Request)
|
||||
Create func(http.ResponseWriter, *http.Request)
|
||||
}
|
||||
|
||||
func NewAuth(ah AuthAPI) *Auth {
|
||||
return &Auth{
|
||||
Login: func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
params := request.NewAuthLogin()
|
||||
resputil.JSON(w, params.Fill(r), func() (interface{}, error) {
|
||||
return ah.Login(r.Context(), params)
|
||||
})
|
||||
},
|
||||
Create: func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
params := request.NewAuthCreate()
|
||||
resputil.JSON(w, params.Fill(r), func() (interface{}, error) {
|
||||
return ah.Create(r.Context(), params)
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (ah *Auth) MountRoutes(r chi.Router, middlewares ...func(http.Handler) http.Handler) {
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(middlewares...)
|
||||
r.Route("/auth", func(r chi.Router) {
|
||||
r.Post("/login", ah.Login)
|
||||
r.Post("/create", ah.Create)
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
package handlers
|
||||
|
||||
/*
|
||||
Hello! This file is auto-generated from `docs/src/spec.json`.
|
||||
|
||||
For development:
|
||||
In order to update the generated files, edit this file under the location,
|
||||
add your struct fields, imports, API definitions and whatever you want, and:
|
||||
|
||||
1. run [spec](https://github.com/titpetric/spec) in the same folder,
|
||||
2. run `./_gen.php` in this folder.
|
||||
|
||||
You may edit `auth.go`, `auth.util.go` or `auth_test.go` to
|
||||
implement your API calls, helper functions and tests. The file `auth.go`
|
||||
is only generated the first time, and will not be overwritten if it exists.
|
||||
*/
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/titpetric/factory/resputil"
|
||||
|
||||
"github.com/crusttech/crust/sam/rest/request"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
type (
|
||||
authPayload interface {
|
||||
Token() string
|
||||
}
|
||||
)
|
||||
|
||||
// Initializies custom auth handler that attaches cookie info
|
||||
//
|
||||
// Cookie with JWT is added on successful login or user creation
|
||||
//
|
||||
func NewAuthCustom(ah AuthAPI, cookieExp int) *Auth {
|
||||
setCookie := func(w http.ResponseWriter, reqUrl *url.URL) func(payload interface{}, err error) (interface{}, error) {
|
||||
return func(payload interface{}, err error) (interface{}, error) {
|
||||
if ap, ok := payload.(authPayload); ok && err == nil {
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: "jwt",
|
||||
Value: ap.Token(),
|
||||
|
||||
HttpOnly: false, // we need this for attachments & ws!
|
||||
Secure: reqUrl.Scheme == "https",
|
||||
Path: "/",
|
||||
//Domain: "localhost",
|
||||
|
||||
// @todo read from the config file.
|
||||
Expires: time.Now().Add(time.Duration(cookieExp) * time.Minute),
|
||||
})
|
||||
}
|
||||
|
||||
return payload, err
|
||||
}
|
||||
}
|
||||
|
||||
return &Auth{
|
||||
Login: func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
params := request.NewAuthLogin()
|
||||
resputil.JSON(w, params.Fill(r), func() (interface{}, error) {
|
||||
return setCookie(w, r.URL)(ah.Login(r.Context(), params))
|
||||
})
|
||||
},
|
||||
Create: func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
params := request.NewAuthCreate()
|
||||
resputil.JSON(w, params.Fill(r), func() (interface{}, error) {
|
||||
return setCookie(w, r.URL)(ah.Create(r.Context(), params))
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
package request
|
||||
|
||||
/*
|
||||
Hello! This file is auto-generated from `docs/src/spec.json`.
|
||||
|
||||
For development:
|
||||
In order to update the generated files, edit this file under the location,
|
||||
add your struct fields, imports, API definitions and whatever you want, and:
|
||||
|
||||
1. run [spec](https://github.com/titpetric/spec) in the same folder,
|
||||
2. run `./_gen.php` in this folder.
|
||||
|
||||
You may edit `auth.go`, `auth.util.go` or `auth_test.go` to
|
||||
implement your API calls, helper functions and tests. The file `auth.go`
|
||||
is only generated the first time, and will not be overwritten if it exists.
|
||||
*/
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/jmoiron/sqlx/types"
|
||||
"github.com/pkg/errors"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var _ = chi.URLParam
|
||||
var _ = types.JSONText{}
|
||||
var _ = multipart.FileHeader{}
|
||||
|
||||
// Auth login request parameters
|
||||
type AuthLogin struct {
|
||||
Username string
|
||||
Password string
|
||||
}
|
||||
|
||||
func NewAuthLogin() *AuthLogin {
|
||||
return &AuthLogin{}
|
||||
}
|
||||
|
||||
func (a *AuthLogin) Fill(r *http.Request) error {
|
||||
var err error
|
||||
|
||||
if strings.ToLower(r.Header.Get("content-type")) == "application/json" {
|
||||
err = json.NewDecoder(r.Body).Decode(a)
|
||||
|
||||
switch {
|
||||
case err == io.EOF:
|
||||
err = nil
|
||||
case err != nil:
|
||||
return errors.Wrap(err, "error parsing http request body")
|
||||
}
|
||||
}
|
||||
|
||||
r.ParseForm()
|
||||
get := map[string]string{}
|
||||
post := map[string]string{}
|
||||
urlQuery := r.URL.Query()
|
||||
for name, param := range urlQuery {
|
||||
get[name] = string(param[0])
|
||||
}
|
||||
postVars := r.Form
|
||||
for name, param := range postVars {
|
||||
post[name] = string(param[0])
|
||||
}
|
||||
|
||||
if val, ok := post["username"]; ok {
|
||||
|
||||
a.Username = val
|
||||
}
|
||||
if val, ok := post["password"]; ok {
|
||||
|
||||
a.Password = val
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
var _ RequestFiller = NewAuthLogin()
|
||||
|
||||
// Auth create request parameters
|
||||
type AuthCreate struct {
|
||||
Name string
|
||||
Email string
|
||||
Username string
|
||||
Password string
|
||||
}
|
||||
|
||||
func NewAuthCreate() *AuthCreate {
|
||||
return &AuthCreate{}
|
||||
}
|
||||
|
||||
func (a *AuthCreate) Fill(r *http.Request) error {
|
||||
var err error
|
||||
|
||||
if strings.ToLower(r.Header.Get("content-type")) == "application/json" {
|
||||
err = json.NewDecoder(r.Body).Decode(a)
|
||||
|
||||
switch {
|
||||
case err == io.EOF:
|
||||
err = nil
|
||||
case err != nil:
|
||||
return errors.Wrap(err, "error parsing http request body")
|
||||
}
|
||||
}
|
||||
|
||||
r.ParseForm()
|
||||
get := map[string]string{}
|
||||
post := map[string]string{}
|
||||
urlQuery := r.URL.Query()
|
||||
for name, param := range urlQuery {
|
||||
get[name] = string(param[0])
|
||||
}
|
||||
postVars := r.Form
|
||||
for name, param := range postVars {
|
||||
post[name] = string(param[0])
|
||||
}
|
||||
|
||||
if val, ok := post["name"]; ok {
|
||||
|
||||
a.Name = val
|
||||
}
|
||||
if val, ok := post["email"]; ok {
|
||||
|
||||
a.Email = val
|
||||
}
|
||||
if val, ok := post["username"]; ok {
|
||||
|
||||
a.Username = val
|
||||
}
|
||||
if val, ok := post["password"]; ok {
|
||||
|
||||
a.Password = val
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
var _ RequestFiller = NewAuthCreate()
|
||||
@@ -9,13 +9,6 @@ import (
|
||||
func MountRoutes(jwtAuth auth.TokenEncoder) func(chi.Router) {
|
||||
// Initialize handers & controllers.
|
||||
return func(r chi.Router) {
|
||||
// Cookie expiration in minutes
|
||||
// @todo pull this from auth/jwt config
|
||||
var cookieExp = 3600
|
||||
|
||||
handlers.NewAuthCustom(Auth{}.New(jwtAuth), cookieExp).MountRoutes(r)
|
||||
|
||||
// @todo solve cookie issues (
|
||||
handlers.NewAttachmentDownloadable(Attachment{}.New()).MountRoutes(r)
|
||||
|
||||
// Protect all _private_ routes
|
||||
|
||||
Reference in New Issue
Block a user