// Delete handles DELETE func (ctl *MenuController) Delete(c *models.Context, siteID int64) { // Start :: Auth site, status, err := models.GetSite(siteID) if err != nil { c.RespondWithErrorDetail(err, status) return } // Use the user ID to check, since the current context is a different site (the root site) // than the site the owner profile is associated with. owner, status, err := models.GetProfileSummary(site.ID, site.OwnedByID) if err != nil { c.RespondWithErrorDetail(err, status) return } if owner.UserID != c.Auth.UserID { c.RespondWithErrorMessage(h.NoAuthMessage, http.StatusForbidden) return } // End :: Auth status, err = models.DeleteMenu(siteID) if err != nil { c.RespondWithErrorDetail(err, status) return } c.RespondWithOK() }
func (wc *WhoAmIController) Read(c *models.Context) { if c.Request.Method != "GET" { c.RespondWithNotImplemented() return } if c.Auth.UserID < 0 { c.RespondWithErrorMessage( "Bad access token supplied", http.StatusForbidden, ) return } if c.Auth.UserID == 0 { c.RespondWithErrorMessage( "You must be authenticated to ask 'who am I?'", http.StatusForbidden, ) return } m, status, err := models.GetProfileSummary(c.Site.ID, c.Auth.ProfileID) if err != nil { if status == http.StatusNotFound { c.RespondWithErrorMessage( "You must create a user profile for this site at api/v1/profiles/", http.StatusNotFound, ) return } c.RespondWithErrorMessage( fmt.Sprintf("Could not retrieve profile: %v", err.Error()), http.StatusInternalServerError, ) return } location := fmt.Sprintf( "%s/%d", h.APITypeProfile, m.ID, ) if c.Auth.ProfileID > 0 && c.Auth.Method == "query" { u, _ := url.Parse(location) qs := u.Query() qs.Del("access_token") qs.Add("access_token", c.Auth.AccessToken.TokenValue) u.RawQuery = qs.Encode() location = u.String() } c.ResponseWriter.Header().Set("Location", location) c.RespondWithStatus(307) }
// Update handles PUT func (ctl *SiteController) Update(c *models.Context) { _, _, itemID, status, err := c.GetItemTypeAndItemID() if err != nil { c.RespondWithErrorDetail(err, status) return } m, status, err := models.GetSite(itemID) if err != nil { c.RespondWithErrorDetail(err, status) return } // Use the user ID to check, since the current context is a different site (the root site) // than the site the owner profile is associated with. owner, status, err := models.GetProfileSummary(m.ID, m.OwnedByID) if err != nil { c.RespondWithErrorDetail(err, status) return } if owner.UserID != c.Auth.UserID { c.RespondWithErrorMessage( fmt.Sprintf("You must be the owner of the site to update it"), http.StatusForbidden, ) return } err = c.Fill(&m) if err != nil { c.RespondWithErrorMessage( fmt.Sprintf("The post data is invalid: %v", err.Error()), http.StatusBadRequest, ) return } status, err = m.Update() if err != nil { c.RespondWithErrorDetail(err, status) return } audit.Replace( c.Site.ID, h.ItemTypes[h.ItemTypeSite], m.ID, c.Auth.ProfileID, time.Now(), c.IP, ) c.RespondWithSeeOther(fmt.Sprintf("%s/%d", h.APITypeSite, m.ID)) }
// Read handles GET func (ctl *SiteCheckController) Read(c *models.Context) { _, _, itemID, status, err := c.GetItemTypeAndItemID() if err != nil { c.RespondWithErrorDetail(err, status) return } m, status, err := models.GetSite(itemID) if err != nil { c.RespondWithErrorDetail(err, status) return } // Use the user ID to check, since the current context is a different site (the root site) // than the site the owner profile is associated with. owner, status, err := models.GetProfileSummary(m.ID, m.OwnedByID) if err != nil { c.RespondWithErrorDetail(err, status) return } if owner.UserID != c.Auth.UserID { c.RespondWithErrorMessage( fmt.Sprintf("You must be the owner of the site to view its status"), http.StatusForbidden, ) return } siteHealth, status, err := models.CheckSiteHealth(m) if err != nil { c.RespondWithErrorMessage( fmt.Sprintf("Error checking site status: %s", err.Error()), status, ) return } glog.Infof("Got site health: %+v\n", siteHealth) c.RespondWithData(siteHealth) }
// Update handles PUT func (ctl *MenuController) Update(c *models.Context, siteID int64) { ems := []h.LinkType{} // Start :: Auth site, status, err := models.GetSite(siteID) if err != nil { c.RespondWithErrorDetail(err, status) return } // Use the user ID to check, since the current context is a different site (the root site) // than the site the owner profile is associated with. owner, status, err := models.GetProfileSummary(site.ID, site.OwnedByID) if err != nil { c.RespondWithErrorDetail(err, status) return } if owner.UserID != c.Auth.UserID { c.RespondWithErrorMessage(h.NoAuthMessage, http.StatusForbidden) return } // End :: Auth err = c.Fill(&ems) if err != nil { c.RespondWithErrorMessage( fmt.Sprintf("The post data is invalid: %v", err.Error()), http.StatusBadRequest, ) return } status, err = models.UpdateMenu(siteID, ems) if err != nil { c.RespondWithErrorDetail(err, status) return } c.RespondWithOK() }
// Update handles PUT func (ctl *AttendeeController) Update(c *models.Context) { // Verify ID is a positive integer eventID, err := strconv.ParseInt(c.RouteVars["event_id"], 10, 64) if err != nil { c.RespondWithErrorMessage( fmt.Sprintf("The supplied event_id ('%s') is not a number.", c.RouteVars["event_id"]), http.StatusBadRequest, ) return } m := models.AttendeeType{} err = c.Fill(&m) if err != nil { c.RespondWithErrorMessage( fmt.Sprintf("The post data is invalid: %v", err.Error()), http.StatusBadRequest, ) return } // Start Authorisation perms := models.GetPermission( models.MakeAuthorisationContext( c, 0, h.ItemTypes[h.ItemTypeEvent], eventID), ) if !perms.CanUpdate { c.RespondWithErrorMessage(h.NoAuthMessage, http.StatusForbidden) return } if perms.IsOwner || perms.IsModerator || perms.IsSiteOwner { if m.ProfileID != c.Auth.ProfileID && m.RSVP == "yes" { c.RespondWithErrorMessage(h.NoAuthMessage, http.StatusForbidden) return } } else { if m.ProfileID != c.Auth.ProfileID { c.RespondWithErrorMessage(h.NoAuthMessage, http.StatusForbidden) return } } _, status, err := models.GetProfileSummary(c.Site.ID, m.ProfileID) if err != nil { c.RespondWithErrorMessage(h.NoAuthMessage, status) return } // End Authorisation // Populate where applicable from auth and context t := time.Now() m.EventID = eventID m.Meta.CreatedByID = c.Auth.ProfileID m.Meta.Created = t m.Meta.EditedByNullable = sql.NullInt64{Int64: c.Auth.ProfileID, Valid: true} m.Meta.EditedNullable = pq.NullTime{Time: t, Valid: true} status, err = m.Update(c.Site.ID) if err != nil { c.RespondWithErrorDetail(err, status) return } if m.RSVP == "yes" { go models.SendUpdatesForNewAttendeeInAnEvent(c.Site.ID, m) } audit.Replace( c.Site.ID, h.ItemTypes[h.ItemTypeAttendee], m.ID, c.Auth.ProfileID, time.Now(), c.IP, ) c.RespondWithSeeOther( fmt.Sprintf("%s/%d", fmt.Sprintf(h.APITypeAttendee, m.EventID), m.ProfileID), ) }
// ParseItemInfo determines what this attachment is attached to func ParseItemInfo(c *models.Context) (int64, int64, models.PermissionType, int, error) { var itemTypeID int64 var itemID int64 if c.RouteVars["profile_id"] != "" { profileID, err := strconv.ParseInt(c.RouteVars["profile_id"], 10, 64) if err != nil { return 0, 0, models.PermissionType{}, http.StatusBadRequest, fmt.Errorf( "The supplied profile ID ('%s') is not a number", c.RouteVars["profile_id"], ) } _, status, err := models.GetProfileSummary(c.Site.ID, profileID) if err != nil { if status == http.StatusNotFound { return 0, 0, models.PermissionType{}, http.StatusBadRequest, fmt.Errorf( "Profile with ID ('%d') does not exist", profileID, ) } return 0, 0, models.PermissionType{}, http.StatusBadRequest, err } itemID = profileID itemTypeID = h.ItemTypes[h.ItemTypeProfile] } else if c.RouteVars["comment_id"] != "" { commentID, err := strconv.ParseInt(c.RouteVars["comment_id"], 10, 64) if err != nil { return 0, 0, models.PermissionType{}, http.StatusBadRequest, fmt.Errorf( "The supplied comment ID ('%s') is not a number", c.RouteVars["comment_id"], ) } _, status, err := models.GetCommentSummary(c.Site.ID, commentID) if err != nil { if status == http.StatusNotFound { return 0, 0, models.PermissionType{}, http.StatusBadRequest, fmt.Errorf( "Comment with ID ('%d') does not exist", commentID, ) } return 0, 0, models.PermissionType{}, http.StatusBadRequest, err } itemID = commentID itemTypeID = h.ItemTypes[h.ItemTypeComment] } else { return 0, 0, models.PermissionType{}, http.StatusBadRequest, fmt.Errorf("You must supply a profile_id or comment_id as a RouteVar") } perms := models.GetPermission( models.MakeAuthorisationContext( c, 0, itemTypeID, itemID), ) return itemTypeID, itemID, perms, http.StatusOK, nil }
// Create handles POST func (ctl *AttachmentsController) Create(c *models.Context) { attachment := models.AttachmentType{} err := c.Fill(&attachment) if err != nil { c.RespondWithErrorMessage( fmt.Sprintf("The post data is invalid: %v", err.Error()), http.StatusBadRequest, ) return } if attachment.FileHash == "" { c.RespondWithErrorMessage( "You must supply a file hash", http.StatusBadRequest, ) return } // Check that the file hash has a corresponding attachment_meta record metadata, status, err := models.GetMetadata(attachment.FileHash) if err != nil { if status == http.StatusNotFound { c.RespondWithErrorMessage( fmt.Sprintf("File does not have a metadata record"), http.StatusBadRequest, ) return } c.RespondWithErrorMessage( fmt.Sprintf("Could not retrieve metadata: %v", err.Error()), http.StatusBadRequest, ) return } attachment.AttachmentMetaID = metadata.AttachmentMetaID // Determine whether this is an attachment to a profile or comment, and if the // user is authorised to do so pathPrefix := "" if c.RouteVars["profile_id"] != "" { profileID, err := strconv.ParseInt(c.RouteVars["profile_id"], 10, 64) if err != nil { c.RespondWithErrorMessage( fmt.Sprintf("The supplied profile ID ('%s') is not a number.", c.RouteVars["profile_id"]), http.StatusBadRequest, ) return } _, status, err := models.GetProfileSummary(c.Site.ID, profileID) if err != nil { if status == http.StatusNotFound { c.RespondWithErrorMessage( fmt.Sprintf("Profile with ID ('%d') does not exist.", profileID), http.StatusBadRequest, ) return } c.RespondWithErrorMessage( fmt.Sprintf("Could not retrieve profile: %v.", err.Error()), http.StatusInternalServerError, ) return } perms := models.GetPermission( models.MakeAuthorisationContext( c, 0, h.ItemTypes[h.ItemTypeProfile], profileID), ) if !perms.CanCreate && !perms.CanUpdate { c.RespondWithErrorMessage(h.NoAuthMessage, http.StatusForbidden) return } attachment.ItemID = profileID attachment.ItemTypeID = h.ItemTypes[h.ItemTypeProfile] pathPrefix = h.APITypeProfile } else if c.RouteVars["comment_id"] != "" { commentID, err := strconv.ParseInt(c.RouteVars["comment_id"], 10, 64) if err != nil { c.RespondWithErrorMessage( fmt.Sprintf("The supplied comment ID ('%s') is not a number.", c.RouteVars["comment_id"]), http.StatusBadRequest, ) return } _, status, err := models.GetCommentSummary(c.Site.ID, commentID) if err != nil { if status == http.StatusNotFound { c.RespondWithErrorMessage( fmt.Sprintf("Comment with ID ('%d') does not exist.", commentID), http.StatusBadRequest, ) return } c.RespondWithErrorMessage( fmt.Sprintf("Could not retrieve comment: %v.", err.Error()), http.StatusInternalServerError, ) return } perms := models.GetPermission( models.MakeAuthorisationContext( c, 0, h.ItemTypes[h.ItemTypeComment], commentID), ) if !perms.CanCreate && !perms.CanUpdate { c.RespondWithErrorMessage(h.NoAuthMessage, http.StatusForbidden) return } if metadata.FileSize > 3145728 { c.RespondWithErrorMessage(fmt.Sprintf("File size must be under 3 megabytes"), http.StatusBadRequest) return } attachment.ItemID = commentID attachment.ItemTypeID = h.ItemTypes[h.ItemTypeComment] pathPrefix = h.APITypeComment } else { c.RespondWithErrorMessage( "You must supply a profile_id or comment_id as a RouteVar", http.StatusBadRequest, ) return } // Check that this file hasn't already been attached to this item oldattachment := models.AttachmentType{} oldattachment, status, err = models.GetAttachment( attachment.ItemTypeID, attachment.ItemID, attachment.FileHash, false, ) if err != nil && status != http.StatusNotFound { c.RespondWithErrorMessage( fmt.Sprintf("An error occurred when checking the attachment: %v", err.Error()), http.StatusInternalServerError, ) return } if status != http.StatusNotFound && attachment.ItemTypeID != h.ItemTypes[h.ItemTypeProfile] { c.RespondWithSeeOther( fmt.Sprintf("%s/%d/%s", pathPrefix, oldattachment.ItemID, h.APITypeAttachment), ) return } if status == http.StatusNotFound { // Update attach count on attachment_meta metadata.AttachCount++ status, err = metadata.Update() if err != nil { c.RespondWithErrorDetail(err, status) return } attachment.ProfileID = c.Auth.ProfileID attachment.Created = time.Now() status, err = attachment.Insert() if err != nil { c.RespondWithErrorDetail(err, status) return } } else { //already exists, need to update it and pull back the attachmentId attachment = oldattachment attachment.Created = time.Now() status, err = attachment.Update() if err != nil { c.RespondWithErrorDetail(err, status) return } } // If attaching to a profile, update the profile with new avatar URL if attachment.ItemTypeID == h.ItemTypes[h.ItemTypeProfile] { profile, _, err := models.GetProfile(c.Site.ID, attachment.ItemID) if err != nil { c.RespondWithErrorDetail(err, status) return } filePath := metadata.FileHash if metadata.FileExt != "" { filePath += `.` + metadata.FileExt } profile.AvatarURLNullable = sql.NullString{ String: fmt.Sprintf("%s/%s", h.APITypeFile, filePath), Valid: true, } profile.AvatarIDNullable = sql.NullInt64{ Int64: attachment.AttachmentID, Valid: true, } status, err = profile.Update() if err != nil { c.RespondWithErrorMessage( fmt.Sprintf("Could not update profile with avatar: %v", err.Error()), status, ) return } } c.RespondWithSeeOther( fmt.Sprintf("%s/%d/%s", pathPrefix, attachment.ItemID, h.APITypeAttachment), ) }
// UpdateMany handles PUT on the collection func (ctl *AttendeesController) UpdateMany(c *models.Context) { // Verify event_id is a positive integer eventID, err := strconv.ParseInt(c.RouteVars["event_id"], 10, 64) if err != nil { glog.Errorln(err.Error()) c.RespondWithErrorMessage( fmt.Sprintf("The supplied event ID ('%s') is not a number.", c.RouteVars["event_id"]), http.StatusBadRequest, ) return } ems := []models.AttendeeType{} err = c.Fill(&ems) if err != nil { glog.Errorln(err.Error()) c.RespondWithErrorMessage( fmt.Sprintf("The post data is invalid: %v", err.Error()), http.StatusBadRequest, ) return } // Start : Authorisation perms := models.GetPermission( models.MakeAuthorisationContext( c, 0, h.ItemTypes[h.ItemTypeEvent], eventID), ) if !perms.CanCreate { c.RespondWithErrorDetail( e.New(c.Site.ID, c.Auth.ProfileID, "attendees.go::UpdateMany", e.NoCreate, "Not authorized to create attendee: CanCreate false"), http.StatusForbidden, ) return } // Everyone can set self to any status. Event/site owners can set people to any status apart from 'attending'. // Also check that profile exists on site. if perms.IsOwner || perms.IsModerator || perms.IsSiteOwner { for _, m := range ems { if m.ProfileID != c.Auth.ProfileID && m.RSVP == "yes" { c.RespondWithErrorMessage(h.NoAuthMessage, http.StatusForbidden) return } _, status, err := models.GetProfileSummary(c.Site.ID, m.ProfileID) if err != nil { c.RespondWithErrorMessage(h.NoAuthMessage, status) return } } } else { for _, m := range ems { if m.ProfileID != c.Auth.ProfileID { c.RespondWithErrorMessage(h.NoAuthMessage, http.StatusForbidden) return } _, status, err := models.GetProfileSummary(c.Site.ID, m.ProfileID) if err != nil { c.RespondWithErrorMessage(h.NoAuthMessage, status) return } } } // End : Authorisation t := time.Now() // Populate where applicable from auth and context for i := range ems { ems[i].EventID = eventID ems[i].Meta.CreatedByID = c.Auth.ProfileID ems[i].Meta.Created = t ems[i].Meta.EditedNullable = pq.NullTime{Time: t, Valid: true} ems[i].Meta.EditedByNullable = sql.NullInt64{Int64: c.Auth.ProfileID, Valid: true} } status, err := models.UpdateManyAttendees(c.Site.ID, ems) if err != nil { glog.Error(err) c.RespondWithErrorDetail(err, status) return } for _, m := range ems { if m.RSVP == "yes" { go models.SendUpdatesForNewAttendeeInAnEvent(c.Site.ID, m) // The new attendee should be following the event now go models.RegisterWatcher( m.ProfileID, h.UpdateTypes[h.UpdateTypeEventReminder], m.EventID, h.ItemTypes[h.ItemTypeEvent], c.Site.ID, ) } audit.Replace( c.Site.ID, h.ItemTypes[h.ItemTypeAttendee], m.ID, c.Auth.ProfileID, time.Now(), c.IP, ) } c.RespondWithOK() }