project_handler.go 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. package api
  2. import (
  3. "encoding/json"
  4. "net/http"
  5. "strconv"
  6. "github.com/go-chi/chi"
  7. "github.com/porter-dev/porter/api/types"
  8. "github.com/porter-dev/porter/internal/forms"
  9. "github.com/porter-dev/porter/internal/models"
  10. )
  11. // Enumeration of user API error codes, represented as int64
  12. const (
  13. ErrProjectDecode ErrorCode = iota + 600
  14. ErrProjectValidateFields
  15. ErrProjectDataRead
  16. )
  17. // HandleCreateProject validates a project form entry, converts the project to a gorm
  18. // model, and saves the user to the database
  19. func (app *App) HandleCreateProject(w http.ResponseWriter, r *http.Request) {
  20. session, err := app.Store.Get(r, app.ServerConf.CookieName)
  21. if err != nil {
  22. http.Error(w, err.Error(), http.StatusInternalServerError)
  23. return
  24. }
  25. userID, _ := session.Values["user_id"].(uint)
  26. form := &forms.CreateProjectForm{}
  27. // decode from JSON to form value
  28. if err := json.NewDecoder(r.Body).Decode(form); err != nil {
  29. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  30. return
  31. }
  32. // validate the form
  33. if err := app.validator.Struct(form); err != nil {
  34. app.handleErrorFormValidation(err, ErrProjectValidateFields, w)
  35. return
  36. }
  37. // convert the form to a project model
  38. projModel, err := form.ToProject(app.Repo.Project())
  39. if err != nil {
  40. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  41. return
  42. }
  43. // handle write to the database
  44. projModel, err = app.Repo.Project().CreateProject(projModel)
  45. if err != nil {
  46. app.handleErrorDataWrite(err, w)
  47. return
  48. }
  49. // create a new Role with the user as the admin
  50. _, err = app.Repo.Project().CreateProjectRole(projModel, &models.Role{
  51. Role: types.Role{
  52. UserID: userID,
  53. ProjectID: projModel.ID,
  54. Kind: types.RoleAdmin,
  55. },
  56. })
  57. if err != nil {
  58. app.handleErrorDataWrite(err, w)
  59. return
  60. }
  61. app.Logger.Info().Msgf("New project created: %d", projModel.ID)
  62. w.WriteHeader(http.StatusCreated)
  63. projExt := projModel.ToProjectType()
  64. if err := json.NewEncoder(w).Encode(projExt); err != nil {
  65. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  66. return
  67. }
  68. }
  69. // HandleGetProjectRoles lists the roles available to the project. For now, these
  70. // roles are static.
  71. func (app *App) HandleGetProjectRoles(w http.ResponseWriter, r *http.Request) {
  72. roles := []string{models.RoleAdmin, models.RoleDeveloper, models.RoleViewer}
  73. w.WriteHeader(http.StatusOK)
  74. if err := json.NewEncoder(w).Encode(&roles); err != nil {
  75. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  76. return
  77. }
  78. }
  79. type Collaborator struct {
  80. ID uint `json:"id"`
  81. Kind string `json:"kind"`
  82. UserID uint `json:"user_id"`
  83. Email string `json:"email"`
  84. ProjectID uint `json:"project_id"`
  85. }
  86. // HandleListProjectCollaborators lists the collaborators in the project
  87. func (app *App) HandleListProjectCollaborators(w http.ResponseWriter, r *http.Request) {
  88. id, err := strconv.ParseUint(chi.URLParam(r, "project_id"), 0, 64)
  89. if err != nil || id == 0 {
  90. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  91. return
  92. }
  93. roles, err := app.Repo.Project().ListProjectRoles(uint(id))
  94. if err != nil {
  95. app.handleErrorRead(err, ErrProjectDataRead, w)
  96. return
  97. }
  98. res := make([]*Collaborator, 0)
  99. roleMap := make(map[uint]*models.Role)
  100. idArr := make([]uint, 0)
  101. for _, role := range roles {
  102. roleCp := role
  103. roleMap[role.UserID] = &roleCp
  104. idArr = append(idArr, role.UserID)
  105. }
  106. users, err := app.Repo.User().ListUsersByIDs(idArr)
  107. if err != nil {
  108. app.handleErrorRead(err, ErrProjectDataRead, w)
  109. return
  110. }
  111. for _, user := range users {
  112. res = append(res, &Collaborator{
  113. ID: roleMap[user.ID].ID,
  114. Kind: string(roleMap[user.ID].Kind),
  115. UserID: roleMap[user.ID].UserID,
  116. Email: user.Email,
  117. ProjectID: roleMap[user.ID].ProjectID,
  118. })
  119. }
  120. w.WriteHeader(http.StatusOK)
  121. if err := json.NewEncoder(w).Encode(res); err != nil {
  122. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  123. return
  124. }
  125. }
  126. // HandleReadProject returns an externalized Project (models.ProjectExternal)
  127. // based on an ID
  128. func (app *App) HandleReadProject(w http.ResponseWriter, r *http.Request) {
  129. id, err := strconv.ParseUint(chi.URLParam(r, "project_id"), 0, 64)
  130. if err != nil || id == 0 {
  131. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  132. return
  133. }
  134. proj, err := app.Repo.Project().ReadProject(uint(id))
  135. if err != nil {
  136. app.handleErrorRead(err, ErrProjectDataRead, w)
  137. return
  138. }
  139. projExt := proj.ToProjectType()
  140. w.WriteHeader(http.StatusOK)
  141. if err := json.NewEncoder(w).Encode(projExt); err != nil {
  142. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  143. return
  144. }
  145. }
  146. // HandleReadProjectPolicy returns the policy document given the current user
  147. func (app *App) HandleReadProjectPolicy(w http.ResponseWriter, r *http.Request) {
  148. id, err := strconv.ParseUint(chi.URLParam(r, "project_id"), 0, 64)
  149. if err != nil || id == 0 {
  150. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  151. return
  152. }
  153. userID, err := app.getUserIDFromRequest(r)
  154. if err != nil {
  155. app.handleErrorInternal(err, w)
  156. return
  157. }
  158. role, err := app.Repo.Project().ReadProjectRole(uint(id), userID)
  159. if err != nil {
  160. app.handleErrorRead(err, ErrProjectDataRead, w)
  161. return
  162. }
  163. // case on the role to get the policy document
  164. var policy types.Policy
  165. switch role.Kind {
  166. case types.RoleAdmin:
  167. policy = types.AdminPolicy
  168. case types.RoleDeveloper:
  169. policy = types.DeveloperPolicy
  170. case types.RoleViewer:
  171. policy = types.ViewerPolicy
  172. }
  173. if err := json.NewEncoder(w).Encode(policy); err != nil {
  174. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  175. return
  176. }
  177. }
  178. // HandleUpdateProjectRole updates a project role with a new "kind"
  179. func (app *App) HandleUpdateProjectRole(w http.ResponseWriter, r *http.Request) {
  180. id, err := strconv.ParseUint(chi.URLParam(r, "project_id"), 0, 64)
  181. if err != nil || id == 0 {
  182. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  183. return
  184. }
  185. userID, err := strconv.ParseUint(chi.URLParam(r, "user_id"), 0, 64)
  186. if err != nil || id == 0 {
  187. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  188. return
  189. }
  190. role, err := app.Repo.Project().ReadProjectRole(uint(id), uint(userID))
  191. if err != nil {
  192. http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
  193. return
  194. }
  195. form := &forms.UpdateProjectRoleForm{}
  196. // decode from JSON to form value
  197. if err := json.NewDecoder(r.Body).Decode(form); err != nil {
  198. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  199. return
  200. }
  201. role.Kind = types.RoleKind(form.Kind)
  202. role, err = app.Repo.Project().UpdateProjectRole(uint(id), role)
  203. if err != nil {
  204. app.handleErrorRead(err, ErrProjectDataRead, w)
  205. return
  206. }
  207. if err := json.NewEncoder(w).Encode(role.Externalize()); err != nil {
  208. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  209. return
  210. }
  211. }
  212. // HandleDeleteProject deletes a project from the db, reading from the project_id
  213. // in the URL param
  214. func (app *App) HandleDeleteProject(w http.ResponseWriter, r *http.Request) {
  215. id, err := strconv.ParseUint(chi.URLParam(r, "project_id"), 0, 64)
  216. if err != nil || id == 0 {
  217. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  218. return
  219. }
  220. proj, err := app.Repo.Project().ReadProject(uint(id))
  221. if err != nil {
  222. app.handleErrorRead(err, ErrProjectDataRead, w)
  223. return
  224. }
  225. proj, err = app.Repo.Project().DeleteProject(proj)
  226. if err != nil {
  227. app.handleErrorRead(err, ErrProjectDataRead, w)
  228. return
  229. }
  230. projExternal := proj.ToProjectType()
  231. w.WriteHeader(http.StatusOK)
  232. if err := json.NewEncoder(w).Encode(projExternal); err != nil {
  233. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  234. return
  235. }
  236. }
  237. // HandleDeleteProjectRole deletes a project role from the db, reading from the project_id
  238. // in the URL param
  239. func (app *App) HandleDeleteProjectRole(w http.ResponseWriter, r *http.Request) {
  240. id, err := strconv.ParseUint(chi.URLParam(r, "project_id"), 0, 64)
  241. if err != nil || id == 0 {
  242. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  243. return
  244. }
  245. userID, err := strconv.ParseUint(chi.URLParam(r, "user_id"), 0, 64)
  246. if err != nil || id == 0 {
  247. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  248. return
  249. }
  250. role, err := app.Repo.Project().ReadProjectRole(uint(id), uint(userID))
  251. if err != nil {
  252. http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
  253. return
  254. }
  255. role, err = app.Repo.Project().DeleteProjectRole(uint(id), uint(userID))
  256. if err != nil {
  257. app.handleErrorRead(err, ErrProjectDataRead, w)
  258. return
  259. }
  260. if err := json.NewEncoder(w).Encode(role.Externalize()); err != nil {
  261. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  262. return
  263. }
  264. }