Clean up service and return license errors (#1097)

- Expose license errors instead of permission errors by adding explicit skip authorization.
- Remove pre-Teams authorization checks from service.

Fixes #964
This commit is contained in:
Zach Wasserman
2021-06-16 10:55:41 -07:00
committed by GitHub
parent 40b756e46e
commit d78c5fd124
13 changed files with 161 additions and 189 deletions
+8 -1
View File
@@ -69,8 +69,15 @@ export class LoginPage extends Component {
const { HOME } = paths;
const redirectTime = 1500;
return dispatch(loginUser(formData))
.then(() => {
.then((user) => {
this.setState({ loginVisible: false });
// Redirect to password reset page if user is forced to reset password.
// Any other requests will fail.
if (user.force_password_reset) {
return dispatch(push(paths.RESET_PASSWORD));
}
setTimeout(() => {
const nextLocation = redirectLocation || HOME;
dispatch(clearRedirectLocation);
+34 -2
View File
@@ -6,6 +6,13 @@ import (
"github.com/fleetdm/fleet/server/fleet"
)
const (
// ForbiddenErrorMessage is the error message that should be returned to
// clients when an action is forbidden. It is intentionally vague to prevent
// disclosing information that a client should not have access to.
ForbiddenErrorMessage = "forbidden"
)
// Forbidden is the error type for authorization errors
type Forbidden struct {
internal string
@@ -28,10 +35,10 @@ func ForbiddenWithInternal(internal string, subject *fleet.User, object, action
// Error implements the error interface.
func (e *Forbidden) Error() string {
return "forbidden"
return ForbiddenErrorMessage
}
// StatusCode implements the service.ErrWithStatusCode interface.
// StatusCode implements the go-kit http StatusCoder interface.
func (e *Forbidden) StatusCode() int {
return http.StatusForbidden
}
@@ -49,3 +56,28 @@ func (e *Forbidden) LogFields() []interface{} {
"action", e.action,
}
}
// CheckMissing is the error to return when no authorization check was performed
// by the service.
type CheckMissing struct {
response interface{}
}
// CheckMissingWithResponse creats a new error indicating the authorization
// check was missed, and including the response for further anaylis by the error
// encoder.
func CheckMissingWithResponse(response interface{}) *CheckMissing {
return &CheckMissing{response: response}
}
func (e *CheckMissing) Error() string {
return ForbiddenErrorMessage
}
func (e *CheckMissing) Internal() string {
return "Missing authorization check"
}
func (e *CheckMissing) Response() interface{} {
return e.response
}
-28
View File
@@ -93,34 +93,6 @@ func (v Viewer) CanPerformActions() bool {
return false
}
// CanPerformAdminActions indicates whether or not the current user can perform
// administrative actions.
func (v Viewer) CanPerformAdminActions() bool {
// TODO this needs revisiting for teams!
if v.User != nil {
return v.CanPerformActions()
}
return false
}
// CanPerformReadActionOnUser returns a bool indicating the current user's
// ability to perform read actions on the given user
func (v Viewer) CanPerformReadActionOnUser(uid uint) bool {
if v.User != nil {
return v.CanPerformActions() || (v.IsLoggedIn() && v.IsUserID(uid))
}
return false
}
// CanPerformWriteActionOnUser returns a bool indicating the current user's
// ability to perform write actions on the given user
func (v Viewer) CanPerformWriteActionOnUser(uid uint) bool {
if v.User != nil {
return (v.IsLoggedIn() && v.IsUserID(uid)) || v.CanPerformAdminActions()
}
return false
}
// CanPerformPasswordReset returns a bool indicating the current user's
// ability to perform a password reset (in the case they have been required by
// the admin).
+18 -15
View File
@@ -7,8 +7,9 @@ import (
)
var (
ErrNoContext = errors.New("context key not set")
ErrMissingLicense = &LicenseError{}
ErrNoContext = errors.New("context key not set")
ErrPasswordResetRequired = &passwordResetRequiredError{}
ErrMissingLicense = &licenseError{}
)
// ErrWithInternal is an interface for errors that include extra "internal"
@@ -29,14 +30,6 @@ type ErrWithLogFields interface {
LogFields() []interface{}
}
// ErrWithStatusCode is an interface for errors that should set a specific HTTP
// status when encoding.
type ErrWithStatusCode interface {
error
// StatusCode returns the HTTP status code that should be returned.
StatusCode() int
}
// ErrWithRetryAfter is an interface for errors that should set a specific HTTP
// Header Retry-After value (see
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After)
@@ -165,13 +158,23 @@ func (e PermissionError) PermissionError() []map[string]string {
return forbidden
}
// LicenseError is returned when the application is not properly licensed.
type LicenseError struct{}
// licenseError is returned when the application is not properly licensed.
type licenseError struct{}
func (e LicenseError) Error() string {
return "requires Fleet Basic license"
func (e licenseError) Error() string {
return "Requires Fleet Basic license"
}
func (e LicenseError) StatusCode() int {
func (e licenseError) StatusCode() int {
return http.StatusPaymentRequired
}
type passwordResetRequiredError struct{}
func (e passwordResetRequiredError) Error() string {
return "password reset required"
}
func (e passwordResetRequiredError) StatusCode() int {
return http.StatusUnauthorized
}
+6 -5
View File
@@ -22,7 +22,7 @@ type appConfigResponse struct {
HostExpirySettings *fleet.HostExpirySettings `json:"host_expiry_settings,omitempty"`
HostSettings *fleet.HostSettings `json:"host_settings,omitempty"`
License *fleet.LicenseInfo `json:"license,omitempty"`
Err error `json:"error,omitempty"`
Err error `json:"error,omitempty"`
}
func (r appConfigResponse) error() error { return r.Err }
@@ -41,11 +41,12 @@ func makeGetAppConfigEndpoint(svc fleet.Service) endpoint.Endpoint {
if err != nil {
return nil, err
}
var smtpSettings *fleet.SMTPSettingsPayload
var ssoSettings *fleet.SSOSettingsPayload
var hostExpirySettings *fleet.HostExpirySettings
// only admin can see smtp, sso, and host expiry settings
if vc.CanPerformAdminActions() {
if vc.User.GlobalRole != nil && *vc.User.GlobalRole == fleet.RoleAdmin {
smtpSettings = smtpSettingsFromAppConfig(config)
if smtpSettings.SMTPPassword != nil {
*smtpSettings.SMTPPassword = "********"
@@ -71,7 +72,7 @@ func makeGetAppConfigEndpoint(svc fleet.Service) endpoint.Endpoint {
OrgLogoURL: &config.OrgLogoURL,
},
ServerSettings: &fleet.ServerSettings{
ServerURL: &config.ServerURL,
ServerURL: &config.ServerURL,
LiveQueryDisabled: &config.LiveQueryDisabled,
},
SMTPSettings: smtpSettings,
@@ -99,7 +100,7 @@ func makeModifyAppConfigEndpoint(svc fleet.Service) endpoint.Endpoint {
OrgLogoURL: &config.OrgLogoURL,
},
ServerSettings: &fleet.ServerSettings{
ServerURL: &config.ServerURL,
ServerURL: &config.ServerURL,
LiveQueryDisabled: &config.LiveQueryDisabled,
},
SMTPSettings: smtpSettingsFromAppConfig(config),
@@ -176,7 +177,7 @@ func makeApplyEnrollSecretSpecEndpoint(svc fleet.Service) endpoint.Endpoint {
type getEnrollSecretSpecResponse struct {
Spec *fleet.EnrollSecretSpec `json:"specs"`
Err error `json:"error,omitempty"`
Err error `json:"error,omitempty"`
}
func (r getEnrollSecretSpecResponse) error() error { return r.Err }
+11 -63
View File
@@ -57,10 +57,16 @@ func getNodeKey(r interface{}) (string, error) {
// authenticatedUser wraps an endpoint, requires that the Fleet user is
// authenticated, and populates the context with a Viewer struct for that user.
//
// If auth fails or the user must reset their password, an error is returned.
func authenticatedUser(svc fleet.Service, next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
// first check if already successfully set
if _, ok := viewer.FromContext(ctx); ok {
if v, ok := viewer.FromContext(ctx); ok {
if v.User.AdminForcedPasswordReset {
return nil, fleet.ErrPasswordResetRequired
}
return next(ctx, request)
}
@@ -75,6 +81,10 @@ func authenticatedUser(svc fleet.Service, next endpoint.Endpoint) endpoint.Endpo
return nil, err
}
if v.User.AdminForcedPasswordReset {
return nil, fleet.ErrPasswordResetRequired
}
ctx = viewer.NewContext(ctx, *v)
return next(ctx, request)
}
@@ -93,60 +103,6 @@ func authViewer(ctx context.Context, sessionKey string, svc fleet.Service) (*vie
return &viewer.Viewer{User: user, Session: session}, nil
}
func mustBeAdmin(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
vc, ok := viewer.FromContext(ctx)
if !ok {
return nil, fleet.ErrNoContext
}
if !vc.CanPerformAdminActions() {
return nil, fleet.NewPermissionError("must be an admin")
}
return next(ctx, request)
}
}
func canPerformActions(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
vc, ok := viewer.FromContext(ctx)
if !ok {
return nil, fleet.ErrNoContext
}
if !vc.CanPerformActions() {
return nil, fleet.NewPermissionError("no read permissions")
}
return next(ctx, request)
}
}
func canReadUser(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
vc, ok := viewer.FromContext(ctx)
if !ok {
return nil, fleet.ErrNoContext
}
uid := requestUserIDFromContext(ctx)
if !vc.CanPerformReadActionOnUser(uid) {
return nil, fleet.NewPermissionError("no read permissions on user")
}
return next(ctx, request)
}
}
func canModifyUser(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
vc, ok := viewer.FromContext(ctx)
if !ok {
return nil, fleet.ErrNoContext
}
uid := requestUserIDFromContext(ctx)
if !vc.CanPerformWriteActionOnUser(uid) {
return nil, fleet.NewPermissionError("no write permissions on user")
}
return next(ctx, request)
}
}
func canPerformPasswordReset(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
vc, ok := viewer.FromContext(ctx)
@@ -159,11 +115,3 @@ func canPerformPasswordReset(next endpoint.Endpoint) endpoint.Endpoint {
return next(ctx, request)
}
}
func requestUserIDFromContext(ctx context.Context) uint {
userID, ok := ctx.Value("request-id").(uint)
if !ok {
return 0
}
return userID
}
@@ -62,10 +62,6 @@ import (
// request interface{}
// }{
// {
// endpoint: mustBeAdmin(e),
// wantErr: fleet.ErrNoContext,
// },
// {
// endpoint: canReadUser(e),
// wantErr: fleet.ErrNoContext,
// },
@@ -74,15 +70,6 @@ import (
// wantErr: fleet.ErrNoContext,
// },
// {
// endpoint: mustBeAdmin(e),
// vc: &viewer.Viewer{User: admin1, Session: admin1Session},
// },
// {
// endpoint: mustBeAdmin(e),
// vc: &viewer.Viewer{User: user1, Session: user1Session},
// wantErr: permissionError{message: "must be an admin"},
// },
// {
// endpoint: canModifyUser(e),
// vc: &viewer.Viewer{User: admin1, Session: admin1Session},
// },
+33 -38
View File
@@ -141,35 +141,30 @@ func MakeFleetServerEndpoints(svc fleet.Service, urlPrefix string, limitStore th
CallbackSSO: makeCallbackSSOEndpoint(svc, urlPrefix),
SSOSettings: makeSSOSettingsEndpoint(svc),
// Authenticated user endpoints
// Each of these endpoints should have exactly one
// authorization check around the make.*Endpoint method. At a
// minimum, canPerformActions. Some endpoints use
// stricter/different checks and should NOT also use
// canPerformActions (these other checks should also call
// canPerformActions if that is appropriate).
Me: authenticatedUser(svc, canPerformActions(makeGetSessionUserEndpoint(svc))),
ChangePassword: authenticatedUser(svc, canPerformActions(makeChangePasswordEndpoint(svc))),
GetUser: authenticatedUser(svc, canReadUser(makeGetUserEndpoint(svc))),
ListUsers: authenticatedUser(svc, canPerformActions(makeListUsersEndpoint(svc))),
ModifyUser: authenticatedUser(svc, canModifyUser(makeModifyUserEndpoint(svc))),
DeleteUser: authenticatedUser(svc, canModifyUser(makeDeleteUserEndpoint(svc))),
RequirePasswordReset: authenticatedUser(svc, mustBeAdmin(makeRequirePasswordResetEndpoint(svc))),
CreateUser: authenticatedUser(svc, mustBeAdmin(makeCreateUserEndpoint(svc))),
// PerformRequiredPasswordReset needs only to authenticate the
// logged in user
PerformRequiredPasswordReset: authenticatedUser(svc, canPerformPasswordReset(makePerformRequiredPasswordResetEndpoint(svc))),
GetSessionsForUserInfo: authenticatedUser(svc, canReadUser(makeGetInfoAboutSessionsForUserEndpoint(svc))),
DeleteSessionsForUser: authenticatedUser(svc, canModifyUser(makeDeleteSessionsForUserEndpoint(svc))),
GetSessionInfo: authenticatedUser(svc, mustBeAdmin(makeGetInfoAboutSessionEndpoint(svc))),
DeleteSession: authenticatedUser(svc, mustBeAdmin(makeDeleteSessionEndpoint(svc))),
GetAppConfig: authenticatedUser(svc, canPerformActions(makeGetAppConfigEndpoint(svc))),
ModifyAppConfig: authenticatedUser(svc, mustBeAdmin(makeModifyAppConfigEndpoint(svc))),
ApplyEnrollSecretSpec: authenticatedUser(svc, mustBeAdmin(makeApplyEnrollSecretSpecEndpoint(svc))),
GetEnrollSecretSpec: authenticatedUser(svc, canPerformActions(makeGetEnrollSecretSpecEndpoint(svc))),
CreateInvite: authenticatedUser(svc, mustBeAdmin(makeCreateInviteEndpoint(svc))),
ListInvites: authenticatedUser(svc, mustBeAdmin(makeListInvitesEndpoint(svc))),
DeleteInvite: authenticatedUser(svc, mustBeAdmin(makeDeleteInviteEndpoint(svc))),
PerformRequiredPasswordReset: canPerformPasswordReset(makePerformRequiredPasswordResetEndpoint(svc)),
// Standard user authentication routes
Me: authenticatedUser(svc, makeGetSessionUserEndpoint(svc)),
ChangePassword: authenticatedUser(svc, makeChangePasswordEndpoint(svc)),
GetUser: authenticatedUser(svc, makeGetUserEndpoint(svc)),
ListUsers: authenticatedUser(svc, makeListUsersEndpoint(svc)),
ModifyUser: authenticatedUser(svc, makeModifyUserEndpoint(svc)),
DeleteUser: authenticatedUser(svc, makeDeleteUserEndpoint(svc)),
RequirePasswordReset: authenticatedUser(svc, makeRequirePasswordResetEndpoint(svc)),
CreateUser: authenticatedUser(svc, makeCreateUserEndpoint(svc)),
GetSessionsForUserInfo: authenticatedUser(svc, makeGetInfoAboutSessionsForUserEndpoint(svc)),
DeleteSessionsForUser: authenticatedUser(svc, makeDeleteSessionsForUserEndpoint(svc)),
GetSessionInfo: authenticatedUser(svc, makeGetInfoAboutSessionEndpoint(svc)),
DeleteSession: authenticatedUser(svc, makeDeleteSessionEndpoint(svc)),
GetAppConfig: authenticatedUser(svc, makeGetAppConfigEndpoint(svc)),
ModifyAppConfig: authenticatedUser(svc, makeModifyAppConfigEndpoint(svc)),
ApplyEnrollSecretSpec: authenticatedUser(svc, makeApplyEnrollSecretSpecEndpoint(svc)),
GetEnrollSecretSpec: authenticatedUser(svc, makeGetEnrollSecretSpecEndpoint(svc)),
CreateInvite: authenticatedUser(svc, makeCreateInviteEndpoint(svc)),
ListInvites: authenticatedUser(svc, makeListInvitesEndpoint(svc)),
DeleteInvite: authenticatedUser(svc, makeDeleteInviteEndpoint(svc)),
GetQuery: authenticatedUser(svc, makeGetQueryEndpoint(svc)),
ListQueries: authenticatedUser(svc, makeListQueriesEndpoint(svc)),
CreateQuery: authenticatedUser(svc, makeCreateQueryEndpoint(svc)),
@@ -221,23 +216,23 @@ func MakeFleetServerEndpoints(svc fleet.Service, urlPrefix string, limitStore th
GetCarve: authenticatedUser(svc, makeGetCarveEndpoint(svc)),
GetCarveBlock: authenticatedUser(svc, makeGetCarveBlockEndpoint(svc)),
Version: authenticatedUser(svc, makeVersionEndpoint(svc)),
// TODO permissions for teams endpoints
CreateTeam: authenticatedUser(svc, makeCreateTeamEndpoint(svc)),
ModifyTeam: authenticatedUser(svc, makeModifyTeamEndpoint(svc)),
ModifyTeamAgentOptions: authenticatedUser(svc, makeModifyTeamAgentOptionsEndpoint(svc)),
DeleteTeam: authenticatedUser(svc, makeDeleteTeamEndpoint(svc)),
ListTeams: authenticatedUser(svc, makeListTeamsEndpoint(svc)),
ListTeamUsers: authenticatedUser(svc, makeListTeamUsersEndpoint(svc)),
AddTeamUsers: authenticatedUser(svc, makeAddTeamUsersEndpoint(svc)),
DeleteTeamUsers: authenticatedUser(svc, makeDeleteTeamUsersEndpoint(svc)),
TeamEnrollSecrets: authenticatedUser(svc, makeTeamEnrollSecretsEndpoint(svc)),
CreateTeam: authenticatedUser(svc, makeCreateTeamEndpoint(svc)),
ModifyTeam: authenticatedUser(svc, makeModifyTeamEndpoint(svc)),
ModifyTeamAgentOptions: authenticatedUser(svc, makeModifyTeamAgentOptionsEndpoint(svc)),
DeleteTeam: authenticatedUser(svc, makeDeleteTeamEndpoint(svc)),
ListTeams: authenticatedUser(svc, makeListTeamsEndpoint(svc)),
ListTeamUsers: authenticatedUser(svc, makeListTeamUsersEndpoint(svc)),
AddTeamUsers: authenticatedUser(svc, makeAddTeamUsersEndpoint(svc)),
DeleteTeamUsers: authenticatedUser(svc, makeDeleteTeamUsersEndpoint(svc)),
TeamEnrollSecrets: authenticatedUser(svc, makeTeamEnrollSecretsEndpoint(svc)),
// Authenticated status endpoints
StatusResultStore: authenticatedUser(svc, makeStatusResultStoreEndpoint(svc)),
StatusLiveQuery: authenticatedUser(svc, makeStatusLiveQueryEndpoint(svc)),
// Osquery endpoints
EnrollAgent: makeEnrollAgentEndpoint(svc),
EnrollAgent: makeEnrollAgentEndpoint(svc),
// Authenticated osquery endpoints
GetClientConfig: authenticatedHost(svc, makeGetClientConfigEndpoint(svc)),
GetDistributedQueries: authenticatedHost(svc, makeGetDistributedQueriesEndpoint(svc)),
SubmitDistributedQueryResults: authenticatedHost(svc, makeSubmitDistributedQueryResultsEndpoint(svc)),
@@ -34,17 +34,14 @@ func (m *Middleware) AuthzCheck() endpoint.Middleware {
// appropriately).
var authFailedError *fleet.AuthFailedError
var authRequiredError *fleet.AuthRequiredError
var licenseError *fleet.LicenseError
if errors.As(err, &authFailedError) ||
errors.As(err, &authRequiredError) ||
errors.As(err, &licenseError) {
if errors.As(err, &authFailedError) || errors.As(err, &authRequiredError) || errors.Is(err, fleet.ErrPasswordResetRequired) {
return nil, err
}
// If authorization was not checked, return a response that will
// marshal to a generic error and log that the check was missed.
if !authzctx.Checked {
return nil, authz.ForbiddenWithInternal("missed authz check", nil, nil, nil)
return nil, authz.CheckMissingWithResponse(response)
}
return response, err
+4 -3
View File
@@ -6,6 +6,7 @@ import (
"strconv"
"time"
"github.com/fleetdm/fleet/server/authz"
"github.com/fleetdm/fleet/server/contexts/viewer"
"github.com/fleetdm/fleet/server/fleet"
"github.com/fleetdm/fleet/server/ptr"
@@ -158,14 +159,14 @@ type campaignStatus struct {
func (svc Service) StreamCampaignResults(ctx context.Context, conn *websocket.Conn, campaignID uint) {
if err := svc.authz.Authorize(ctx, &fleet.Query{}, fleet.ActionRun); err != nil {
level.Info(svc.logger).Log("err", "stream results authorization failed")
conn.WriteJSONError("forbidden")
conn.WriteJSONError(authz.ForbiddenErrorMessage)
return
}
vc, ok := viewer.FromContext(ctx)
if !ok {
level.Info(svc.logger).Log("err", "stream results viewer missing")
conn.WriteJSONError("forbidden")
conn.WriteJSONError(authz.ForbiddenErrorMessage)
return
}
@@ -183,7 +184,7 @@ func (svc Service) StreamCampaignResults(ctx context.Context, conn *websocket.Co
"expected", campaign.UserID,
"got", vc.User.ID,
)
conn.WriteJSONError("forbidden")
conn.WriteJSONError(authz.ForbiddenErrorMessage)
return
}
+36
View File
@@ -8,37 +8,73 @@ import (
)
func (svc *Service) NewTeam(ctx context.Context, p fleet.TeamPayload) (*fleet.Team, error) {
// skipauth: No authorization check needed due to implementation returning
// only license error.
svc.authz.SkipAuthorization(ctx)
return nil, fleet.ErrMissingLicense
}
func (svc *Service) ModifyTeam(ctx context.Context, id uint, payload fleet.TeamPayload) (*fleet.Team, error) {
// skipauth: No authorization check needed due to implementation returning
// only license error.
svc.authz.SkipAuthorization(ctx)
return nil, fleet.ErrMissingLicense
}
func (svc *Service) ModifyTeamAgentOptions(ctx context.Context, id uint, options json.RawMessage) (*fleet.Team, error) {
// skipauth: No authorization check needed due to implementation returning
// only license error.
svc.authz.SkipAuthorization(ctx)
return nil, fleet.ErrMissingLicense
}
func (svc *Service) AddTeamUsers(ctx context.Context, teamID uint, users []fleet.TeamUser) (*fleet.Team, error) {
// skipauth: No authorization check needed due to implementation returning
// only license error.
svc.authz.SkipAuthorization(ctx)
return nil, fleet.ErrMissingLicense
}
func (svc *Service) DeleteTeamUsers(ctx context.Context, teamID uint, users []fleet.TeamUser) (*fleet.Team, error) {
// skipauth: No authorization check needed due to implementation returning
// only license error.
svc.authz.SkipAuthorization(ctx)
return nil, fleet.ErrMissingLicense
}
func (svc *Service) ListTeamUsers(ctx context.Context, teamID uint, opt fleet.ListOptions) ([]*fleet.User, error) {
// skipauth: No authorization check needed due to implementation returning
// only license error.
svc.authz.SkipAuthorization(ctx)
return nil, fleet.ErrMissingLicense
}
func (svc *Service) ListTeams(ctx context.Context, opt fleet.ListOptions) ([]*fleet.Team, error) {
// skipauth: No authorization check needed due to implementation returning
// only license error.
svc.authz.SkipAuthorization(ctx)
return nil, fleet.ErrMissingLicense
}
func (svc *Service) DeleteTeam(ctx context.Context, tid uint) error {
// skipauth: No authorization check needed due to implementation returning
// only license error.
svc.authz.SkipAuthorization(ctx)
return fleet.ErrMissingLicense
}
func (svc *Service) TeamEnrollSecrets(ctx context.Context, teamID uint) ([]*fleet.EnrollSecret, error) {
// skipauth: No authorization check needed due to implementation returning
// only license error.
svc.authz.SkipAuthorization(ctx)
return nil, fleet.ErrMissingLicense
}
+8 -5
View File
@@ -7,6 +7,7 @@ import (
"strconv"
"github.com/fleetdm/fleet/server/fleet"
kithttp "github.com/go-kit/kit/transport/http"
"github.com/pkg/errors"
)
@@ -24,9 +25,11 @@ type jsonError struct {
// a generic "name" field. The frontend client always expects errors in a
// []map[string]string format.
func baseError(err string) []map[string]string {
return []map[string]string{map[string]string{
"name": "base",
"reason": err},
return []map[string]string{
{
"name": "base",
"reason": err,
},
}
}
@@ -67,7 +70,7 @@ func encodeError(ctx context.Context, err error, w http.ResponseWriter) {
Message: "Validation Failed",
Errors: baseError(err.Error()),
}
w.WriteHeader(http.StatusForbidden)
w.WriteHeader(http.StatusUnprocessableEntity)
enc.Encode(ve)
return
}
@@ -139,7 +142,7 @@ func encodeError(ctx context.Context, err error, w http.ResponseWriter) {
// Get specific status code if it is available from this error type,
// defaulting to HTTP 500
status := http.StatusInternalServerError
if e, ok := err.(fleet.ErrWithStatusCode); ok {
if e, ok := err.(kithttp.StatusCoder); ok {
status = e.StatusCode()
}
+1 -11
View File
@@ -135,17 +135,7 @@ func passwordRequiredForEmailChange(ctx context.Context, uid uint, invalid *flee
return false
}
// if a user is changing own email need a password no matter what
if vc.UserID() == uid {
return true
}
// if an admin is changing another users email no password needed
if vc.CanPerformAdminActions() {
return false
}
// should never get here because a non admin can't change the email of another
// user
invalid.Append("auth", "this user can't change another user's email")
return false
return vc.UserID() == uid
}
func (mw validationMiddleware) ChangePassword(ctx context.Context, oldPass, newPass string) error {