// Show returns the authorized user based on the provided Token func (c *UserController) Show(ctx *app.ShowUserContext) error { id, err := c.tokenManager.Locate(ctx) if err != nil { jerrors, _ := jsonapi.ErrorToJSONAPIErrors(goa.ErrBadRequest(err.Error())) return ctx.BadRequest(jerrors) } return application.Transactional(c.db, func(appl application.Application) error { identity, err := appl.Identities().Load(ctx, id) if err != nil || identity == nil { log.Error(ctx, map[string]interface{}{ "identityID": id, }, "auth token containers id %s of unknown Identity", id) jerrors, _ := jsonapi.ErrorToJSONAPIErrors(goa.ErrUnauthorized(fmt.Sprintf("Auth token contains id %s of unknown Identity\n", id))) return ctx.Unauthorized(jerrors) } var user *account.User userID := identity.UserID if userID.Valid { user, err = appl.Users().Load(ctx.Context, userID.UUID) if err != nil { return jsonapi.JSONErrorResponse(ctx, errors.Wrap(err, fmt.Sprintf("Can't load user with id %s", userID.UUID))) } } return ctx.OK(ConvertUser(ctx.RequestData, identity, user)) }) }
// Update does PATCH comment func (c *CommentsController) Update(ctx *app.UpdateCommentsContext) error { identity, err := login.ContextIdentity(ctx) if err != nil { return jsonapi.JSONErrorResponse(ctx, goa.ErrUnauthorized(err.Error())) } return application.Transactional(c.db, func(appl application.Application) error { cm, err := appl.Comments().Load(ctx.Context, ctx.CommentID) if err != nil { return jsonapi.JSONErrorResponse(ctx, err) } if identity != cm.CreatedBy.String() { // need to use the goa.NewErrorClass() func as there is no native support for 403 in goa // and it is not planned to be supported yet: https://github.com/goadesign/goa/pull/1030 return jsonapi.JSONErrorResponse(ctx, goa.NewErrorClass("forbidden", 403)("User is not the comment author")) } cm.Body = *ctx.Payload.Data.Attributes.Body cm.Markup = rendering.NilSafeGetMarkup(ctx.Payload.Data.Attributes.Markup) cm, err = appl.Comments().Save(ctx.Context, cm) if err != nil { return jsonapi.JSONErrorResponse(ctx, err) } res := &app.CommentSingle{ Data: ConvertComment(ctx.RequestData, cm, CommentIncludeParentWorkItem()), } return ctx.OK(res) }) }
// Create runs the create action. func (c *SpaceController) Create(ctx *app.CreateSpaceContext) error { _, err := login.ContextIdentity(ctx) if err != nil { return jsonapi.JSONErrorResponse(ctx, goa.ErrUnauthorized(err.Error())) } err = validateCreateSpace(ctx) if err != nil { return jsonapi.JSONErrorResponse(ctx, err) } return application.Transactional(c.db, func(appl application.Application) error { reqSpace := ctx.Payload.Data newSpace := space.Space{ Name: *reqSpace.Attributes.Name, } if reqSpace.Attributes.Description != nil { newSpace.Description = *reqSpace.Attributes.Description } space, err := appl.Spaces().Create(ctx, &newSpace) if err != nil { return jsonapi.JSONErrorResponse(ctx, err) } res := &app.SpaceSingle{ Data: ConvertSpace(ctx.RequestData, space), } ctx.ResponseData.Header().Set("Location", rest.AbsoluteURL(ctx.RequestData, app.SpaceHref(res.Data.ID))) return ctx.Created(res) }) }
// Create runs the create action. func (c *WorkItemCommentsController) Create(ctx *app.CreateWorkItemCommentsContext) error { return application.Transactional(c.db, func(appl application.Application) error { _, err := appl.WorkItems().Load(ctx, ctx.ID) if err != nil { return jsonapi.JSONErrorResponse(ctx, goa.ErrNotFound(err.Error())) } currentUser, err := login.ContextIdentity(ctx) if err != nil { return jsonapi.JSONErrorResponse(ctx, goa.ErrUnauthorized(err.Error())) } currentUserID, err := uuid.FromString(currentUser) if err != nil { return jsonapi.JSONErrorResponse(ctx, goa.ErrUnauthorized(err.Error())) } reqComment := ctx.Payload.Data markup := rendering.NilSafeGetMarkup(reqComment.Attributes.Markup) newComment := comment.Comment{ ParentID: ctx.ID, Body: reqComment.Attributes.Body, Markup: markup, CreatedBy: currentUserID, } err = appl.Comments().Create(ctx, &newComment) if err != nil { return jsonapi.JSONErrorResponse(ctx, goa.ErrInternal(err.Error())) } res := &app.CommentSingle{ Data: ConvertComment(ctx.RequestData, &newComment), } return ctx.OK(res) }) }
// Update runs the update action. func (c *IterationController) Update(ctx *app.UpdateIterationContext) error { _, err := login.ContextIdentity(ctx) if err != nil { return jsonapi.JSONErrorResponse(ctx, goa.ErrUnauthorized(err.Error())) } id, err := uuid.FromString(ctx.IterationID) if err != nil { return jsonapi.JSONErrorResponse(ctx, goa.ErrNotFound(err.Error())) } return application.Transactional(c.db, func(appl application.Application) error { itr, err := appl.Iterations().Load(ctx.Context, id) if err != nil { return jsonapi.JSONErrorResponse(ctx, err) } if ctx.Payload.Data.Attributes.Name != nil { itr.Name = *ctx.Payload.Data.Attributes.Name } if ctx.Payload.Data.Attributes.StartAt != nil { itr.StartAt = ctx.Payload.Data.Attributes.StartAt } if ctx.Payload.Data.Attributes.EndAt != nil { itr.EndAt = ctx.Payload.Data.Attributes.EndAt } if ctx.Payload.Data.Attributes.Description != nil { itr.Description = ctx.Payload.Data.Attributes.Description } if ctx.Payload.Data.Attributes.State != nil { if *ctx.Payload.Data.Attributes.State == iteration.IterationStateStart { res, err := appl.Iterations().CanStartIteration(ctx, itr) if res == false && err != nil { return jsonapi.JSONErrorResponse(ctx, err) } } itr.State = *ctx.Payload.Data.Attributes.State } itr, err = appl.Iterations().Save(ctx.Context, *itr) if err != nil { return jsonapi.JSONErrorResponse(ctx, err) } response := app.IterationSingle{ Data: ConvertIteration(ctx.RequestData, itr), } return ctx.OK(&response) }) }
// Create runs the create action. func (c *SpaceIterationsController) Create(ctx *app.CreateSpaceIterationsContext) error { _, err := login.ContextIdentity(ctx) if err != nil { return jsonapi.JSONErrorResponse(ctx, goa.ErrUnauthorized(err.Error())) } spaceID, err := uuid.FromString(ctx.ID) if err != nil { return jsonapi.JSONErrorResponse(ctx, goa.ErrNotFound(err.Error())) } // Validate Request if ctx.Payload.Data == nil { return jsonapi.JSONErrorResponse(ctx, errors.NewBadParameterError("data", nil).Expected("not nil")) } reqIter := ctx.Payload.Data if reqIter.Attributes.Name == nil { return jsonapi.JSONErrorResponse(ctx, errors.NewBadParameterError("data.attributes.name", nil).Expected("not nil")) } return application.Transactional(c.db, func(appl application.Application) error { _, err = appl.Spaces().Load(ctx, spaceID) if err != nil { return jsonapi.JSONErrorResponse(ctx, goa.ErrNotFound(err.Error())) } newItr := iteration.Iteration{ SpaceID: spaceID, Name: *reqIter.Attributes.Name, StartAt: reqIter.Attributes.StartAt, EndAt: reqIter.Attributes.EndAt, } if reqIter.Attributes.Description != nil { newItr.Description = reqIter.Attributes.Description } err = appl.Iterations().Create(ctx, &newItr) if err != nil { return jsonapi.JSONErrorResponse(ctx, err) } res := &app.IterationSingle{ Data: ConvertIteration(ctx.RequestData, &newItr), } ctx.ResponseData.Header().Set("Location", rest.AbsoluteURL(ctx.RequestData, app.IterationHref(res.Data.ID))) return ctx.Created(res) }) }
// Show runs the show action. func (c *CommentsController) Show(ctx *app.ShowCommentsContext) error { return application.Transactional(c.db, func(appl application.Application) error { c, err := appl.Comments().Load(ctx, ctx.CommentID) if err != nil { jerrors, _ := jsonapi.ErrorToJSONAPIErrors(goa.ErrUnauthorized(err.Error())) return ctx.NotFound(jerrors) } res := &app.CommentSingle{} res.Data = ConvertComment( ctx.RequestData, c, CommentIncludeParentWorkItem()) return ctx.OK(res) }) }
// Delete runs the delete action. func (c *SpaceController) Delete(ctx *app.DeleteSpaceContext) error { _, err := login.ContextIdentity(ctx) if err != nil { return jsonapi.JSONErrorResponse(ctx, goa.ErrUnauthorized(err.Error())) } id, err := satoriuuid.FromString(ctx.ID) if err != nil { return jsonapi.JSONErrorResponse(ctx, goa.ErrNotFound(err.Error())) } return application.Transactional(c.db, func(appl application.Application) error { err = appl.Spaces().Delete(ctx.Context, id) if err != nil { return jsonapi.JSONErrorResponse(ctx, err) } return ctx.OK([]byte{}) }) }
// CreateChild runs the create-child action. func (c *AreaController) CreateChild(ctx *app.CreateChildAreaContext) error { _, err := login.ContextIdentity(ctx) if err != nil { return jsonapi.JSONErrorResponse(ctx, goa.ErrUnauthorized(err.Error())) } parentID, err := uuid.FromString(ctx.ID) if err != nil { return jsonapi.JSONErrorResponse(ctx, goa.ErrNotFound(err.Error())) } return application.Transactional(c.db, func(appl application.Application) error { parent, err := appl.Areas().Load(ctx, parentID) if err != nil { return jsonapi.JSONErrorResponse(ctx, goa.ErrNotFound(err.Error())) } reqArea := ctx.Payload.Data if reqArea.Attributes.Name == nil { return jsonapi.JSONErrorResponse(ctx, errors.NewBadParameterError("data.attributes.name", nil).Expected("not nil")) } childPath := area.ConvertToLtreeFormat(parentID.String()) if parent.Path != "" { childPath = parent.Path + pathSepInDatabase + childPath } newArea := area.Area{ SpaceID: parent.SpaceID, Path: childPath, Name: *reqArea.Attributes.Name, } err = appl.Areas().Create(ctx, &newArea) if err != nil { return jsonapi.JSONErrorResponse(ctx, err) } res := &app.AreaSingle{ Data: ConvertArea(appl, ctx.RequestData, &newArea, addResolvedPath), } ctx.ResponseData.Header().Set("Location", rest.AbsoluteURL(ctx.RequestData, app.AreaHref(res.Data.ID))) return ctx.Created(res) }) }
// Generate obtain the access token from Keycloak for the test user func (c *LoginController) Generate(ctx *app.GenerateLoginContext) error { if !configuration.IsPostgresDeveloperModeEnabled() { log.Error(ctx, map[string]interface{}{ "method": "Generate", }, "Postgres developer mode not enabled") jerrors, _ := jsonapi.ErrorToJSONAPIErrors(goa.ErrUnauthorized("Postgres developer mode not enabled")) return ctx.Unauthorized(jerrors) } var scopes []account.Identity scopes = append(scopes, test.TestIdentity) scopes = append(scopes, test.TestObserverIdentity) client := &http.Client{Timeout: 10 * time.Second} username := configuration.GetKeycloakTestUserName() res, err := client.PostForm(configuration.GetKeycloakEndpointToken(), url.Values{ "client_id": {configuration.GetKeycloakClientID()}, "client_secret": {configuration.GetKeycloakSecret()}, "username": {username}, "password": {configuration.GetKeycloakTestUserSecret()}, "grant_type": {"password"}, }) if err != nil { return jsonapi.JSONErrorResponse(ctx, errors.NewInternalError("error when obtaining token "+err.Error())) } token, err := readToken(res, ctx) if err != nil { log.Error(ctx, map[string]interface{}{ "tokenEndpoint": res, "err": err, }, "Error when unmarshal json with access token") return jsonapi.JSONErrorResponse(ctx, e.Wrap(err, "Error when unmarshal json with access token")) } var tokens app.AuthTokenCollection tokens = append(tokens, &app.AuthToken{Token: token}) // Creates the testuser user and identity if they don't yet exist c.auth.CreateKeycloakUser(*token.AccessToken, ctx) return ctx.OK(tokens) }
// Create runs the create action. func (c *SpaceAreasController) Create(ctx *app.CreateSpaceAreasContext) error { _, err := login.ContextIdentity(ctx) if err != nil { return jsonapi.JSONErrorResponse(ctx, goa.ErrUnauthorized(err.Error())) } spaceID, err := uuid.FromString(ctx.ID) if err != nil { return jsonapi.JSONErrorResponse(ctx, goa.ErrNotFound(err.Error())) } // Validate Request if ctx.Payload.Data == nil { return jsonapi.JSONErrorResponse(ctx, errors.NewBadParameterError("data", nil).Expected("not nil")) } reqIter := ctx.Payload.Data if reqIter.Attributes.Name == nil { return jsonapi.JSONErrorResponse(ctx, errors.NewBadParameterError("data.attributes.name", nil).Expected("not nil")) } return application.Transactional(c.db, func(appl application.Application) error { _, err = appl.Spaces().Load(ctx, spaceID) if err != nil { return jsonapi.JSONErrorResponse(ctx, goa.ErrNotFound(err.Error())) } newArea := area.Area{ SpaceID: spaceID, Name: *reqIter.Attributes.Name, } err = appl.Areas().Create(ctx, &newArea) if err != nil { return jsonapi.JSONErrorResponse(ctx, err) } res := &app.AreaSingle{ Data: ConvertArea(appl, ctx.RequestData, &newArea, addResolvedPath), } ctx.ResponseData.Header().Set("Location", rest.AbsoluteURL(ctx.RequestData, app.AreaHref(res.Data.ID))) return ctx.Created(res) }) }
// Update runs the update action. func (c *SpaceController) Update(ctx *app.UpdateSpaceContext) error { _, err := login.ContextIdentity(ctx) if err != nil { return jsonapi.JSONErrorResponse(ctx, goa.ErrUnauthorized(err.Error())) } id, err := satoriuuid.FromString(ctx.ID) if err != nil { return jsonapi.JSONErrorResponse(ctx, goa.ErrNotFound(err.Error())) } err = validateUpdateSpace(ctx) if err != nil { return jsonapi.JSONErrorResponse(ctx, err) } return application.Transactional(c.db, func(appl application.Application) error { s, err := appl.Spaces().Load(ctx.Context, id) if err != nil { return jsonapi.JSONErrorResponse(ctx, err) } s.Version = *ctx.Payload.Data.Attributes.Version if ctx.Payload.Data.Attributes.Name != nil { s.Name = *ctx.Payload.Data.Attributes.Name } if ctx.Payload.Data.Attributes.Description != nil { s.Description = *ctx.Payload.Data.Attributes.Description } s, err = appl.Spaces().Save(ctx.Context, s) if err != nil { return jsonapi.JSONErrorResponse(ctx, err) } response := app.SpaceSingle{ Data: ConvertSpace(ctx.RequestData, s), } return ctx.OK(&response) }) }
// Create does POST workitem func (c *WorkitemController) Create(ctx *app.CreateWorkitemContext) error { currentUser, err := login.ContextIdentity(ctx) if err != nil { jerrors, _ := jsonapi.ErrorToJSONAPIErrors(goa.ErrUnauthorized(err.Error())) return ctx.Unauthorized(jerrors) } var wit *string if ctx.Payload.Data != nil && ctx.Payload.Data.Relationships != nil && ctx.Payload.Data.Relationships.BaseType != nil && ctx.Payload.Data.Relationships.BaseType.Data != nil { wit = &ctx.Payload.Data.Relationships.BaseType.Data.ID } if wit == nil { // TODO Figure out path source etc. Should be a required relation return jsonapi.JSONErrorResponse(ctx, errors.NewBadParameterError("Data.Relationships.BaseType.Data.ID", err)) } wi := app.WorkItem{ Fields: make(map[string]interface{}), } return application.Transactional(c.db, func(appl application.Application) error { err := ConvertJSONAPIToWorkItem(appl, *ctx.Payload.Data, &wi) if err != nil { return jsonapi.JSONErrorResponse(ctx, errs.Wrap(err, fmt.Sprintf("Error creating work item"))) } wi, err := appl.WorkItems().Create(ctx, *wit, wi.Fields, currentUser) if err != nil { return jsonapi.JSONErrorResponse(ctx, errs.Wrap(err, fmt.Sprintf("Error creating work item"))) } wi2 := ConvertWorkItem(ctx.RequestData, wi) resp := &app.WorkItem2Single{ Data: wi2, Links: &app.WorkItemLinks{ Self: buildAbsoluteURL(ctx.RequestData), }, } ctx.ResponseData.Header().Set("Location", app.WorkitemHref(wi2.ID)) return ctx.Created(resp) }) }
// Delete does DELETE comment func (c *CommentsController) Delete(ctx *app.DeleteCommentsContext) error { identity, err := login.ContextIdentity(ctx) if err != nil { return jsonapi.JSONErrorResponse(ctx, goa.ErrUnauthorized(err.Error())) } return application.Transactional(c.db, func(appl application.Application) error { cm, err := appl.Comments().Load(ctx.Context, ctx.CommentID) if err != nil { return jsonapi.JSONErrorResponse(ctx, err) } if identity != cm.CreatedBy.String() { // need to use the goa.NewErrorClass() func as there is no native support for 403 in goa // and it is not planned to be supported yet: https://github.com/goadesign/goa/pull/1030 return jsonapi.JSONErrorResponse(ctx, goa.NewErrorClass("forbidden", 403)("User is not the comment author")) } err = appl.Comments().Delete(ctx.Context, cm.ID) if err != nil { return jsonapi.JSONErrorResponse(ctx, err) } return ctx.OK([]byte{}) }) }
// Perform performs authenticatin func (keycloak *KeycloakOAuthProvider) Perform(ctx *app.AuthorizeLoginContext) error { state := ctx.Params.Get("state") code := ctx.Params.Get("code") referer := ctx.RequestData.Header.Get("Referer") if code != "" { // After redirect from oauth provider // validate known state var knownReferer string defer func() { delete(stateReferer, state) }() knownReferer = stateReferer[state] if state == "" || knownReferer == "" { log.Error(ctx, map[string]interface{}{ "state": state, "referer": knownReferer, }, "state or known referer was empty") jerrors, _ := jsonapi.ErrorToJSONAPIErrors(goa.ErrUnauthorized("State or known referer was empty")) return ctx.Unauthorized(jerrors) } keycloakToken, err := keycloak.config.Exchange(ctx, code) if err != nil || keycloakToken.AccessToken == "" { log.Error(ctx, map[string]interface{}{ "code": code, "err": err, }, "keycloak exchange operation failed") return redirectWithError(ctx, knownReferer, InvalidCodeError) } _, _, err = keycloak.CreateKeycloakUser(keycloakToken.AccessToken, ctx) if err != nil { log.Error(ctx, map[string]interface{}{ "token": keycloakToken.AccessToken, "err": err, }, "failed to create a user and KeyCloak identity using the access token") return redirectWithError(ctx, knownReferer, err.Error()) } referelURL, err := url.Parse(knownReferer) if err != nil { return redirectWithError(ctx, knownReferer, err.Error()) } err = encodeToken(referelURL, keycloakToken) if err != nil { return redirectWithError(ctx, knownReferer, err.Error()) } ctx.ResponseData.Header().Set("Location", referelURL.String()) return ctx.TemporaryRedirect() } // First time access, redirect to oauth provider // store referer id to state for redirect later log.Info(ctx, map[string]interface{}{ "pkg": "login", "referer": referer, }, "Got Request from!") state = uuid.NewV4().String() mapLock.Lock() defer mapLock.Unlock() stateReferer[state] = referer keycloak.config.RedirectURL = rest.AbsoluteURL(ctx.RequestData, "/api/login/authorize") redirectURL := keycloak.config.AuthCodeURL(state, oauth2.AccessTypeOnline) ctx.ResponseData.Header().Set("Location", redirectURL) return ctx.TemporaryRedirect() }