func (m *MessagesController) innerList(ctx *gin.Context) (*tat.MessagesJSON, tat.User, tat.Topic, *tat.MessageCriteria, int, error) { var criteria = m.buildCriteria(ctx) if criteria.Limit <= 0 || criteria.Limit > 1000 { return nil, tat.User{}, tat.Topic{}, criteria, http.StatusBadRequest, fmt.Errorf("Please put a limit <= 50 for fetching message") } log.Debugf("criteria::---> %+v", criteria) out := &tat.MessagesJSON{} // we can't use NotLabel or NotTag with fulltree or onetree // this avoid potential wrong results associated with a short param limit if (criteria.NotLabel != "" || criteria.NotTag != "") && (criteria.TreeView == tat.TreeViewFullTree || criteria.TreeView == tat.TreeViewOneTree) && criteria.OnlyMsgRoot == "" { return out, tat.User{}, tat.Topic{}, criteria, http.StatusBadRequest, fmt.Errorf("You can't use fulltree or onetree with NotLabel or NotTag") } topicIn, err := GetParam(ctx, "topic") if err != nil { return out, tat.User{}, tat.Topic{}, criteria, http.StatusBadRequest, fmt.Errorf("Invalid topic") } criteria.Topic = topicIn // add / if search on topic // as topic is in path, it can't start with a / if criteria.Topic != "" && string(criteria.Topic[0]) != "/" { criteria.Topic = "/" + criteria.Topic } var user tat.User var e error if getCtxUsername(ctx) != "" { user, e = PreCheckUser(ctx) if e != nil { return out, tat.User{}, tat.Topic{}, criteria, http.StatusBadRequest, e } } topic, errt := topicDB.FindByTopic(criteria.Topic, true, false, false, &user) if errt != nil { topicCriteria := "" _, topicCriteria, err = checkDMTopic(ctx, criteria.Topic) if err != nil { return out, tat.User{}, tat.Topic{}, criteria, http.StatusBadRequest, fmt.Errorf("topic " + criteria.Topic + " does not exist or you have no read access on it") } // hack to get new created DM Topic topic, errt = topicDB.FindByTopic(topicCriteria, true, false, false, &user) if errt != nil { return out, tat.User{}, tat.Topic{}, criteria, http.StatusBadRequest, fmt.Errorf("topic " + criteria.Topic + " does not exist or you have no read access on it (2)") } criteria.Topic = topicCriteria } out.IsTopicRw, out.IsTopicAdmin = topicDB.GetUserRights(topic, &user) return out, user, *topic, criteria, -1, nil }
func (t *TopicsController) preCheckUserAdminOnTopic(ctx *gin.Context, topicName string) (*tat.Topic, error) { topic, errfind := topicDB.FindByTopic(topicName, true, false, false, nil) if errfind != nil { e := errors.New(errfind.Error()) return nil, e } if isTatAdmin(ctx) { // if Tat admin, ok return topic, nil } user, err := PreCheckUser(ctx) if err != nil { return nil, err } if !topicDB.IsUserAdmin(topic, &user) { e := fmt.Errorf("user %s is not admin on topic %s", user.Username, topic.Topic) ctx.JSON(http.StatusForbidden, gin.H{"error": e}) return nil, e } return topic, nil }
// MigrateMessagesForDedicatedTopic migrates all msg of a topic to a dedicated collection func (t *TopicsController) MigrateMessagesForDedicatedTopic(ctx *gin.Context) { slimit, e1 := GetParam(ctx, "limit") if e1 != nil { ctx.JSON(http.StatusBadRequest, gin.H{"error": e1.Error()}) return } limit, e2 := strconv.Atoi(slimit) if e2 != nil { ctx.JSON(http.StatusBadRequest, gin.H{"error": e2.Error()}) return } topicRequest, err := GetParam(ctx, "topic") if err != nil { ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } topic, errfind := topicDB.FindByTopic(topicRequest, true, false, false, nil) if errfind != nil { ctx.JSON(http.StatusInternalServerError, gin.H{"error": errfind.Error()}) return } nMigrate, err := messageDB.MigrateMessagesToDedicatedTopic(topic, limit) if err != nil { ctx.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Error after %d migrate, err:%s", nMigrate, err.Error())}) return } ctx.JSON(http.StatusOK, gin.H{"info": fmt.Sprintf("No error after migrate %d messages (%d asked for migrate)", nMigrate, limit)}) }
func (t *TopicsController) innerOneTopic(ctx *gin.Context, topicRequest string) (*tat.TopicJSON, *tat.User, int, error) { var user = tat.User{} found, err := userDB.FindByUsername(&user, getCtxUsername(ctx)) if !found { return nil, nil, http.StatusInternalServerError, fmt.Errorf("User unknown") } else if err != nil { return nil, nil, http.StatusInternalServerError, fmt.Errorf("Error while fetching user.") } topic, errfind := topicDB.FindByTopic(topicRequest, user.IsAdmin, true, true, &user) if errfind != nil { topic, _, err = checkDMTopic(ctx, topicRequest) if err != nil { return nil, nil, http.StatusBadRequest, fmt.Errorf("topic " + topicRequest + " does not exist or you have no access on it") } } filters := []tat.Filter{} for _, f := range topic.Filters { if f.UserID == user.ID { filters = append(filters, f) } } topic.Filters = filters out := &tat.TopicJSON{Topic: topic} out.IsTopicRw, out.IsTopicAdmin = topicDB.GetUserRights(topic, &user) return out, &user, http.StatusOK, nil }
func checkTopicParentDM(user tat.User) error { topicName := "/Private/" + user.Username + "/DM" topicParent, err := topicDB.FindByTopic(topicName, false, false, false, nil) if err != nil { topicParent.Topic = topicName topicParent.Description = "DM Topics" if err := topicDB.Insert(topicParent, &user); err != nil { log.Errorf("Error while InsertTopic Parent %s", err) return err } } return nil }
func (m *MessagesController) moveMessage(ctx *gin.Context, messageIn *tat.MessageJSON, message tat.Message, user tat.User, fromTopic tat.Topic) { // Check if user can delete msg on from topic if err := m.checkBeforeDelete(ctx, message, user, true, fromTopic); err != nil { // ctx writes in checkBeforeDelete return } toTopic, err := topicDB.FindByTopic(messageIn.Option, true, false, false, &user) if err != nil { e := fmt.Sprintf("Topic destination %s does not exist", message.Topic) ctx.JSON(http.StatusNotFound, gin.H{"error": e}) return } // Check if user can write msg from dest topic if isRW, _ := topicDB.GetUserRights(toTopic, &user); !isRW { ctx.JSON(http.StatusForbidden, gin.H{"error": fmt.Sprintf("No RW Access to topic %s", toTopic.Topic)}) return } // check if message is a reply -> not possible if message.InReplyOfIDRoot != "" { ctx.JSON(http.StatusForbidden, gin.H{"error": fmt.Sprintf("You can't move a reply message")}) return } info := "" if messageIn.Action == tat.MessageActionMove { err := messageDB.Move(&message, user, fromTopic, *toTopic) if err != nil { log.Errorf("Error while move a message to topic: %s err: %s", toTopic.Topic, err) ctx.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Error while move a message to topic %s", toTopic.Topic)}) return } info = fmt.Sprintf("Message move to %s", toTopic.Topic) } else { ctx.JSON(http.StatusBadRequest, gin.H{"error": "Invalid action: " + messageIn.Action}) return } out := &tat.MessageJSONOut{Info: info, Message: message} hook.SendHook(&tat.HookJSON{HookMessage: &tat.HookMessageJSON{MessageJSONOut: out, Action: messageIn.Action}}, *toTopic) ctx.JSON(http.StatusCreated, out) }
func (m *PresencesController) listWithCriteria(ctx *gin.Context, criteria *tat.PresenceCriteria) { user, e := m.preCheckUser(ctx) if e != nil { return } if criteria.Topic != "" { _, err := topicDB.FindByTopic(criteria.Topic, true, false, false, user) if err != nil { ctx.AbortWithError(http.StatusBadRequest, errors.New("topic "+criteria.Topic+" does not exist or you have no Read Access on it")) return } // add / if search on topic // as topic is in path, it can't start with a / if criteria.Topic != "" && string(criteria.Topic[0]) != "/" { criteria.Topic = "/" + criteria.Topic } topicDM := "/Private/" + getCtxUsername(ctx) + "/DM/" if strings.HasPrefix(criteria.Topic, topicDM) { part := strings.Split(criteria.Topic, "/") if len(part) != 5 { log.Errorf("wrong topic name for DM") ctx.AbortWithError(http.StatusInternalServerError, errors.New("Wrong topic name for DM:"+criteria.Topic)) return } topicInverse := "/Private/" + part[4] + "/DM/" + getCtxUsername(ctx) criteria.Topic = criteria.Topic + "," + topicInverse } } count, presences, err := presenceDB.ListPresences(criteria) if err != nil { ctx.AbortWithError(http.StatusInternalServerError, err) return } out := &tat.PresencesJSON{ Count: count, Presences: presences, } ctx.JSON(http.StatusOK, out) }
// MigrateToDedicatedTopic migrates a topic to dedicated collection on mongo func (t *TopicsController) MigrateToDedicatedTopic(ctx *gin.Context) { topicRequest, err := GetParam(ctx, "topic") if err != nil { ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } topic, errfind := topicDB.FindByTopic(topicRequest, true, false, false, nil) if errfind != nil { ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } if errMigrate := topicDB.MigrateToDedicatedTopic(topic); errMigrate != nil { ctx.JSON(http.StatusInternalServerError, gin.H{"error": errMigrate.Error()}) return } ctx.JSON(http.StatusOK, gin.H{"info": fmt.Sprintf("%s is now dedicated", topicRequest)}) }
// EnableNotificationsTopic enable notication on one topic func (*UsersController) EnableNotificationsTopic(ctx *gin.Context) { topicIn, err := GetParam(ctx, "topic") if err != nil { return } user, err := PreCheckUser(ctx) if err != nil { return } topic, err := topicDB.FindByTopic(topicIn, true, false, false, &user) if err != nil { AbortWithReturnError(ctx, http.StatusBadRequest, errors.New("topic "+topicIn+" does not exist or you have no Read Access on it")) return } if err := userDB.EnableNotificationsTopic(&user, topic.Topic); err != nil { AbortWithReturnError(ctx, http.StatusInternalServerError, fmt.Errorf("Error while enable notication on topic %s to user:%s", topic.Topic, user.Username)) return } ctx.JSON(http.StatusCreated, gin.H{"info": fmt.Sprintf("Notications enabled on Topic %s", topic.Topic)}) }
func (m *PresencesController) preCheckTopic(ctx *gin.Context) (tat.PresenceJSON, tat.Topic, *tat.User, error) { var presenceIn tat.PresenceJSON ctx.Bind(&presenceIn) topicIn, err := GetParam(ctx, "topic") if err != nil { return presenceIn, tat.Topic{}, nil, err } presenceIn.Topic = topicIn user, e := m.preCheckUser(ctx) if e != nil { return presenceIn, tat.Topic{}, nil, err } topic, err := topicDB.FindByTopic(presenceIn.Topic, true, false, false, user) if err != nil { e := errors.New("Topic " + presenceIn.Topic + " does not exist") ctx.AbortWithError(http.StatusInternalServerError, e) return presenceIn, tat.Topic{}, nil, e } return presenceIn, *topic, user, nil }
// CheckTopics check default topics for user and creates them if fixTopics is true func CheckTopics(user *tat.User, fixTopics bool) string { topicsInfo := "" topicNames := [...]string{"", "Notifications"} for _, shortName := range topicNames { topicName := fmt.Sprintf("/Private/%s", user.Username) if shortName != "" { topicName = fmt.Sprintf("%s/%s", topicName, shortName) } if _, errfinding := topic.FindByTopic(topicName, false, false, false, nil); errfinding != nil { topicsInfo = fmt.Sprintf("%s %s KO : not exist; ", topicsInfo, topicName) if fixTopics { if err := CreatePrivateTopic(user, shortName); err != nil { topicsInfo = fmt.Sprintf("%s Error while creating %s; ", topicsInfo, topicName) } else { topicsInfo = fmt.Sprintf("%s %s created; ", topicsInfo, topicName) } } } else { topicsInfo = fmt.Sprintf("%s %s OK; ", topicsInfo, topicName) } } return topicsInfo }
func (m *MessagesController) messageDelete(ctx *gin.Context, cascade, force bool) { idMessageIn, err := GetParam(ctx, "idMessage") if err != nil { return } topicIn, err := GetParam(ctx, "topic") if err != nil { return } user, e := PreCheckUser(ctx) if e != nil { return } topic, errf := topicDB.FindByTopic(topicIn, true, false, false, &user) if errf != nil { log.Errorf("messageDelete> err:%s", errf) e := fmt.Sprintf("Topic %s does not exist", topicIn) ctx.JSON(http.StatusNotFound, gin.H{"error": e}) return } message := tat.Message{} if err = messageDB.FindByID(&message, idMessageIn, *topic); err != nil { ctx.JSON(http.StatusNotFound, gin.H{"error": fmt.Sprintf("Message %s does not exist", idMessageIn)}) return } err = m.checkBeforeDelete(ctx, message, user, force, *topic) if err != nil { // ctx writes in checkBeforeDelete return } c := &tat.MessageCriteria{ InReplyOfID: message.ID, TreeView: tat.TreeViewOneTree, Topic: topic.Topic, } msgs, err := messageDB.ListMessages(c, "", *topic) if err != nil { log.Errorf("Error while list Messages in Delete %s", err) ctx.JSON(http.StatusInternalServerError, gin.H{"error": "Error while list Messages in Delete"}) return } if cascade { for _, r := range msgs { errCheck := m.checkBeforeDelete(ctx, r, user, force, *topic) if errCheck != nil { // ctx writes in checkBeforeDelete return } } } else if len(msgs) > 0 { ctx.JSON(http.StatusBadRequest, gin.H{"error": "Could not delete this message, this message have replies"}) return } if err = messageDB.Delete(&message, cascade, *topic); err != nil { log.Errorf("Error while delete a message %s", err) ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } ctx.JSON(http.StatusOK, gin.H{"info": fmt.Sprintf("Message deleted from %s", topic.Topic)}) }
func (m *MessagesController) preCheckTopic(ctx *gin.Context, messageIn *tat.MessageJSON) (tat.Message, tat.Topic, *tat.User, error) { var message = tat.Message{} user, e := PreCheckUser(ctx) if e != nil { return message, tat.Topic{}, nil, e } topicIn, err := GetParam(ctx, "topic") if err != nil { return message, tat.Topic{}, nil, err } messageIn.Topic = topicIn if messageIn.Topic == "/" && messageIn.IDReference != "" { log.Warnf("preCheckTopic fallback to FindByIDDefaultCollection for %s", messageIn.IDReference) if efind := messageDB.FindByIDDefaultCollection(&message, messageIn.IDReference); efind != nil { e := errors.New("Invalid request, no topic and message " + messageIn.IDReference + " not found in default collection") ctx.JSON(http.StatusNotFound, gin.H{"error": e.Error()}) return message, tat.Topic{}, nil, e } messageIn.Topic = message.Topic } topic, efind := topicDB.FindByTopic(messageIn.Topic, true, true, true, &user) if efind != nil { topica, _, edm := checkDMTopic(ctx, messageIn.Topic) if edm != nil { e := errors.New("Topic " + messageIn.Topic + " does not exist or you have no read access on it") ctx.JSON(http.StatusNotFound, gin.H{"error": e.Error()}) return message, tat.Topic{}, nil, e } topic = topica } if messageIn.IDReference == "" && messageIn.TagReference == "" && messageIn.StartTagReference == "" && messageIn.LabelReference == "" && messageIn.StartLabelReference == "" { // nothing here } else if messageIn.IDReference != "" || messageIn.StartTagReference != "" || messageIn.TagReference != "" || messageIn.StartLabelReference != "" || messageIn.LabelReference != "" { if messageIn.IDReference != "" { if efind := messageDB.FindByID(&message, messageIn.IDReference, *topic); efind != nil { e := errors.New("Message " + messageIn.IDReference + " does not exist or you have no read access on it") ctx.JSON(http.StatusNotFound, gin.H{"error": e.Error()}) return message, tat.Topic{}, nil, e } } else { // TagReference, StartTagReference,LabelReference, StartLabelReference onlyMsgRoot := tat.True // default value must be true if messageIn.OnlyRootReference == tat.False { onlyMsgRoot = tat.False } c := &tat.MessageCriteria{ AndTag: messageIn.TagReference, StartTag: messageIn.StartTagReference, AndLabel: messageIn.LabelReference, StartLabel: messageIn.StartLabelReference, OnlyMsgRoot: onlyMsgRoot, Topic: topic.Topic, } mlist, efind := messageDB.ListMessages(c, user.Username, *topic) if efind != nil { e := errors.New("Searched Message does not exist or you have no read access on it") ctx.JSON(http.StatusNotFound, gin.H{"error": e.Error()}) return message, tat.Topic{}, nil, e } if len(mlist) != 1 { if messageIn.Action != "" { e := errors.New(fmt.Sprintf("Searched Message, expected 1 message and %d message(s) matching on tat", len(mlist))) ctx.JSON(http.StatusNotFound, gin.H{"error": e.Error()}) return message, tat.Topic{}, nil, e } // take last root message if len(mlist) > 0 { message = mlist[0] } } else { message = mlist[0] } } topicName := "" if messageIn.Action == tat.MessageActionUpdate { topicName = messageIn.Topic } else if messageIn.Action == "" || messageIn.Action == tat.MessageActionReply || messageIn.Action == tat.MessageActionLike || messageIn.Action == tat.MessageActionUnlike || messageIn.Action == tat.MessageActionLabel || messageIn.Action == tat.MessageActionUnlabel || messageIn.Action == tat.MessageActionVoteup || messageIn.Action == tat.MessageActionVotedown || messageIn.Action == tat.MessageActionUnvoteup || messageIn.Action == tat.MessageActionUnvotedown || messageIn.Action == tat.MessageActionRelabel || messageIn.Action == tat.MessageActionConcat { topicName = m.inverseIfDMTopic(ctx, message.Topic) } else if messageIn.Action == tat.MessageActionMove { topicName = topicIn } else if messageIn.Action == tat.MessageActionTask || messageIn.Action == tat.MessageActionUntask { topicName = m.inverseIfDMTopic(ctx, message.Topic) } else { e := errors.New("Invalid Call. IDReference not empty with unknown action") ctx.JSON(http.StatusBadRequest, gin.H{"error": e.Error()}) return message, tat.Topic{}, nil, e } if topicName == "" && messageIn.Action == "" { topicName = messageIn.Topic } topic, err = topicDB.FindByTopic(topicName, true, true, true, &user) if err != nil { e := errors.New("Topic " + topicName + " does not exist") ctx.JSON(http.StatusNotFound, gin.H{"error": e.Error()}) return message, tat.Topic{}, nil, e } } else { e := errors.New("Topic and Reference (ID, StartTag, Tag, StartLabel, Label) are null. Wrong request") ctx.JSON(http.StatusBadRequest, gin.H{"error": e.Error()}) return message, tat.Topic{}, nil, e } return message, *topic, &user, nil }
// SetParam update Topic Parameters : MaxLength, CanForeceDate, CanUpdateMsg, CanDeleteMsg, CanUpdateAllMsg, CanDeleteAllMsg, AdminCanDeleteAllMsg // admin only, except on Private topic func (t *TopicsController) SetParam(ctx *gin.Context) { var paramsBind paramsJSON ctx.Bind(¶msBind) topic := &tat.Topic{} var errFind, err error if strings.HasPrefix(paramsBind.Topic, "/Private/"+getCtxUsername(ctx)) { topic, errFind = topicDB.FindByTopic(paramsBind.Topic, false, false, false, nil) if errFind != nil { ctx.JSON(http.StatusInternalServerError, gin.H{"error": "Error while fetching topic /Private/" + getCtxUsername(ctx)}) return } } else { topic, err = t.preCheckUserAdminOnTopic(ctx, paramsBind.Topic) if err != nil { ctx.JSON(tat.Error(err)) return } } for _, p := range paramsBind.Parameters { if strings.HasPrefix(p.Key, tat.HookTypeKafka) && !isTatAdmin(ctx) { ctx.JSON(http.StatusInternalServerError, gin.H{"error": "Only Tat Admin can use tathook-kafka"}) return } } err = topicDB.SetParam(topic, getCtxUsername(ctx), paramsBind.Recursive, paramsBind.MaxLength, paramsBind.CanForceDate, paramsBind.CanUpdateMsg, paramsBind.CanDeleteMsg, paramsBind.CanUpdateAllMsg, paramsBind.CanDeleteAllMsg, paramsBind.AdminCanUpdateAllMsg, paramsBind.AdminCanDeleteAllMsg, paramsBind.IsAutoComputeTags, paramsBind.IsAutoComputeLabels, paramsBind.Parameters) // add tat2xmpp_username RO or RW on this topic if a key is xmpp for _, p := range paramsBind.Parameters { if strings.HasPrefix(p.Key, tat.HookTypeXMPPOut) { found := false for _, u := range topic.ROUsers { if u == viper.GetString("tat2xmpp_username") { found = true } } for _, u := range topic.RWUsers { if u == viper.GetString("tat2xmpp_username") { found = true } } if !found { if errf := topicDB.AddRoUser(topic, getCtxUsername(ctx), viper.GetString("tat2xmpp_username"), false); errf != nil { log.Errorf("Error while adding read only user tat2xmpp_username: %s", errf) ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } } } else if strings.HasPrefix(p.Key, tat.HookTypeXMPP) { found := false for _, u := range topic.RWUsers { if u == viper.GetString("tat2xmpp_username") { found = true } } if !found { if errf := topicDB.AddRwUser(topic, getCtxUsername(ctx), viper.GetString("tat2xmpp_username"), false); errf != nil { log.Errorf("Error while adding read write user tat2xmpp_username: %s", errf) ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } } } } if err != nil { log.Errorf("Error while setting parameters: %s", err) ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } ctx.JSON(http.StatusCreated, gin.H{"info": fmt.Sprintf("Topic %s updated", topic.Topic)}) }