@AFI19

Как правильно сделать авторизацию на Casbin и Go?

Есть API который принимает запросы от пользователей 2 типов.
1. Хочу ограничить доступ к API по ролям, что бы они могли посещать только страницы предназначеные для них.

Сделал middleware:
1. Он проверяет jwt токен.
2. В Casbin сверяет роль из payload и путь.
3. Получает пользователя.
func (c *MiddlewareCollection) UserAuthorization(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		header := r.Header.Get(authorizationHeader)

		// . . .

		payload, err := (*c.js).Validate(headerParts[1])
		if err != nil {
			controllers.Unauthorized(w, err)
			return
		}

		res, err := (*c.e).Enforce(
			strconv.Itoa(payload.Role),
			r.URL.Path,
			r.Method,
		)

		if !res {
			controllers.Unauthorized(w, errors.New("access denied"))
			return
		}

		user, err := (*c.us).FindById(payload.UserId)
		if err != nil {
			// . . .
		}

		if !user.DeletedAt.IsZero() {
			controllers.Unauthorized(w, err)
			return
		}

		ctx := context.WithValue(r.Context(), "user", user)

		next.ServeHTTP(w, r.WithContext(ctx))
	})
}

Модель Casbin
[request_definition]
r = sub, obj, act

[policy_definition]
p = sub, obj, act

[policy_effect]
e = some(where (p.eft == allow))

[matchers]
m = r.sub == p.sub && keyMatch2(r.obj, p.obj) && regexMatch(r.act, p.act)

Policy
p, client, /v1/users/:id/reset-password, GET
p, client, /v1/users/:id, PUT
p,specialist,/v1/specialist/:id, PUT


2. Пользователь может изменять только свои данные. Id пользователя указывается в url /v1/users/:id
Мой вариант, это создать новый middleware для таких маршрутов (с id в url) и взять id из url, и сравнить с payload.UserId.

3. Пользователь может редактировать сущности которые он создал, статья, комментарий и тд.
Здесь я планирую делать проверку в сервесе, получить статью и проверить её OwnerId* с Id пользователя.

Как правильно создавать авторизации такого рода?
Где должна происходить проверка?
Буду благодарен за реальные примеры.
  • Вопрос задан
  • 232 просмотра
Пригласить эксперта
Ответы на вопрос 1
Maksclub
@Maksclub
maksfedorov.ru
Аутентификацию и авторизацию сделать отдельными посредниками

Для авторизации можете создать дополнительную абстракцию, назовем ее Voter
Делаете посредник, который принимает voters и можете навешивать их на разные роуты

Что-то типа (на примере др роутера):

router.Group(func(router chi.Router) {
     // вся группа роутов внутри только для аутентифицированных
     // тут же можно обогатить контекст юзером
     router.Use(oauth.Authorize().Handler)

     // роут только для админов
     router.
         With(perm.WithVoters(perm.ManagerVoter(perm.AdminVoter())).
         Get("/admin", AdminHandler())

     // только для manager И owner одновременно
     router.Group(func(router chi.Router) {
          router.Use(perm.WithVoters(
               perm.ManagerVoter(),
               campaign.OwnerVoter("id"),
               // ... любой др voter
          ).Handler)

          router.Get("/campaign/{id}", GetCampaign())
          router.Patch("/campaign/{id}", UpdateCampaign())
    })
}


Получается гибко и можно управлять ролями для разных роутов
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы