add authenticated-controller example - proof of concept

Former-commit-id: 7a15da2bd966f2a699337cd494af8b5d1877b11d
This commit is contained in:
Gerasimos (Makis) Maropoulos 2020-05-06 04:45:12 +03:00
parent 47075a82e9
commit ffc493a0b9
9 changed files with 200 additions and 50 deletions

View File

@ -170,6 +170,7 @@
* [Singleton](mvc/singleton)
* [Regexp](mvc/regexp/main.go)
* [Session Controller](mvc/session-controller/main.go)
* [Authenticated Controller](mvc/authenticated-controller/main.go)
* [Websocket Controller](mvc/websocket)
* [Register Middleware](mvc/middleware)
* Object-Relational Mapping

View File

@ -0,0 +1,104 @@
// Package main shows how to use a dependency to check if a user is logged in
// using a special custom Go type `Authenticated`, which when,
// present on a controller's method or a field then
// it limits the visibility to "authenticated" users only.
//
// The same result could be done through a middleware as well, however using a static type
// any person reads your code can see that an "X" controller or method
// should only be used by "authenticated" users.
package main
import (
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/mvc"
"github.com/kataras/iris/v12/sessions"
)
func main() {
app := iris.New()
app.Logger().SetLevel("debug")
sess := sessions.New(sessions.Config{
Cookie: "myapp_session_id",
AllowReclaim: true,
})
app.Use(sess.Handler())
userRouter := app.Party("/user")
{
// Initialize a new MVC application on top of the "userRouter".
userApp := mvc.New(userRouter)
// Register Dependencies.
userApp.Register(authDependency)
// Register Controllers.
userApp.Handle(new(MeController))
userApp.Handle(new(UserController))
}
// Open a client, e.g. Postman and visit the below endpoints.
// GET: http://localhost:8080/user
// POST: http://localhost:8080/user/login
// GET: http://localhost:8080/user
// GET: http://localhost:8080/user/me
// POST: http://localhost:8080/user/logout
app.Listen(":8080")
}
// Authenticated is a custom type used as "annotation" for resources that requires authentication,
// its value should be the logged user ID.
type Authenticated uint64
func authDependency(ctx iris.Context, session *sessions.Session) Authenticated {
userID := session.GetUint64Default("user_id", 0)
if userID == 0 {
// If execution was stopped
// any controller's method will not be executed at all.
ctx.StopWithStatus(iris.StatusUnauthorized)
return 0
}
return Authenticated(userID)
}
// UserController serves the "public" User API.
type UserController struct {
Session *sessions.Session
}
// PostLogin serves
// POST: /user/login
func (c *UserController) PostLogin() mvc.Response {
c.Session.Set("user_id", 1)
// Redirect (you can still use the Context.Redirect if you want so).
return mvc.Response{
Path: "/user",
Code: iris.StatusFound,
}
}
// PostLogout serves
// POST: /user/logout
func (c *UserController) PostLogout(ctx iris.Context) {
c.Session.Man.Destroy(ctx)
}
// GetMe showcases that the same type can be used inside controller's method too,
// a second controller like `MeController` is not required.
// GET: user/me
func (c *UserController) GetMe(_ Authenticated) string {
return `UserController.GetMe: The Authenticated type
can be used to secure a controller's method too.`
}
// MeController provides the logged user's available actions.
type MeController struct {
CurrentUserID Authenticated
}
// Get returns a message for the shake of the example.
// GET: /user
func (c *MeController) Get() string {
return "This will be executed only when the user is logged in"
}

View File

@ -61,7 +61,7 @@ func main() {
}
db.AutoMigrate(&User{}) // create table: // AutoMigrate run auto migration for given models, will only add missing fields, won't delete/change current data
app.Post("/post_user", func(context iris.Context) {
app.Post("/post_user", func(ctx iris.Context) {
var user User
user = User{
Username: "gorm",
@ -71,41 +71,41 @@ func main() {
}
if err := db.FirstOrCreate(&user); err == nil {
app.Logger().Fatalf("created one record failed: %s", err.Error)
context.JSON(iris.Map{
ctx.JSON(iris.Map{
"code": http.StatusBadRequest,
"error": err.Error,
})
return
}
context.JSON(
ctx.JSON(
iris.Map{
"code": http.StatusOK,
"data": user.Serializer(),
})
})
app.Get("/get_user/{id:uint}", func(context iris.Context) {
app.Get("/get_user/{id:uint}", func(ctx iris.Context) {
var user User
id, _ := context.Params().GetUint("id")
id, _ := ctx.Params().GetUint("id")
app.Logger().Println(id)
if err := db.Where("id = ?", int(id)).First(&user).Error; err != nil {
app.Logger().Fatalf("find one record failed: %t", err == nil)
context.JSON(iris.Map{
ctx.JSON(iris.Map{
"code": http.StatusBadRequest,
"error": err.Error,
})
return
}
context.JSON(iris.Map{
ctx.JSON(iris.Map{
"code": http.StatusOK,
"data": user.Serializer(),
})
})
app.Delete("/delete_user/{id:uint}", func(context iris.Context) {
id, _ := context.Params().GetUint("id")
app.Delete("/delete_user/{id:uint}", func(ctx iris.Context) {
id, _ := ctx.Params().GetUint("id")
if id == 0 {
context.JSON(iris.Map{
ctx.JSON(iris.Map{
"code": http.StatusOK,
"detail": "query param id should not be nil",
})
@ -114,23 +114,23 @@ func main() {
var user User
if err := db.Where("id = ?", id).First(&user).Error; err != nil {
app.Logger().Fatalf("record not found")
context.JSON(iris.Map{
ctx.JSON(iris.Map{
"code": http.StatusOK,
"detail": err.Error,
})
return
}
db.Delete(&user)
context.JSON(iris.Map{
ctx.JSON(iris.Map{
"code": http.StatusOK,
"data": user.Serializer(),
})
})
app.Patch("/patch_user/{id:uint}", func(context iris.Context) {
id, _ := context.Params().GetUint("id")
app.Patch("/patch_user/{id:uint}", func(ctx iris.Context) {
id, _ := ctx.Params().GetUint("id")
if id == 0 {
context.JSON(iris.Map{
ctx.JSON(iris.Map{
"code": http.StatusOK,
"detail": "query param id should not be nil",
})
@ -140,7 +140,7 @@ func main() {
tx := db.Begin()
if err := tx.Where("id = ?", id).First(&user).Error; err != nil {
app.Logger().Fatalf("record not found")
context.JSON(iris.Map{
ctx.JSON(iris.Map{
"code": http.StatusOK,
"detail": err.Error,
})
@ -148,19 +148,19 @@ func main() {
}
var body patchParam
context.ReadJSON(&body)
ctx.ReadJSON(&body)
app.Logger().Println(body)
if err := tx.Model(&user).Updates(map[string]interface{}{"username": body.Data.UserName, "password": body.Data.Password}).Error; err != nil {
app.Logger().Fatalf("update record failed")
tx.Rollback()
context.JSON(iris.Map{
ctx.JSON(iris.Map{
"code": http.StatusBadRequest,
"error": err.Error,
})
return
}
tx.Commit()
context.JSON(iris.Map{
ctx.JSON(iris.Map{
"code": http.StatusOK,
"data": user.Serializer(),
})

View File

@ -14,9 +14,9 @@ func TestRouteRegisterRuleExample(t *testing.T) {
for _, method := range router.AllMethods {
tt := e.Request(method, "/").Expect().Status(httptest.StatusOK).Body()
if method == "GET" {
tt.Equal("From GET: github.com/kataras/iris/v12/_examples/routing/route-register-rule.getHandler")
tt.Equal("From GET: iris/_examples/routing/route-register-rule.getHandler")
} else {
tt.Equal("From " + method + ": github.com/kataras/iris/v12/_examples/routing/route-register-rule.anyHandler")
tt.Equal("From " + method + ": iris/_examples/routing/route-register-rule.anyHandler")
}
}
}

View File

@ -1,5 +1,3 @@
// +build go1.9
package iris
import (

View File

@ -1,5 +1,3 @@
// +build go1.9
package httptest
import "github.com/gavv/httpexpect"

View File

@ -1,6 +1,7 @@
package sessions
import (
"math"
"reflect"
"strconv"
"sync"
@ -225,20 +226,22 @@ func newErrEntryNotFound(key string, kind reflect.Kind, value interface{}) *ErrE
func (s *Session) GetInt(key string) (int, error) {
v := s.Get(key)
if vint, ok := v.(int); ok {
return vint, nil
}
if v != nil {
if vint, ok := v.(int); ok {
return vint, nil
}
if vfloat64, ok := v.(float64); ok {
return int(vfloat64), nil
}
if vfloat64, ok := v.(float64); ok {
return int(vfloat64), nil
}
if vint64, ok := v.(int64); ok {
return int(vint64), nil
}
if vint64, ok := v.(int64); ok {
return int(vint64), nil
}
if vstring, sok := v.(string); sok {
return strconv.Atoi(vstring)
if vstring, sok := v.(string); sok {
return strconv.Atoi(vstring)
}
}
return -1, newErrEntryNotFound(key, reflect.Int, v)
@ -277,21 +280,22 @@ func (s *Session) Decrement(key string, n int) (newValue int) {
// if key doesn't exist then it returns -1 and a non-nil error.
func (s *Session) GetInt64(key string) (int64, error) {
v := s.Get(key)
if v != nil {
if vint64, ok := v.(int64); ok {
return vint64, nil
}
if vint64, ok := v.(int64); ok {
return vint64, nil
}
if vfloat64, ok := v.(float64); ok {
return int64(vfloat64), nil
}
if vfloat64, ok := v.(float64); ok {
return int64(vfloat64), nil
}
if vint, ok := v.(int); ok {
return int64(vint), nil
}
if vint, ok := v.(int); ok {
return int64(vint), nil
}
if vstring, sok := v.(string); sok {
return strconv.ParseInt(vstring, 10, 64)
if vstring, sok := v.(string); sok {
return strconv.ParseInt(vstring, 10, 64)
}
}
return -1, newErrEntryNotFound(key, reflect.Int64, v)
@ -307,6 +311,49 @@ func (s *Session) GetInt64Default(key string, defaultValue int64) int64 {
return defaultValue
}
// GetUint64 same as `Get` but returns as uint64,
// if key doesn't exist then it returns 0 and a non-nil error.
func (s *Session) GetUint64(key string) (uint64, error) {
v := s.Get(key)
if v != nil {
switch vv := v.(type) {
case string:
val, err := strconv.ParseUint(vv, 10, 64)
if err != nil {
return 0, err
}
if val > math.MaxUint64 {
break
}
return uint64(val), nil
case uint8:
return uint64(vv), nil
case uint16:
return uint64(vv), nil
case uint32:
return uint64(vv), nil
case uint64:
return vv, nil
case int64:
return uint64(vv), nil
case int:
return uint64(vv), nil
}
}
return 0, newErrEntryNotFound(key, reflect.Uint64, v)
}
// GetUint64Default same as `Get` but returns as uint64,
// if key doesn't exist it returns the "defaultValue".
func (s *Session) GetUint64Default(key string, defaultValue uint64) uint64 {
if v, err := s.GetUint64(key); err == nil {
return v
}
return defaultValue
}
// GetFloat32 same as `Get` but returns its float32 representation,
// if key doesn't exist then it returns -1 and a non-nil error.
func (s *Session) GetFloat32(key string) (float32, error) {

View File

@ -7,6 +7,10 @@ import (
"github.com/kataras/iris/v12/context"
)
func init() {
context.SetHandlerName("iris/sessions.*Handler", "iris.session")
}
// A Sessions manager should be responsible to Start a sesion, based
// on a Context, which should return
// a compatible Session interface, type. If the external session manager

View File

@ -1,5 +1,3 @@
// +build go1.9
package websocket
import (