2018-05-14 16:26:38 +00:00
|
|
|
package http
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"context"
|
|
|
|
"encoding/json"
|
2018-05-22 22:05:17 +00:00
|
|
|
"errors"
|
2018-09-10 20:56:11 +00:00
|
|
|
"fmt"
|
2018-05-14 16:26:38 +00:00
|
|
|
"net/http"
|
2018-05-16 18:59:35 +00:00
|
|
|
"path"
|
2018-05-14 16:26:38 +00:00
|
|
|
|
|
|
|
"go.uber.org/zap"
|
|
|
|
|
2019-01-08 00:37:16 +00:00
|
|
|
platform "github.com/influxdata/influxdb"
|
|
|
|
platcontext "github.com/influxdata/influxdb/context"
|
2018-05-14 16:26:38 +00:00
|
|
|
"github.com/julienschmidt/httprouter"
|
|
|
|
)
|
|
|
|
|
|
|
|
// AuthorizationHandler represents an HTTP API handler for authorizations.
|
|
|
|
type AuthorizationHandler struct {
|
|
|
|
*httprouter.Router
|
|
|
|
Logger *zap.Logger
|
|
|
|
|
2018-12-28 23:02:19 +00:00
|
|
|
OrganizationService platform.OrganizationService
|
2018-12-21 16:14:55 +00:00
|
|
|
UserService platform.UserService
|
2018-12-28 23:02:19 +00:00
|
|
|
AuthorizationService platform.AuthorizationService
|
|
|
|
LookupService platform.LookupService
|
2018-05-14 16:26:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewAuthorizationHandler returns a new instance of AuthorizationHandler.
|
2018-12-21 16:14:55 +00:00
|
|
|
func NewAuthorizationHandler(userService platform.UserService) *AuthorizationHandler {
|
2018-05-14 16:26:38 +00:00
|
|
|
h := &AuthorizationHandler{
|
2018-12-21 16:14:55 +00:00
|
|
|
Router: NewRouter(),
|
|
|
|
Logger: zap.NewNop(),
|
|
|
|
UserService: userService,
|
2018-05-14 16:26:38 +00:00
|
|
|
}
|
|
|
|
|
2018-09-26 08:49:19 +00:00
|
|
|
h.HandlerFunc("POST", "/api/v2/authorizations", h.handlePostAuthorization)
|
|
|
|
h.HandlerFunc("GET", "/api/v2/authorizations", h.handleGetAuthorizations)
|
|
|
|
h.HandlerFunc("GET", "/api/v2/authorizations/:id", h.handleGetAuthorization)
|
|
|
|
h.HandlerFunc("PATCH", "/api/v2/authorizations/:id", h.handleSetAuthorizationStatus)
|
|
|
|
h.HandlerFunc("DELETE", "/api/v2/authorizations/:id", h.handleDeleteAuthorization)
|
2018-05-14 16:26:38 +00:00
|
|
|
return h
|
|
|
|
}
|
|
|
|
|
2018-09-10 20:56:11 +00:00
|
|
|
type authResponse struct {
|
2018-12-28 23:02:19 +00:00
|
|
|
ID platform.ID `json:"id"`
|
|
|
|
Token string `json:"token"`
|
|
|
|
Status platform.Status `json:"status"`
|
|
|
|
Description string `json:"description"`
|
|
|
|
OrgID platform.ID `json:"orgID"`
|
|
|
|
Org string `json:"org"`
|
|
|
|
UserID platform.ID `json:"userID"`
|
|
|
|
User string `json:"user"`
|
|
|
|
Permissions []permissionResponse `json:"permissions"`
|
|
|
|
Links map[string]string `json:"links"`
|
2018-09-10 20:56:11 +00:00
|
|
|
}
|
|
|
|
|
2018-12-28 23:02:19 +00:00
|
|
|
func newAuthResponse(a *platform.Authorization, org *platform.Organization, user *platform.User, ps []permissionResponse) *authResponse {
|
|
|
|
res := &authResponse{
|
|
|
|
ID: a.ID,
|
|
|
|
Token: a.Token,
|
|
|
|
Status: a.Status,
|
|
|
|
Description: a.Description,
|
|
|
|
OrgID: a.OrgID,
|
|
|
|
UserID: a.UserID,
|
|
|
|
User: user.Name,
|
|
|
|
Org: org.Name,
|
|
|
|
Permissions: ps,
|
2018-09-10 20:56:11 +00:00
|
|
|
Links: map[string]string{
|
2018-09-26 08:49:19 +00:00
|
|
|
"self": fmt.Sprintf("/api/v2/authorizations/%s", a.ID),
|
|
|
|
"user": fmt.Sprintf("/api/v2/users/%s", a.UserID),
|
2018-09-10 20:56:11 +00:00
|
|
|
},
|
|
|
|
}
|
2018-12-28 23:02:19 +00:00
|
|
|
return res
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *authResponse) toPlatform() *platform.Authorization {
|
|
|
|
res := &platform.Authorization{
|
|
|
|
ID: a.ID,
|
|
|
|
Token: a.Token,
|
|
|
|
Status: a.Status,
|
|
|
|
Description: a.Description,
|
|
|
|
OrgID: a.OrgID,
|
|
|
|
UserID: a.UserID,
|
|
|
|
}
|
|
|
|
for _, p := range a.Permissions {
|
|
|
|
res.Permissions = append(res.Permissions, p.Permission)
|
|
|
|
}
|
|
|
|
return res
|
|
|
|
}
|
|
|
|
|
|
|
|
type permissionResponse struct {
|
|
|
|
platform.Permission
|
|
|
|
Name string `json:"name,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func newPermissionsResponse(ctx context.Context, ps []platform.Permission, svc platform.LookupService) ([]permissionResponse, error) {
|
|
|
|
res := make([]permissionResponse, len(ps))
|
|
|
|
for i, p := range ps {
|
|
|
|
res[i] = permissionResponse{
|
|
|
|
Permission: p,
|
|
|
|
}
|
|
|
|
|
|
|
|
if p.ID != nil {
|
|
|
|
name, err := svc.Name(ctx, p.Resource, *p.ID)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
res[i].Name = name
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return res, nil
|
2018-09-10 20:56:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type authsResponse struct {
|
|
|
|
Links map[string]string `json:"links"`
|
2018-12-17 03:51:21 +00:00
|
|
|
Auths []*authResponse `json:"authorizations"`
|
2018-09-10 20:56:11 +00:00
|
|
|
}
|
|
|
|
|
2018-12-28 23:02:19 +00:00
|
|
|
func newAuthsResponse(as []*authResponse) *authsResponse {
|
2018-09-10 20:56:11 +00:00
|
|
|
return &authsResponse{
|
|
|
|
// TODO(desa): update links to include paging and filter information
|
|
|
|
Links: map[string]string{
|
2018-09-26 08:49:19 +00:00
|
|
|
"self": "/api/v2/authorizations",
|
2018-09-10 20:56:11 +00:00
|
|
|
},
|
2018-12-28 23:02:19 +00:00
|
|
|
Auths: as,
|
2018-09-10 20:56:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-26 08:49:19 +00:00
|
|
|
// handlePostAuthorization is the HTTP handler for the POST /api/v2/authorizations route.
|
2018-05-14 16:26:38 +00:00
|
|
|
func (h *AuthorizationHandler) handlePostAuthorization(w http.ResponseWriter, r *http.Request) {
|
|
|
|
ctx := r.Context()
|
|
|
|
|
|
|
|
req, err := decodePostAuthorizationRequest(ctx, r)
|
|
|
|
if err != nil {
|
2018-06-28 19:32:16 +00:00
|
|
|
EncodeError(ctx, err, w)
|
2018-05-14 16:26:38 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-12-28 23:02:19 +00:00
|
|
|
user, err := getAuthorizedUser(r, h.UserService)
|
|
|
|
if err != nil {
|
|
|
|
EncodeError(ctx, platform.ErrUnableToCreateToken, w)
|
|
|
|
return
|
2018-12-21 16:14:55 +00:00
|
|
|
}
|
2018-12-28 23:02:19 +00:00
|
|
|
|
|
|
|
auth := req.toPlatform(user.ID)
|
|
|
|
|
|
|
|
org, err := h.OrganizationService.FindOrganizationByID(ctx, auth.OrgID)
|
|
|
|
if err != nil {
|
|
|
|
EncodeError(ctx, platform.ErrUnableToCreateToken, w)
|
2018-12-21 16:14:55 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-12-28 23:02:19 +00:00
|
|
|
if err := h.AuthorizationService.CreateAuthorization(ctx, auth); err != nil {
|
|
|
|
EncodeError(ctx, err, w)
|
|
|
|
return
|
|
|
|
}
|
2018-05-14 16:26:38 +00:00
|
|
|
|
2018-12-28 23:02:19 +00:00
|
|
|
perms, err := newPermissionsResponse(ctx, auth.Permissions, h.LookupService)
|
|
|
|
if err != nil {
|
2018-06-28 19:32:16 +00:00
|
|
|
EncodeError(ctx, err, w)
|
2018-05-14 16:26:38 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-12-28 23:02:19 +00:00
|
|
|
if err := encodeResponse(ctx, w, http.StatusCreated, newAuthResponse(auth, org, user, perms)); err != nil {
|
|
|
|
EncodeError(ctx, err, w)
|
2018-05-14 16:26:38 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type postAuthorizationRequest struct {
|
2018-12-28 23:02:19 +00:00
|
|
|
Status platform.Status `json:"status"`
|
|
|
|
OrgID platform.ID `json:"orgID"`
|
|
|
|
Description string `json:"description"`
|
|
|
|
Permissions []platform.Permission `json:"permissions"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *postAuthorizationRequest) toPlatform(userID platform.ID) *platform.Authorization {
|
|
|
|
return &platform.Authorization{
|
|
|
|
OrgID: p.OrgID,
|
|
|
|
Status: p.Status,
|
|
|
|
Description: p.Description,
|
|
|
|
Permissions: p.Permissions,
|
|
|
|
UserID: userID,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func newPostAuthorizationRequest(a *platform.Authorization) (*postAuthorizationRequest, error) {
|
|
|
|
res := &postAuthorizationRequest{
|
|
|
|
OrgID: a.OrgID,
|
|
|
|
Description: a.Description,
|
|
|
|
Permissions: a.Permissions,
|
|
|
|
Status: a.Status,
|
|
|
|
}
|
|
|
|
|
|
|
|
res.SetDefaults()
|
|
|
|
|
|
|
|
return res, res.Validate()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *postAuthorizationRequest) SetDefaults() {
|
|
|
|
if p.Status == "" {
|
|
|
|
p.Status = platform.Active
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *postAuthorizationRequest) Validate() error {
|
|
|
|
if len(p.Permissions) == 0 {
|
|
|
|
return &platform.Error{
|
|
|
|
Code: platform.EInvalid,
|
|
|
|
Msg: "authorization must include permissions",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, perm := range p.Permissions {
|
|
|
|
if err := perm.Valid(); err != nil {
|
|
|
|
return &platform.Error{
|
|
|
|
Err: err,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if !p.OrgID.Valid() {
|
|
|
|
return &platform.Error{
|
|
|
|
Err: platform.ErrInvalidID,
|
|
|
|
Code: platform.EInvalid,
|
|
|
|
Msg: "org id required",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if p.Status == "" {
|
|
|
|
p.Status = platform.Active
|
|
|
|
}
|
|
|
|
|
|
|
|
err := p.Status.Valid()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
2018-05-14 16:26:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func decodePostAuthorizationRequest(ctx context.Context, r *http.Request) (*postAuthorizationRequest, error) {
|
2018-12-28 23:02:19 +00:00
|
|
|
a := &postAuthorizationRequest{}
|
2018-05-14 16:26:38 +00:00
|
|
|
if err := json.NewDecoder(r.Body).Decode(a); err != nil {
|
2018-12-28 23:02:19 +00:00
|
|
|
return nil, &platform.Error{
|
|
|
|
Code: platform.EInvalid,
|
|
|
|
Msg: "invalid json structure",
|
|
|
|
Err: err,
|
|
|
|
}
|
2018-05-14 16:26:38 +00:00
|
|
|
}
|
|
|
|
|
2018-12-28 23:02:19 +00:00
|
|
|
a.SetDefaults()
|
|
|
|
|
|
|
|
return a, a.Validate()
|
2018-05-14 16:26:38 +00:00
|
|
|
}
|
|
|
|
|
2018-09-26 08:49:19 +00:00
|
|
|
// handleGetAuthorizations is the HTTP handler for the GET /api/v2/authorizations route.
|
2018-05-14 16:26:38 +00:00
|
|
|
func (h *AuthorizationHandler) handleGetAuthorizations(w http.ResponseWriter, r *http.Request) {
|
|
|
|
ctx := r.Context()
|
|
|
|
|
|
|
|
req, err := decodeGetAuthorizationsRequest(ctx, r)
|
|
|
|
if err != nil {
|
|
|
|
h.Logger.Info("failed to decode request", zap.String("handler", "getAuthorizations"), zap.Error(err))
|
2018-06-28 19:32:16 +00:00
|
|
|
EncodeError(ctx, err, w)
|
2018-05-14 16:26:38 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-09-10 20:56:11 +00:00
|
|
|
opts := platform.FindOptions{}
|
|
|
|
as, _, err := h.AuthorizationService.FindAuthorizations(ctx, req.filter, opts)
|
2018-05-14 16:26:38 +00:00
|
|
|
if err != nil {
|
2018-06-28 19:32:16 +00:00
|
|
|
EncodeError(ctx, err, w)
|
2018-05-14 16:26:38 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-12-28 23:02:19 +00:00
|
|
|
auths := make([]*authResponse, len(as))
|
|
|
|
for i, a := range as {
|
|
|
|
o, err := h.OrganizationService.FindOrganizationByID(ctx, a.OrgID)
|
|
|
|
if err != nil {
|
|
|
|
EncodeError(ctx, err, w)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
u, err := h.UserService.FindUserByID(ctx, a.UserID)
|
|
|
|
if err != nil {
|
|
|
|
EncodeError(ctx, err, w)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
ps, err := newPermissionsResponse(ctx, a.Permissions, h.LookupService)
|
|
|
|
if err != nil {
|
|
|
|
EncodeError(ctx, err, w)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
auths[i] = newAuthResponse(a, o, u, ps)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := encodeResponse(ctx, w, http.StatusOK, newAuthsResponse(auths)); err != nil {
|
|
|
|
EncodeError(ctx, err, w)
|
2018-05-14 16:26:38 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type getAuthorizationsRequest struct {
|
|
|
|
filter platform.AuthorizationFilter
|
|
|
|
}
|
|
|
|
|
|
|
|
func decodeGetAuthorizationsRequest(ctx context.Context, r *http.Request) (*getAuthorizationsRequest, error) {
|
|
|
|
qp := r.URL.Query()
|
|
|
|
|
|
|
|
req := &getAuthorizationsRequest{}
|
|
|
|
|
2018-05-16 18:59:35 +00:00
|
|
|
userID := qp.Get("userID")
|
2018-05-14 16:26:38 +00:00
|
|
|
if userID != "" {
|
2018-07-20 10:24:07 +00:00
|
|
|
id, err := platform.IDFromString(userID)
|
|
|
|
if err != nil {
|
2018-05-14 16:26:38 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
2018-07-20 10:24:07 +00:00
|
|
|
req.filter.UserID = id
|
2018-05-16 18:59:35 +00:00
|
|
|
}
|
2018-05-14 16:26:38 +00:00
|
|
|
|
2018-05-16 18:59:35 +00:00
|
|
|
user := qp.Get("user")
|
|
|
|
if user != "" {
|
|
|
|
req.filter.User = &user
|
|
|
|
}
|
|
|
|
|
|
|
|
authID := qp.Get("id")
|
|
|
|
if authID != "" {
|
2018-07-20 10:24:07 +00:00
|
|
|
id, err := platform.IDFromString(authID)
|
|
|
|
if err != nil {
|
2018-05-16 18:59:35 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
2018-07-20 10:24:07 +00:00
|
|
|
req.filter.ID = id
|
2018-05-14 16:26:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return req, nil
|
|
|
|
}
|
|
|
|
|
2018-09-26 08:49:19 +00:00
|
|
|
// handleGetAuthorization is the HTTP handler for the GET /api/v2/authorizations/:id route.
|
2018-05-16 18:59:35 +00:00
|
|
|
func (h *AuthorizationHandler) handleGetAuthorization(w http.ResponseWriter, r *http.Request) {
|
|
|
|
ctx := r.Context()
|
|
|
|
|
|
|
|
req, err := decodeGetAuthorizationRequest(ctx, r)
|
|
|
|
if err != nil {
|
|
|
|
h.Logger.Info("failed to decode request", zap.String("handler", "getAuthorization"), zap.Error(err))
|
2018-06-28 19:32:16 +00:00
|
|
|
EncodeError(ctx, err, w)
|
2018-05-16 18:59:35 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
a, err := h.AuthorizationService.FindAuthorizationByID(ctx, req.ID)
|
|
|
|
if err != nil {
|
|
|
|
// Don't log here, it should already be handled by the service
|
2018-06-28 19:32:16 +00:00
|
|
|
EncodeError(ctx, err, w)
|
2018-05-16 18:59:35 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-12-28 23:02:19 +00:00
|
|
|
o, err := h.OrganizationService.FindOrganizationByID(ctx, a.OrgID)
|
|
|
|
if err != nil {
|
|
|
|
EncodeError(ctx, err, w)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
u, err := h.UserService.FindUserByID(ctx, a.UserID)
|
|
|
|
if err != nil {
|
|
|
|
EncodeError(ctx, err, w)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
ps, err := newPermissionsResponse(ctx, a.Permissions, h.LookupService)
|
|
|
|
if err != nil {
|
|
|
|
EncodeError(ctx, err, w)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := encodeResponse(ctx, w, http.StatusOK, newAuthResponse(a, o, u, ps)); err != nil {
|
|
|
|
EncodeError(ctx, err, w)
|
2018-05-16 18:59:35 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type getAuthorizationRequest struct {
|
|
|
|
ID platform.ID
|
|
|
|
}
|
|
|
|
|
|
|
|
func decodeGetAuthorizationRequest(ctx context.Context, r *http.Request) (*getAuthorizationRequest, error) {
|
|
|
|
params := httprouter.ParamsFromContext(ctx)
|
|
|
|
id := params.ByName("id")
|
|
|
|
if id == "" {
|
2019-01-04 17:21:34 +00:00
|
|
|
return nil, &platform.Error{
|
|
|
|
Code: platform.EInvalid,
|
|
|
|
Msg: "url missing id",
|
|
|
|
}
|
2018-05-16 18:59:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var i platform.ID
|
|
|
|
if err := i.DecodeFromString(id); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &getAuthorizationRequest{
|
|
|
|
ID: i,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2018-09-26 08:49:19 +00:00
|
|
|
// handleSetAuthorizationStatus is the HTTP handler for the PATCH /api/v2/authorizations/:id route that updates the authorization's status.
|
2018-08-28 17:58:38 +00:00
|
|
|
func (h *AuthorizationHandler) handleSetAuthorizationStatus(w http.ResponseWriter, r *http.Request) {
|
2018-08-27 19:18:11 +00:00
|
|
|
ctx := r.Context()
|
|
|
|
|
2018-08-28 17:58:38 +00:00
|
|
|
req, err := decodeSetAuthorizationStatusRequest(ctx, r)
|
2018-08-27 19:18:11 +00:00
|
|
|
if err != nil {
|
|
|
|
h.Logger.Info("failed to decode request", zap.String("handler", "updateAuthorization"), zap.Error(err))
|
|
|
|
EncodeError(ctx, err, w)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
a, err := h.AuthorizationService.FindAuthorizationByID(ctx, req.ID)
|
|
|
|
if err != nil {
|
|
|
|
EncodeError(ctx, err, w)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-08-28 17:58:38 +00:00
|
|
|
if req.Status != a.Status {
|
|
|
|
a.Status = req.Status
|
|
|
|
if err := h.AuthorizationService.SetAuthorizationStatus(ctx, a.ID, a.Status); err != nil {
|
2018-08-27 19:18:11 +00:00
|
|
|
EncodeError(ctx, err, w)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-28 23:02:19 +00:00
|
|
|
o, err := h.OrganizationService.FindOrganizationByID(ctx, a.OrgID)
|
|
|
|
if err != nil {
|
|
|
|
EncodeError(ctx, err, w)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
u, err := h.UserService.FindUserByID(ctx, a.UserID)
|
|
|
|
if err != nil {
|
|
|
|
EncodeError(ctx, err, w)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
ps, err := newPermissionsResponse(ctx, a.Permissions, h.LookupService)
|
|
|
|
if err != nil {
|
|
|
|
EncodeError(ctx, err, w)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := encodeResponse(ctx, w, http.StatusOK, newAuthResponse(a, o, u, ps)); err != nil {
|
|
|
|
EncodeError(ctx, err, w)
|
2018-08-27 19:18:11 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type updateAuthorizationRequest struct {
|
2018-08-28 17:58:38 +00:00
|
|
|
ID platform.ID
|
|
|
|
Status platform.Status
|
2018-08-27 19:18:11 +00:00
|
|
|
}
|
|
|
|
|
2018-08-28 17:58:38 +00:00
|
|
|
func decodeSetAuthorizationStatusRequest(ctx context.Context, r *http.Request) (*updateAuthorizationRequest, error) {
|
2018-08-27 19:18:11 +00:00
|
|
|
params := httprouter.ParamsFromContext(ctx)
|
|
|
|
id := params.ByName("id")
|
|
|
|
if id == "" {
|
2019-01-04 17:21:34 +00:00
|
|
|
return nil, &platform.Error{
|
|
|
|
Code: platform.EInvalid,
|
|
|
|
Msg: "url missing id",
|
|
|
|
}
|
2018-08-27 19:18:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var i platform.ID
|
|
|
|
if err := i.DecodeFromString(id); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2018-08-28 17:58:38 +00:00
|
|
|
a := &setAuthorizationStatusRequest{}
|
2018-08-27 19:18:11 +00:00
|
|
|
if err := json.NewDecoder(r.Body).Decode(a); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &updateAuthorizationRequest{
|
2018-08-28 17:58:38 +00:00
|
|
|
ID: i,
|
|
|
|
Status: a.Status,
|
2018-08-27 19:18:11 +00:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2018-09-26 08:49:19 +00:00
|
|
|
// handleDeleteAuthorization is the HTTP handler for the DELETE /api/v2/authorizations/:id route.
|
2018-05-16 18:59:35 +00:00
|
|
|
func (h *AuthorizationHandler) handleDeleteAuthorization(w http.ResponseWriter, r *http.Request) {
|
|
|
|
ctx := r.Context()
|
|
|
|
|
|
|
|
req, err := decodeDeleteAuthorizationRequest(ctx, r)
|
|
|
|
if err != nil {
|
|
|
|
h.Logger.Info("failed to decode request", zap.String("handler", "deleteAuthorization"), zap.Error(err))
|
2018-06-28 19:32:16 +00:00
|
|
|
EncodeError(ctx, err, w)
|
2018-05-16 18:59:35 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := h.AuthorizationService.DeleteAuthorization(ctx, req.ID); err != nil {
|
|
|
|
// Don't log here, it should already be handled by the service
|
2018-06-28 19:32:16 +00:00
|
|
|
EncodeError(ctx, err, w)
|
2018-05-16 18:59:35 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-09-10 20:56:11 +00:00
|
|
|
w.WriteHeader(http.StatusNoContent)
|
2018-05-16 18:59:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type deleteAuthorizationRequest struct {
|
|
|
|
ID platform.ID
|
|
|
|
}
|
|
|
|
|
|
|
|
func decodeDeleteAuthorizationRequest(ctx context.Context, r *http.Request) (*deleteAuthorizationRequest, error) {
|
|
|
|
params := httprouter.ParamsFromContext(ctx)
|
|
|
|
id := params.ByName("id")
|
|
|
|
if id == "" {
|
2019-01-04 17:21:34 +00:00
|
|
|
return nil, &platform.Error{
|
|
|
|
Code: platform.EInvalid,
|
|
|
|
Msg: "url missing id",
|
|
|
|
}
|
2018-05-16 18:59:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var i platform.ID
|
|
|
|
if err := i.DecodeFromString(id); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &deleteAuthorizationRequest{
|
|
|
|
ID: i,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2018-12-28 23:02:19 +00:00
|
|
|
func getAuthorizedUser(r *http.Request, svc platform.UserService) (*platform.User, error) {
|
|
|
|
ctx := r.Context()
|
|
|
|
|
|
|
|
a, err := platcontext.GetAuthorizer(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return svc.FindUserByID(ctx, a.GetUserID())
|
|
|
|
}
|
|
|
|
|
2018-05-14 16:26:38 +00:00
|
|
|
// AuthorizationService connects to Influx via HTTP using tokens to manage authorizations
|
|
|
|
type AuthorizationService struct {
|
|
|
|
Addr string
|
|
|
|
Token string
|
|
|
|
InsecureSkipVerify bool
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ platform.AuthorizationService = (*AuthorizationService)(nil)
|
|
|
|
|
2018-08-27 19:18:11 +00:00
|
|
|
// FindAuthorizationByID finds the authorization against a remote influx server.
|
2018-05-16 18:59:35 +00:00
|
|
|
func (s *AuthorizationService) FindAuthorizationByID(ctx context.Context, id platform.ID) (*platform.Authorization, error) {
|
|
|
|
u, err := newURL(s.Addr, authorizationIDPath(id))
|
2018-05-14 16:26:38 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
req, err := http.NewRequest("GET", u.String(), nil)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-08-27 19:18:11 +00:00
|
|
|
SetToken(s.Token, req)
|
2018-05-14 16:26:38 +00:00
|
|
|
|
|
|
|
hc := newClient(u.Scheme, s.InsecureSkipVerify)
|
|
|
|
resp, err := hc.Do(req)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2018-11-07 18:55:52 +00:00
|
|
|
if err := CheckError(resp, true); err != nil {
|
2018-05-23 18:29:01 +00:00
|
|
|
return nil, err
|
2018-05-14 16:26:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var b platform.Authorization
|
2018-05-16 18:59:35 +00:00
|
|
|
if err := json.NewDecoder(resp.Body).Decode(&b); err != nil {
|
2018-05-14 16:26:38 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
2018-05-16 18:59:35 +00:00
|
|
|
defer resp.Body.Close()
|
2018-05-14 16:26:38 +00:00
|
|
|
|
|
|
|
return &b, nil
|
|
|
|
}
|
|
|
|
|
2018-05-16 18:59:35 +00:00
|
|
|
// FindAuthorizationByToken returns a single authorization by Token.
|
|
|
|
func (s *AuthorizationService) FindAuthorizationByToken(ctx context.Context, token string) (*platform.Authorization, error) {
|
2018-05-22 22:05:17 +00:00
|
|
|
return nil, errors.New("not supported in HTTP authorization service")
|
2018-05-14 16:26:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// FindAuthorizations returns a list of authorizations that match filter and the total count of matching authorizations.
|
|
|
|
// Additional options provide pagination & sorting.
|
|
|
|
func (s *AuthorizationService) FindAuthorizations(ctx context.Context, filter platform.AuthorizationFilter, opt ...platform.FindOptions) ([]*platform.Authorization, int, error) {
|
|
|
|
u, err := newURL(s.Addr, authorizationPath)
|
|
|
|
if err != nil {
|
|
|
|
return nil, 0, err
|
|
|
|
}
|
|
|
|
|
|
|
|
query := u.Query()
|
|
|
|
|
|
|
|
req, err := http.NewRequest("GET", u.String(), nil)
|
|
|
|
if err != nil {
|
|
|
|
return nil, 0, err
|
|
|
|
}
|
|
|
|
|
2018-05-16 18:59:35 +00:00
|
|
|
if filter.ID != nil {
|
|
|
|
query.Add("id", filter.ID.String())
|
|
|
|
}
|
|
|
|
|
|
|
|
if filter.UserID != nil {
|
|
|
|
query.Add("userID", filter.UserID.String())
|
|
|
|
}
|
|
|
|
|
|
|
|
if filter.User != nil {
|
|
|
|
query.Add("user", *filter.User)
|
|
|
|
}
|
|
|
|
|
2018-05-14 16:26:38 +00:00
|
|
|
req.URL.RawQuery = query.Encode()
|
2018-08-27 19:18:11 +00:00
|
|
|
SetToken(s.Token, req)
|
2018-05-14 16:26:38 +00:00
|
|
|
|
|
|
|
hc := newClient(u.Scheme, s.InsecureSkipVerify)
|
|
|
|
resp, err := hc.Do(req)
|
|
|
|
if err != nil {
|
|
|
|
return nil, 0, err
|
|
|
|
}
|
|
|
|
|
2018-11-07 18:55:52 +00:00
|
|
|
if err := CheckError(resp, true); err != nil {
|
2018-05-23 18:29:01 +00:00
|
|
|
return nil, 0, err
|
2018-05-14 16:26:38 +00:00
|
|
|
}
|
|
|
|
|
2018-12-28 23:02:19 +00:00
|
|
|
var as authsResponse
|
|
|
|
if err := json.NewDecoder(resp.Body).Decode(&as); err != nil {
|
2018-05-14 16:26:38 +00:00
|
|
|
return nil, 0, err
|
|
|
|
}
|
2018-05-16 18:59:35 +00:00
|
|
|
defer resp.Body.Close()
|
2018-05-14 16:26:38 +00:00
|
|
|
|
2018-12-28 23:02:19 +00:00
|
|
|
auths := make([]*platform.Authorization, 0, len(as.Auths))
|
|
|
|
for _, a := range as.Auths {
|
|
|
|
auths = append(auths, a.toPlatform())
|
2018-09-10 20:56:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return auths, len(auths), nil
|
2018-05-14 16:26:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const (
|
2018-09-26 08:49:19 +00:00
|
|
|
authorizationPath = "/api/v2/authorizations"
|
2018-05-14 16:26:38 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// CreateAuthorization creates a new authorization and sets b.ID with the new identifier.
|
2018-05-16 18:59:35 +00:00
|
|
|
func (s *AuthorizationService) CreateAuthorization(ctx context.Context, a *platform.Authorization) error {
|
2018-05-14 16:26:38 +00:00
|
|
|
u, err := newURL(s.Addr, authorizationPath)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-12-28 23:02:19 +00:00
|
|
|
newAuth, err := newPostAuthorizationRequest(a)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
octets, err := json.Marshal(newAuth)
|
2018-05-14 16:26:38 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
req, err := http.NewRequest("POST", u.String(), bytes.NewReader(octets))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
req.Header.Set("Content-Type", "application/json")
|
2018-08-27 19:18:11 +00:00
|
|
|
SetToken(s.Token, req)
|
2018-05-14 16:26:38 +00:00
|
|
|
|
|
|
|
hc := newClient(u.Scheme, s.InsecureSkipVerify)
|
|
|
|
|
|
|
|
resp, err := hc.Do(req)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-05-23 18:29:01 +00:00
|
|
|
// TODO(jsternberg): Should this check for a 201 explicitly?
|
2018-11-07 18:55:52 +00:00
|
|
|
if err := CheckError(resp, true); err != nil {
|
2018-05-23 18:29:01 +00:00
|
|
|
return err
|
2018-05-14 16:26:38 +00:00
|
|
|
}
|
|
|
|
|
2018-05-16 18:59:35 +00:00
|
|
|
if err := json.NewDecoder(resp.Body).Decode(a); err != nil {
|
2018-05-14 16:26:38 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-08-28 17:58:38 +00:00
|
|
|
type setAuthorizationStatusRequest struct {
|
|
|
|
Status platform.Status `json:"status"`
|
2018-08-27 19:18:11 +00:00
|
|
|
}
|
|
|
|
|
2018-08-28 17:58:38 +00:00
|
|
|
// SetAuthorizationStatus updates an authorization's status.
|
|
|
|
func (s *AuthorizationService) SetAuthorizationStatus(ctx context.Context, id platform.ID, status platform.Status) error {
|
2018-08-27 19:18:11 +00:00
|
|
|
u, err := newURL(s.Addr, authorizationIDPath(id))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-08-28 17:58:38 +00:00
|
|
|
b, err := json.Marshal(setAuthorizationStatusRequest{
|
|
|
|
Status: status,
|
2018-08-27 19:18:11 +00:00
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
req, err := http.NewRequest("PATCH", u.String(), bytes.NewReader(b))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
SetToken(s.Token, req)
|
|
|
|
|
|
|
|
hc := newClient(u.Scheme, s.InsecureSkipVerify)
|
|
|
|
|
|
|
|
resp, err := hc.Do(req)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-11-07 18:55:52 +00:00
|
|
|
if err := CheckError(resp, true); err != nil {
|
2018-08-27 19:18:11 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-05-16 18:59:35 +00:00
|
|
|
// DeleteAuthorization removes a authorization by id.
|
|
|
|
func (s *AuthorizationService) DeleteAuthorization(ctx context.Context, id platform.ID) error {
|
|
|
|
u, err := newURL(s.Addr, authorizationIDPath(id))
|
2018-05-14 16:26:38 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
req, err := http.NewRequest("DELETE", u.String(), nil)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-08-27 19:18:11 +00:00
|
|
|
SetToken(s.Token, req)
|
2018-05-14 16:26:38 +00:00
|
|
|
|
|
|
|
hc := newClient(u.Scheme, s.InsecureSkipVerify)
|
|
|
|
resp, err := hc.Do(req)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-11-07 18:55:52 +00:00
|
|
|
return CheckError(resp, true)
|
2018-05-14 16:26:38 +00:00
|
|
|
}
|
|
|
|
|
2018-05-16 18:59:35 +00:00
|
|
|
func authorizationIDPath(id platform.ID) string {
|
|
|
|
return path.Join(authorizationPath, id.String())
|
2018-05-14 16:26:38 +00:00
|
|
|
}
|