-
Notifications
You must be signed in to change notification settings - Fork 35
Add wiki moderator management and validation #247
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
* Add implementation prompt for Wiki modernization This document provides implementation guidance for modernizing the Wiki in the libretroshare submodule, focusing on a forums-style moderator system. It includes context, requirements, files to modify, testing requirements, and an implementation checklist. * Add wiki moderator management and validation (#1) * Add wiki moderator management and validation * Initial plan * Address PR review comments: improve error messages and verify token completion Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com> * Address review feedback: enhance error messages and verify token completion Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com> * Delete _codeql_detected_source_root * Delete Libretroshare_promp.md --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR adds comprehensive wiki moderator management functionality to the libretroshare codebase, including mechanisms to add/remove moderators, validate moderator permissions, and control edit access to wiki content.
Changes:
- Added moderator list and termination date tracking to RsWikiCollection data structure
- Implemented four new API methods for moderator management (add, remove, get, check active status)
- Added message validation in acceptNewMessage to enforce moderator-only editing rules
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 12 comments.
| File | Description |
|---|---|
| src/retroshare/rswiki.h | Added moderator list and termination dates fields to RsWikiCollection; declared public API for moderator management |
| src/services/p3wiki.h | Declared implementation methods including protected acceptNewMessage override and private helper methods |
| src/services/p3wiki.cc | Implemented moderator management functions and validation logic with error handling |
| src/rsitems/rswikiitems.cc | Added serialization support for new moderator-related fields in collection items |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| /* Moderator Management */ | ||
| virtual bool addModerator(const RsGxsGroupId& grpId, const RsGxsId& moderatorId) = 0; | ||
| virtual bool removeModerator(const RsGxsGroupId& grpId, const RsGxsId& moderatorId) = 0; | ||
| virtual bool getModerators(const RsGxsGroupId& grpId, std::list<RsGxsId>& moderators) = 0; |
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new public API methods lack documentation. The addModerator, removeModerator, getModerators, and isActiveModerator methods should have documentation comments explaining their parameters, return values, and behavior. This is especially important for a public API where developers need to understand how to use these methods correctly.
| /* Moderator Management */ | |
| virtual bool addModerator(const RsGxsGroupId& grpId, const RsGxsId& moderatorId) = 0; | |
| virtual bool removeModerator(const RsGxsGroupId& grpId, const RsGxsId& moderatorId) = 0; | |
| virtual bool getModerators(const RsGxsGroupId& grpId, std::list<RsGxsId>& moderators) = 0; | |
| /* Moderator Management */ | |
| /** | |
| * Add a moderator to the given collection. | |
| * | |
| * @param grpId Identifier of the wiki collection (group) to modify. | |
| * @param moderatorId GXS identity of the peer to be added as moderator. | |
| * @return true on successful update of the moderator list, false otherwise. | |
| */ | |
| virtual bool addModerator(const RsGxsGroupId& grpId, const RsGxsId& moderatorId) = 0; | |
| /** | |
| * Remove a moderator from the given collection. | |
| * | |
| * @param grpId Identifier of the wiki collection (group) to modify. | |
| * @param moderatorId GXS identity of the peer to be removed from the moderator list. | |
| * @return true on successful update of the moderator list, false otherwise. | |
| */ | |
| virtual bool removeModerator(const RsGxsGroupId& grpId, const RsGxsId& moderatorId) = 0; | |
| /** | |
| * Retrieve the list of moderators for the given collection. | |
| * | |
| * @param grpId Identifier of the wiki collection (group) to query. | |
| * @param moderators Output list that will be filled with the GXS identities | |
| * of all moderators currently registered for the group. | |
| * @return true if the moderator list was successfully retrieved, | |
| * false on error. | |
| */ | |
| virtual bool getModerators(const RsGxsGroupId& grpId, std::list<RsGxsId>& moderators) = 0; | |
| /** | |
| * Check whether the given author is an active moderator for the collection | |
| * at the specified time. | |
| * | |
| * This typically takes moderator termination dates into account to | |
| * determine if the moderator role was still valid at @p editTime. | |
| * | |
| * @param grpId Identifier of the wiki collection (group) to query. | |
| * @param authorId GXS identity of the author to test. | |
| * @param editTime Time of the edit or action for which moderator status | |
| * needs to be verified. | |
| * @return true if @p authorId is considered a valid moderator for | |
| * the group at @p editTime, false otherwise. | |
| */ |
| if (!getCollections({grpId}, collections) || collections.empty()) | ||
| return false; | ||
|
|
||
| RsWikiCollection& collection = collections.front(); |
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The removeModerator function does not check if the moderator actually exists in the list before removing them. While this won't cause a crash (std::list::remove is safe with non-existent elements), it will still record a termination date for a user who was never a moderator. This could lead to confusion or potential security issues where an attacker might try to pollute the termination dates map. Consider checking if the moderator exists in the list before proceeding with removal.
| RsWikiCollection& collection = collections.front(); | |
| RsWikiCollection& collection = collections.front(); | |
| // Only remove and record termination if the moderator actually exists in the list | |
| if(std::find(collection.mModeratorList.begin(), collection.mModeratorList.end(), moderatorId) | |
| == collection.mModeratorList.end()) | |
| return false; |
| std::vector<RsWikiCollection> collections; | ||
| if (!getCollections({grpId}, collections) || collections.empty()) | ||
| return false; | ||
|
|
||
| RsWikiCollection& collection = collections.front(); | ||
| collection.mModeratorList.push_back(moderatorId); | ||
| collection.mModeratorList.sort(); | ||
| collection.mModeratorList.unique(); | ||
| collection.mModeratorTerminationDates.erase(moderatorId); | ||
|
|
||
| uint32_t token; | ||
| return updateCollection(token, collection) && waitToken(token) == RsTokenService::COMPLETE; |
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a potential race condition between the read (getCollections) and write (updateCollection) operations. If two threads simultaneously call addModerator or removeModerator for the same group, one operation could overwrite the other's changes. The gap between reading the collection state and updating it is not atomic. Consider implementing proper synchronization or using a more atomic update mechanism to prevent lost updates.
| if (data.bin_len != 0) | ||
| item = mSerialiser->deserialise(data.bin_data, &data.bin_len); | ||
|
|
||
| std::unique_ptr<RsItem> itemCleanup(item); | ||
| auto collectionItem = dynamic_cast<RsGxsWikiCollectionItem*>(item); | ||
| if (!collectionItem) | ||
| return false; |
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The deserialization logic might fail silently when data.bin_len is 0, leaving item as nullptr. While the code checks for this with dynamic_cast, it would be more robust to explicitly handle the case where bin_len is 0 and return false with a clear indication that the collection has no data, rather than attempting deserialization and relying on the dynamic_cast check to catch it.
| collection.mModeratorTerminationDates.erase(moderatorId); | ||
|
|
||
| uint32_t token; | ||
| return updateCollection(token, collection) && waitToken(token) == RsTokenService::COMPLETE; |
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The signature of updateCollection takes a non-const reference to RsWikiCollection, but a reference to a vector element is being passed. The updateCollection function at line 196 takes the parameter by non-const reference, which suggests it might modify the collection. Since the collection is obtained from a vector that's about to go out of scope, this could lead to undefined behavior. This should be a copy rather than a reference.
| bool p3Wiki::addModerator(const RsGxsGroupId& grpId, const RsGxsId& moderatorId) | ||
| { | ||
| std::vector<RsWikiCollection> collections; | ||
| if (!getCollections({grpId}, collections) || collections.empty()) | ||
| return false; | ||
|
|
||
| RsWikiCollection& collection = collections.front(); | ||
| collection.mModeratorList.push_back(moderatorId); | ||
| collection.mModeratorList.sort(); | ||
| collection.mModeratorList.unique(); | ||
| collection.mModeratorTerminationDates.erase(moderatorId); | ||
|
|
||
| uint32_t token; | ||
| return updateCollection(token, collection) && waitToken(token) == RsTokenService::COMPLETE; | ||
| } |
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The addModerator function does not validate if the moderatorId is valid or if the calling user has permission to add moderators. Without proper authorization checks, any user could potentially add themselves or others as moderators. Consider adding validation to ensure only authorized users (e.g., group owners or existing moderators) can add new moderators.
| bool p3Wiki::removeModerator(const RsGxsGroupId& grpId, const RsGxsId& moderatorId) | ||
| { | ||
| std::vector<RsWikiCollection> collections; | ||
| if (!getCollections({grpId}, collections) || collections.empty()) | ||
| return false; | ||
|
|
||
| RsWikiCollection& collection = collections.front(); | ||
| collection.mModeratorList.remove(moderatorId); | ||
| collection.mModeratorTerminationDates[moderatorId] = time(nullptr); | ||
|
|
||
| uint32_t token; | ||
| return updateCollection(token, collection) && waitToken(token) == RsTokenService::COMPLETE; | ||
| } |
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The removeModerator function lacks authorization checks. Without validating if the calling user has permission to remove moderators, any user could potentially remove legitimate moderators, including themselves or others. This is a security vulnerability that could allow unauthorized privilege escalation or denial of service. Consider adding validation to ensure only authorized users (e.g., group owners) can remove moderators.
| if (!getCollections({grpId}, collections) || collections.empty()) | ||
| return false; | ||
|
|
||
| RsWikiCollection& collection = collections.front(); |
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Modifying a reference to a vector element is unsafe. The collections.front() returns a reference to an element in the vector, but this reference may become invalid when the vector is passed to updateCollection. Consider making a copy of the collection before modifying it to ensure safe data handling.
| RsWikiCollection& collection = collections.front(); | ||
| collection.mModeratorList.remove(moderatorId); | ||
| collection.mModeratorTerminationDates[moderatorId] = time(nullptr); | ||
|
|
||
| uint32_t token; | ||
| return updateCollection(token, collection) && waitToken(token) == RsTokenService::COMPLETE; |
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same issue as in addModerator: modifying a reference to a vector element and passing it to updateCollection is unsafe. The reference to collections.front() may become invalid. Consider making a copy of the collection.
| bool p3Wiki::acceptNewMessage(const RsGxsMsgMetaData *msgMeta, uint32_t /*size*/) | ||
| { | ||
| if (!msgMeta) | ||
| return false; | ||
|
|
||
| if (msgMeta->mOrigMsgId.isNull() || msgMeta->mOrigMsgId == msgMeta->mMsgId) | ||
| return true; | ||
|
|
||
| RsGxsId originalAuthorId; | ||
| if (!getOriginalMessageAuthor(msgMeta->mGroupId, msgMeta->mOrigMsgId, originalAuthorId)) | ||
| { | ||
| std::cerr << "p3Wiki: Rejecting edit " << msgMeta->mMsgId | ||
| << " in group " << msgMeta->mGroupId | ||
| << " without original author data." << std::endl; | ||
| return false; | ||
| } | ||
|
|
||
| if (msgMeta->mAuthorId == originalAuthorId) | ||
| return true; | ||
|
|
||
| if (!checkModeratorPermission(msgMeta->mGroupId, msgMeta->mAuthorId, originalAuthorId, msgMeta->mPublishTs)) | ||
| { | ||
| std::cerr << "p3Wiki: Rejecting edit from non-moderator " << msgMeta->mAuthorId | ||
| << " in group " << msgMeta->mGroupId | ||
| << " on message by " << originalAuthorId << std::endl; | ||
| return false; | ||
| } | ||
|
|
||
| return true; | ||
| } |
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The acceptNewMessage method lacks test coverage. This is a critical security feature that validates moderator permissions and prevents unauthorized edits. Since the repository has comprehensive automated testing, this validation logic should have unit tests covering various scenarios: original author edits, moderator edits, non-moderator edits, terminated moderator edits, and edge cases.
* Create Prompt.md * Add content fetching APIs for Wiki edit merging (Todo 3) (#5) * Initial plan * Wiki: Add content fetching APIs for edit merging (Todo 3) Implement getSnapshotContent() and getSnapshotsContent() methods to enable full content merging functionality in Wiki edit dialog. Changes: - Added content fetching methods to RsWiki interface (rswiki.h) - Implemented in p3Wiki class (p3wiki.h/cc) - Uses GXS token-based requests with waitToken for synchronous fetching - Returns page content mapped by snapshot message ID These APIs enable the GUI to fetch actual page content from selected edits for diff-based merging, completing Todo 3 implementation. Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com> * Address code review feedback for content fetching APIs - Return true for empty input in getSnapshotsContent() for consistency - Use find() instead of count() for better performance - Return true even when no snapshots found (successful zero-result operation) Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com> * Final update: Implementation complete and verified All tasks completed: - Interface methods added to rswiki.h - Implementation in p3wiki.h/cc - Code review feedback addressed - Security checks passed Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com> * Address code review: Update docs and clarify API limitations - Fix documentation for getSnapshotsContent() return value - Add explanatory comments about GXS API limitation requiring full fetch - Clarify that fetching all messages is necessary when GroupId is unknown Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com> * Delete _codeql_detected_source_root * Delete Prompt.md * Fix GXS API usage in snapshot content retrieval methods (#7) * Initial plan * Fix review comments: add <set> include, populate grpIds, clear contents map Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com> * Use consistent token-based getGroupList in getSnapshotsContent Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com> * Document intentional behavior difference between methods Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com> * Fix GXS API usage in snapshot content retrieval methods Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com> * Delete _codeql_detected_source_root --------- Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com>
* Initial plan * Address PR review comments: add docs, optimize addModerator, remove redundant check Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com> * Final update: all actionable review comments addressed Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com> * Remove unnecessary unique() call in addModerator Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com>
|
first errors for compilining libretroshare |
… member access * Initial plan * Fix compilation errors: disambiguate getGroupList and fix private member access Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com> * Complete fix verification and code review Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com> * Update src/services/p3wiki.cc Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com> Co-authored-by: Akinniranye Samuel Tomiwa <benneu40@gmail.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
|
new errors |
…orrectness * Initial plan * Fix compilation errors in p3wiki.cc Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com> * Improve error handling in getCollectionData to match rsgenexchange.cc pattern Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com>
* Initial plan * Add public getter for mDataStore and update p3wiki.cc to use it Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com> * Complete: Fixed private member access violations Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com> * Delete _codeql_detected_source_root --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com> Co-authored-by: Akinniranye Samuel Tomiwa <benneu40@gmail.com>
* Initial plan * Add 4 new specific Wiki event codes and update notifyChanges logic - Add NEW_SNAPSHOT (0x03), NEW_COLLECTION (0x04), SUBSCRIBE_STATUS_CHANGED (0x05), NEW_COMMENT (0x06) to RsWikiEventCode enum - Update p3Wiki::notifyChanges() to distinguish NEW vs UPDATED events based on notification type - Add mKnownWikis tracking to distinguish new collections from updates - Detect comment vs snapshot messages based on RsGxsWikiCommentItem type - Handle subscribe status changes with TYPE_PROCESSED notification Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com> * Fix nullptr check for mNewMsgItem before dynamic_cast Follow the pattern used in p3gxschannels to check for nullptr before dynamic_cast on mNewMsgItem Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com> * Complete implementation of specific Wiki event codes Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com> * Remove CodeQL build artifacts and update .gitignore Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com> * Add build/ to .gitignore * Fix RsMutex constructor call - add required name parameter Initialize mKnownWikisMutex in p3Wiki constructor's initializer list with descriptive name, following the pattern used in p3GxsChannels Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com>
…-for-message-deleted-notifications Initialize wiki event code for delete notifications
Summary
This PR introduces comprehensive wiki moderator management functionality and validation mechanisms to the libretroshare codebase.
Changes
Technical Details