Skip to content

Commit

Permalink
Merge pull request #362 from factly/feat/create-identity
Browse files Browse the repository at this point in the history
create identity and organisation with admin endpoint
  • Loading branch information
shreeharsha-factly authored Apr 20, 2023
2 parents 676bcef + aae91a7 commit 7cfeb2e
Show file tree
Hide file tree
Showing 10 changed files with 537 additions and 0 deletions.
130 changes: 130 additions & 0 deletions server/action/admin/organisation/create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
package organisation

import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
"strconv"

"github.com/factly/kavach-server/model"
keto "github.com/factly/kavach-server/util/keto/relationTuple"
"github.com/factly/x/errorx"
"github.com/factly/x/loggerx"
"github.com/factly/x/renderx"
"github.com/factly/x/validationx"
)

type organisation struct {
Title string `json:"title" validate:"required"`
Slug string `json:"slug"`
Description string `json:"description"`
FeaturedMediumID uint `json:"featured_medium_id"`
IsIndividual bool `json:"is_individual"`
UserID uint `json:"user_id"`
}

// create - Create organisation
// @Summary Create organisation
// @Description Create organisation
// @Tags Organisation
// @ID add-organisation
// @Consume json
// @Produce json
// @Param X-User header string true "User ID"
// @Param Organisation body organisation true "Organisation Object"
// @Success 201 {object} orgWithRole
// @Failure 400 {array} string
// @Router /organisations [post]
func create(w http.ResponseWriter, r *http.Request) {
userID, err := strconv.Atoi(r.Header.Get("X-User"))
if err != nil {
errorx.Render(w, errorx.Parser(errorx.InvalidID()))
return
}

org := &organisation{}

err = json.NewDecoder(r.Body).Decode(&org)
if err != nil {
loggerx.Error(err)
errorx.Render(w, errorx.Parser(errorx.DecodeError()))
return
}

validationError := validationx.Check(org)
if validationError != nil {
loggerx.Error(errors.New("validation error"))
errorx.Render(w, validationError)
return
}

mediumID := &org.FeaturedMediumID
if org.FeaturedMediumID == 0 {
mediumID = nil
}

tx := model.DB.WithContext(context.WithValue(r.Context(), userContext, userID)).Begin()

organisation := &model.Organisation{
Title: org.Title,
Slug: org.Slug,
Description: org.Description,
FeaturedMediumID: mediumID,
IsIndividual: org.IsIndividual,
}

err = tx.Model(&model.Organisation{}).Create(&organisation).Error

if err != nil {
tx.Rollback()
loggerx.Error(err)
errorx.Render(w, errorx.Parser(errorx.DBError()))
return
}

tx.Model(&model.Organisation{}).Preload("Medium").First(&organisation)

permission := model.OrganisationUser{}
permission.OrganisationID = uint(organisation.ID)
permission.UserID = uint(org.UserID)
permission.Role = "owner"

err = tx.Model(&model.OrganisationUser{}).Create(&permission).Error

if err != nil {
tx.Rollback()
loggerx.Error(err)
errorx.Render(w, errorx.Parser(errorx.DBError()))
return
}

// creating the organisation-role: owner, on the keto api
tuple := &model.KetoRelationTupleWithSubjectID{
KetoSubjectSet: model.KetoSubjectSet{
Namespace: "organisations",
Object: fmt.Sprintf("org:%d", organisation.ID),
Relation: "owner",
},
SubjectID: fmt.Sprintf("%d", org.UserID),
}

err = keto.CreateRelationTupleWithSubjectID(tuple)
if err != nil {
tx.Rollback()
loggerx.Error(err)
errorx.Render(w, errorx.Parser(errorx.InternalServerError()))
return
}

result := model.Organisation{}

result = *organisation

result.OrganisationUsers = []model.OrganisationUser{permission}

tx.Commit()

renderx.JSON(w, http.StatusCreated, result)
}
46 changes: 46 additions & 0 deletions server/action/admin/organisation/details.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package organisation

import (
"net/http"
"strconv"

"github.com/factly/kavach-server/model"
"github.com/factly/x/errorx"
"github.com/factly/x/loggerx"
"github.com/factly/x/renderx"
"github.com/go-chi/chi"
)

// details - Get organisation by id
// @Summary Show a organisation by id
// @Description Get organisation by ID
// @Tags Organisation
// @ID get-organisation-by-id
// @Produce json
// @Param X-User header string true "User ID"
// @Param organisation_id path string true "Organisation ID"
// @Success 200 {object} orgWithRole
// @Router /organisations/{organisation_id} [get]
func details(w http.ResponseWriter, r *http.Request) {
organisationID := chi.URLParam(r, "organisation_id")
id, err := strconv.Atoi(organisationID)

if err != nil {
loggerx.Error(err)
errorx.Render(w, errorx.Parser(errorx.InvalidID()))
return
}

organisation := &model.Organisation{}
organisation.ID = uint(id)

err = model.DB.Model(&model.Organisation{}).Preload("OrganisationUsers").Preload("OrganisationUsers.User").First(&organisation).Error

if err != nil {
loggerx.Error(err)
errorx.Render(w, errorx.Parser(errorx.RecordNotFound()))
return
}

renderx.JSON(w, http.StatusOK, organisation)
}
34 changes: 34 additions & 0 deletions server/action/admin/organisation/list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package organisation

import (
"net/http"

"github.com/factly/kavach-server/model"
"github.com/factly/x/errorx"
"github.com/factly/x/loggerx"
"github.com/factly/x/paginationx"
"github.com/factly/x/renderx"
)

func list(w http.ResponseWriter, r *http.Request) {

orgIDs := r.URL.Query()["id"]

offset, limit := paginationx.Parse(r.URL.Query())

if len(orgIDs) > 0 {
offset = 0
limit = len(orgIDs)
}

res := make([]model.Organisation, 0)

err := model.DB.Model(&model.Organisation{}).Where(orgIDs).Offset(offset).Limit(limit).Find(&res).Error
if err != nil {
loggerx.Error(err)
errorx.Render(w, errorx.Parser(errorx.DBError()))
return
}

renderx.JSON(w, http.StatusOK, res)
}
22 changes: 22 additions & 0 deletions server/action/admin/organisation/routes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package organisation

import (
"github.com/factly/kavach-server/model"
"github.com/go-chi/chi"
)

var userContext model.ContextKey = "organisation_user"

// Router organisation
func Router() chi.Router {
r := chi.NewRouter()

r.Post("/", create)
r.Get("/", list)
r.Route("/{organisation_id}", func(r chi.Router) {
r.Put("/", update)
r.Get("/", details)
})

return r
}
104 changes: 104 additions & 0 deletions server/action/admin/organisation/update.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package organisation

import (
"context"
"encoding/json"
"net/http"
"strconv"

"github.com/factly/kavach-server/model"
"github.com/factly/x/errorx"
"github.com/factly/x/loggerx"
"github.com/factly/x/renderx"
"github.com/go-chi/chi"
)

func update(w http.ResponseWriter, r *http.Request) {
req := organisation{}
err := json.NewDecoder(r.Body).Decode(&req)
if err != nil {
loggerx.Error(err)
errorx.Render(w, errorx.Parser(errorx.DecodeError()))
return
}

organisationID := chi.URLParam(r, "organisation_id")
orgID, err := strconv.Atoi(organisationID)

if err != nil {
loggerx.Error(err)
errorx.Render(w, errorx.Parser(errorx.InvalidID()))
return
}

hostID, err := strconv.Atoi(r.Header.Get("X-User"))
if err != nil {
loggerx.Error(err)
errorx.Render(w, errorx.Parser(errorx.InvalidID()))
return
}

organisation := &model.Organisation{}
organisation.ID = uint(orgID)

// check record exists or not
err = model.DB.First(&organisation).Error
if err != nil {
loggerx.Error(err)
errorx.Render(w, errorx.Parser(errorx.RecordNotFound()))
return
}

tx := model.DB.WithContext(context.WithValue(r.Context(), userContext, hostID)).Begin()

mediumID := &req.FeaturedMediumID
organisation.FeaturedMediumID = &req.FeaturedMediumID
if req.FeaturedMediumID == 0 {
err = tx.Model(&organisation).Updates(map[string]interface{}{"featured_medium_id": nil}).First(&organisation).Error
mediumID = nil
if err != nil {
tx.Rollback()
loggerx.Error(err)
errorx.Render(w, errorx.Parser(errorx.DBError()))
return
}
}

// update
updateMap := map[string]interface{}{
"updated_by_id": uint(hostID),
"title": req.Title,
"slug": req.Slug,
"description": req.Description,
"featured_medium_id": mediumID,
"is_individual": req.IsIndividual,
}

err = tx.Model(&organisation).Updates(&updateMap).Preload("Medium").First(&organisation).Error

if err != nil {
tx.Rollback()
loggerx.Error(err)
errorx.Render(w, errorx.Parser(errorx.DBError()))
return
}
// fetching permissions of the user
permission := &model.OrganisationUser{}
err = tx.Model(&model.OrganisationUser{}).Where(&model.OrganisationUser{
OrganisationID: uint(orgID),
}).First(permission).Error

if err != nil {
tx.Rollback()
loggerx.Error(err)
errorx.Render(w, errorx.Parser(errorx.RecordNotFound()))
return
}
result := model.Organisation{}
result = *organisation
result.OrganisationUsers = []model.OrganisationUser{*permission}

tx.Commit()

renderx.JSON(w, http.StatusOK, result)
}
38 changes: 38 additions & 0 deletions server/action/admin/route.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package admin

import (
"log"
"net/http"

"github.com/factly/kavach-server/action/admin/organisation"
"github.com/factly/kavach-server/action/admin/user"
"github.com/go-chi/chi"
"github.com/spf13/viper"
)

// Router organisation
func AdminRouter() chi.Router {
r := chi.NewRouter()

r.With(CheckMasterKey).Route("/", func(r chi.Router) {
r.Mount("/users", user.Router())
r.Mount("/organisations", organisation.Router())
})

return r
}

// CheckMasterKey check X-User in header
func CheckMasterKey(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
requestMasterKey := r.Header.Get("X-KAVACH-MASTER-KEY")

log.Println(requestMasterKey)
log.Println(viper.GetString("master_key"))
if requestMasterKey != viper.GetString("master_key") {
w.WriteHeader(http.StatusUnauthorized)
return
}
h.ServeHTTP(w, r)
})
}
Loading

0 comments on commit 7cfeb2e

Please sign in to comment.