示例#1
0
// 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()
}
示例#2
0
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)
}
示例#3
0
// 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))
}
示例#4
0
// 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)
}
示例#5
0
// 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()
}
示例#6
0
// 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),
	)
}
示例#7
0
// 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
}
示例#8
0
// 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),
	)
}
示例#9
0
// 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()
}