mirror of
https://github.com/kataras/iris.git
synced 2025-12-20 03:17:04 +00:00
add a jwt tutorial + go client
This commit is contained in:
9
_examples/auth/jwt/tutorial/domain/model/role.go
Normal file
9
_examples/auth/jwt/tutorial/domain/model/role.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package model
|
||||
|
||||
// Role represents a role.
|
||||
type Role string
|
||||
|
||||
const (
|
||||
// Admin represents the Admin access role.
|
||||
Admin Role = "admin"
|
||||
)
|
||||
10
_examples/auth/jwt/tutorial/domain/model/todo.go
Normal file
10
_examples/auth/jwt/tutorial/domain/model/todo.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package model
|
||||
|
||||
// Todo represents the Todo model.
|
||||
type Todo struct {
|
||||
ID string `json:"id"`
|
||||
UserID string `json:"user_id"`
|
||||
Title string `json:"title"`
|
||||
Body string `json:"body"`
|
||||
CreatedAt int64 `json:"created_at"` // unix seconds.
|
||||
}
|
||||
9
_examples/auth/jwt/tutorial/domain/model/user.go
Normal file
9
_examples/auth/jwt/tutorial/domain/model/user.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package model
|
||||
|
||||
// User represents our User model.
|
||||
type User struct {
|
||||
ID string `json:"id"`
|
||||
Username string `json:"username"`
|
||||
HashedPassword []byte `json:"-"`
|
||||
Roles []Role `json:"roles"`
|
||||
}
|
||||
45
_examples/auth/jwt/tutorial/domain/repository/samples.go
Normal file
45
_examples/auth/jwt/tutorial/domain/repository/samples.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"myapp/domain/model"
|
||||
)
|
||||
|
||||
// GenerateSamples generates data samples.
|
||||
func GenerateSamples(userRepo UserRepository, todoRepo TodoRepository) error {
|
||||
// Create users.
|
||||
for _, username := range []string{"vasiliki", "george", "kwstas"} {
|
||||
// My grandmother.
|
||||
// My young brother.
|
||||
// My youngest brother.
|
||||
password := fmt.Sprintf("%s_pass", username)
|
||||
if _, err := userRepo.Create(username, password); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Create a user with admin role.
|
||||
if _, err := userRepo.Create("admin", "admin", model.Admin); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create two todos per user.
|
||||
users, err := userRepo.GetAll()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i, u := range users {
|
||||
for j := 0; j < 2; j++ {
|
||||
title := fmt.Sprintf("%s todo %d:%d title", u.Username, i, j)
|
||||
body := fmt.Sprintf("%s todo %d:%d body", u.Username, i, j)
|
||||
_, err := todoRepo.Create(u.ID, title, body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
|
||||
"myapp/domain/model"
|
||||
"myapp/util"
|
||||
)
|
||||
|
||||
// ErrNotFound indicates that an entry was not found.
|
||||
// Usage: errors.Is(err, ErrNotFound)
|
||||
var ErrNotFound = errors.New("not found")
|
||||
|
||||
// TodoRepository is responsible for Todo CRUD operations,
|
||||
// however, for the sake of the example we only implement the Create and Read ones.
|
||||
type TodoRepository interface {
|
||||
Create(userID, title, body string) (model.Todo, error)
|
||||
GetByID(id string) (model.Todo, error)
|
||||
GetAll() ([]model.Todo, error)
|
||||
GetAllByUser(userID string) ([]model.Todo, error)
|
||||
}
|
||||
|
||||
var (
|
||||
_ TodoRepository = (*memoryTodoRepository)(nil)
|
||||
)
|
||||
|
||||
type memoryTodoRepository struct {
|
||||
todos []model.Todo // map[string]model.Todo
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// NewMemoryTodoRepository returns the default in-memory todo repository.
|
||||
func NewMemoryTodoRepository() TodoRepository {
|
||||
r := new(memoryTodoRepository)
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *memoryTodoRepository) Create(userID, title, body string) (model.Todo, error) {
|
||||
id, err := util.GenerateUUID()
|
||||
if err != nil {
|
||||
return model.Todo{}, err
|
||||
}
|
||||
|
||||
todo := model.Todo{
|
||||
ID: id,
|
||||
UserID: userID,
|
||||
Title: title,
|
||||
Body: body,
|
||||
CreatedAt: util.Now().Unix(),
|
||||
}
|
||||
|
||||
r.mu.Lock()
|
||||
r.todos = append(r.todos, todo)
|
||||
r.mu.Unlock()
|
||||
|
||||
return todo, nil
|
||||
}
|
||||
|
||||
func (r *memoryTodoRepository) GetByID(id string) (model.Todo, error) {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
|
||||
for _, todo := range r.todos {
|
||||
if todo.ID == id {
|
||||
return todo, nil
|
||||
}
|
||||
}
|
||||
|
||||
return model.Todo{}, ErrNotFound
|
||||
}
|
||||
|
||||
func (r *memoryTodoRepository) GetAll() ([]model.Todo, error) {
|
||||
r.mu.RLock()
|
||||
tmp := make([]model.Todo, len(r.todos))
|
||||
copy(tmp, r.todos)
|
||||
r.mu.RUnlock()
|
||||
return tmp, nil
|
||||
}
|
||||
|
||||
func (r *memoryTodoRepository) GetAllByUser(userID string) ([]model.Todo, error) {
|
||||
// initialize a slice, so we don't have "null" at empty response.
|
||||
todos := make([]model.Todo, 0)
|
||||
|
||||
r.mu.RLock()
|
||||
for _, todo := range r.todos {
|
||||
if todo.UserID == userID {
|
||||
todos = append(todos, todo)
|
||||
}
|
||||
}
|
||||
r.mu.RUnlock()
|
||||
|
||||
return todos, nil
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"myapp/domain/model"
|
||||
"myapp/util"
|
||||
)
|
||||
|
||||
// UserRepository is responsible for User CRUD operations,
|
||||
// however, for the sake of the example we only implement the Read one.
|
||||
type UserRepository interface {
|
||||
Create(username, password string, roles ...model.Role) (model.User, error)
|
||||
// GetByUsernameAndPassword should return a User based on the given input.
|
||||
GetByUsernameAndPassword(username, password string) (model.User, bool)
|
||||
GetAll() ([]model.User, error)
|
||||
}
|
||||
|
||||
var (
|
||||
_ UserRepository = (*memoryUserRepository)(nil)
|
||||
)
|
||||
|
||||
type memoryUserRepository struct {
|
||||
// Users represents a user database.
|
||||
// For the sake of the tutorial we use a simple slice of users.
|
||||
users []model.User
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// NewMemoryUserRepository returns the default in-memory user repository.
|
||||
func NewMemoryUserRepository() UserRepository {
|
||||
r := new(memoryUserRepository)
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *memoryUserRepository) Create(username, password string, roles ...model.Role) (model.User, error) {
|
||||
id, err := util.GenerateUUID()
|
||||
if err != nil {
|
||||
return model.User{}, err
|
||||
}
|
||||
|
||||
hashedPassword, err := util.GeneratePassword(password)
|
||||
if err != nil {
|
||||
return model.User{}, err
|
||||
}
|
||||
|
||||
user := model.User{
|
||||
ID: id,
|
||||
Username: username,
|
||||
HashedPassword: hashedPassword,
|
||||
Roles: roles,
|
||||
}
|
||||
|
||||
r.mu.Lock()
|
||||
r.users = append(r.users, user)
|
||||
r.mu.Unlock()
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// GetByUsernameAndPassword returns a user from the storage based on the given "username" and "password".
|
||||
func (r *memoryUserRepository) GetByUsernameAndPassword(username, password string) (model.User, bool) {
|
||||
for _, u := range r.users { // our example uses a static slice.
|
||||
if u.Username == username {
|
||||
// we compare the user input and the stored hashed password.
|
||||
ok := util.ValidatePassword(password, u.HashedPassword)
|
||||
if ok {
|
||||
return u, true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return model.User{}, false
|
||||
}
|
||||
|
||||
func (r *memoryUserRepository) GetAll() ([]model.User, error) {
|
||||
r.mu.RLock()
|
||||
tmp := make([]model.User, len(r.users))
|
||||
copy(tmp, r.users)
|
||||
r.mu.RUnlock()
|
||||
return tmp, nil
|
||||
}
|
||||
Reference in New Issue
Block a user