1
0
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:
Gerasimos (Makis) Maropoulos
2020-11-04 21:12:13 +02:00
parent ed38047385
commit 579c3878f0
22 changed files with 818 additions and 163 deletions

View File

@@ -0,0 +1,9 @@
package model
// Role represents a role.
type Role string
const (
// Admin represents the Admin access role.
Admin Role = "admin"
)

View 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.
}

View 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"`
}

View 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
}

View File

@@ -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
}

View File

@@ -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
}