func (t *Topic) makeInvite(notify, target, from types.Uid, act types.InviteAction, modeWant, modeGiven types.AccessMode, info interface{}) *ServerComMessage { // FIXME(gene): this is a workaround for gorethink's broken way of marshalling json inv, err := json.Marshal(MsgInvitation{ Topic: t.name, User: target.UserId(), Action: act.String(), Acs: MsgAccessMode{modeWant.String(), modeGiven.String()}, Info: info}) if err != nil { log.Println(err) } converted := map[string]interface{}{} err = json.Unmarshal(inv, &converted) if err != nil { log.Println(err) } // endof workaround msg := &ServerComMessage{Data: &MsgServerData{ Topic: "me", From: from.UserId(), Timestamp: time.Now().UTC().Round(time.Millisecond), Content: converted}, rcptto: notify.UserId()} log.Printf("Invite generated: %#+v", msg.Data) return msg }
// approveSub processes a request to initiate an invite or approve a subscription request from another user: // Handle these cases: // A. Manager is inviting another user for the first time (no prior subscription) // B. Manager is re-inviting another user (adjusting modeGiven, modeWant is still "N") // C. Manager is changing modeGiven for another user, modeWant != "N" func (t *Topic) approveSub(h *Hub, sess *Session, target types.Uid, set *MsgClientSet) error { now := time.Now().UTC().Round(time.Millisecond) // Check if requester actually has permission to manage sharing if userData, ok := t.perUser[sess.uid]; !ok || !userData.modeGiven.IsManager() || !userData.modeWant.IsManager() { sess.queueOut(ErrPermissionDenied(set.Id, t.original, now)) return errors.New("topic access denied") } // Parse the access mode granted var modeGiven types.AccessMode if set.Sub.Mode != "" { modeGiven.UnmarshalText([]byte(set.Sub.Mode)) } // If the user is banned from topic, make sute it's the only change if modeGiven.IsBanned() { modeGiven = types.ModeBanned } // Make sure no one but the owner can do an ownership transfer if modeGiven.IsOwner() && t.owner != sess.uid { sess.queueOut(ErrPermissionDenied(set.Id, t.original, now)) return errors.New("attempt to transfer ownership by non-owner") } var givenBefore types.AccessMode // Check if it's a new invite. If so, save it to database as a subscription. // Saved subscription does not mean the user is allowed to post/read userData, ok := t.perUser[target] if !ok { if modeGiven == types.ModeNone { if t.accessAuth != types.ModeNone { // Request to use default access mode for the new subscriptions. modeGiven = t.accessAuth } else { sess.queueOut(ErrMalformed(set.Id, t.original, now)) return errors.New("cannot invite without giving any access rights") } } // Add subscription to database sub := &types.Subscription{ User: target.String(), Topic: t.name, ModeWant: types.ModeNone, ModeGiven: modeGiven, } if err := store.Subs.Create(sub); err != nil { sess.queueOut(ErrUnknown(set.Id, t.original, now)) return err } userData = perUserData{ modeGiven: sub.ModeGiven, modeWant: sub.ModeWant, private: nil, } t.perUser[target] = userData } else { // Action on an existing subscription (re-invite or confirm/decline) givenBefore = userData.modeGiven // Request to re-send invite without changing the access mode if modeGiven == types.ModeNone { modeGiven = userData.modeGiven } else if modeGiven != userData.modeGiven { userData.modeGiven = modeGiven // Save changed value to database if err := store.Subs.Update(t.name, sess.uid, map[string]interface{}{"ModeGiven": modeGiven}); err != nil { return err } t.perUser[target] = userData } } // The user does not want to be bothered, no further action is needed if userData.modeWant.IsBanned() { sess.queueOut(ErrPermissionDenied(set.Id, t.original, now)) return errors.New("topic access denied") } // Handle the following cases: // * a ban of the user, modeGive.IsBanned = true (if user is banned no need to invite anyone) // * regular invite: modeWant = "N", modeGiven > 0 // * access rights update: old modeGiven != new modeGiven if !modeGiven.IsBanned() { if userData.modeWant == types.ModeNone { // (re-)Send the invite to target h.route <- t.makeInvite(target, target, sess.uid, types.InvJoin, userData.modeWant, modeGiven, set.Sub.Info) } else if givenBefore != modeGiven { // Inform target that the access has changed h.route <- t.makeInvite(target, target, sess.uid, types.InvInfo, userData.modeWant, modeGiven, set.Sub.Info) } } // Has anything actually changed? if givenBefore != modeGiven { // inform requester of the change made h.route <- t.makeInvite(sess.uid, target, sess.uid, types.InvInfo, userData.modeWant, modeGiven, map[string]string{"before": givenBefore.String()}) } return nil }