nextcloud / nextcloud/circles

Team membership approvals are not final and can be overriden

Open
#2,824 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

0. Needs triage bug
Dominant language
JavaScript
Stars
177
Forks
68
Avg merge
2d 22h
Merged PRs (30d)
60

Description

How to use GitHub
  • Please use the 👍 reaction to show that you are affected by the same issue.
  • Please don't comment if you have no relevant information to add. It's just extra noise for everyone subscribed to this issue.
  • Subscribe to receive notifications on status change and new comments.

Steps to reproduce
  1. Create a new team "team5" as "admin" user
  2. Add "user1" and "user2"
  3. Assign an Admin role to user1
  4. Check/enable team properties:
    • Anyone can request membership
    • Members need to accept invitation
    • Memberships must be confirmed/accepted by a Moderator (requires "Anyone can request membership")
    • Visible to everyone
  5. Switch to "user4" and apply for a request to join team5
  6. Switch to user1 and approve the request
  7. (user4 gets added to the team)
  8. Switch to admin user and reject the request
  9. (user4 gets removed to the team)

Alternatively, if user1 rejects the request and admin approves it, admin gets a generic error "Failed to perform action" and the network log shows a generic 404.
^This one is a little hard to catch since the notification is removed soon but a helpful message would be nice nonetheless.

Expected behaviour

One approval from the notifications from any one of the admins/moderators should be final, and either the notifications from others users be removed or the actions there should be ignored if the user has been approved already.

Actual behaviour

Rejection from any one of the admins/moderators takes effect and removes the user.
Didn't test subsequent actions from additional admins/moderators from their notifications.

Server configuration

Web server: Apache/Nginx

Database: MySQL/Maria/SQLite/PostgreSQL

PHP version: 8.5/8.4/8.3

Nextcloud version: 34.0.3.2

List of activated apps
  - circles: 34.0.0
  - contacts: 8.8.0-rc.1
Nextcloud configuration
{
    "system": {
        "debug": true,
        "profiler": true,
        "apps_paths": [
            {
                "path": "\/var\/www\/html\/apps",
                "url": "\/apps",
                "writable": false
            },
            {
                "path": "\/var\/www\/html\/apps-extra",
                "url": "\/apps-extra",
                "writable": false
            },
            {
                "path": "\/var\/www\/html\/apps-shared",
                "url": "\/apps-shared",
                "writable": false
            },
            {
                "path": "\/var\/www\/html\/apps-writable",
                "url": "\/apps-writable",
                "writable": true
            }
        ],
        "allow_local_remote_servers": true,
        "mail_from_address": "***REMOVED SENSITIVE VALUE***",
        "mail_smtpmode": "smtp",
        "mail_sendmailmode": "smtp",
        "mail_domain": "***REMOVED SENSITIVE VALUE***",
        "mail_smtphost": "***REMOVED SENSITIVE VALUE***",
        "mail_smtpport": "1025",
        "skeletondirectory": "\/skeleton",
        "setup_create_db_user": false,
        "loglevel": 2,
        "log_query": false,
        "query_log_file": "\/shared\/log\/querylog-stable34.log",
        "query_log_file_requestid": "yes",
        "diagnostics.logging": false,
        "diagnostics.logging.threshold": 0,
        "log.condition": {
            "apps": [
                "diagnostics",
                "admin_audit"
            ]
        },
        "sharing.federation.allowSelfSignedCertificates": true,
        "gss.selfsigned.allow": true,
        "passwordsalt": "***REMOVED SENSITIVE VALUE***",
        "secret": "***REMOVED SENSITIVE VALUE***",
        "trusted_domains": [
            "localhost",
            "nextcloud",
            "stable34.nc.ncweb",
            "192.168.21.8",
            "localhost"
        ],
        "datadirectory": "***REMOVED SENSITIVE VALUE***",
        "dbtype": "mysql",
        "version": "34.0.3.2",
        "overwrite.cli.url": "https:\/\/stable34.nc.ncweb",
        "instanceid": "***REMOVED SENSITIVE VALUE***",
        "dbname": "***REMOVED SENSITIVE VALUE***",
        "dbhost": "***REMOVED SENSITIVE VALUE***",
        "dbtableprefix": "oc_",
        "mysql.utf8mb4": true,
        "dbuser": "***REMOVED SENSITIVE VALUE***",
        "dbpassword": "***REMOVED SENSITIVE VALUE***",
        "updater.release.channel": "git",
        "installed": true,
        "lookup_server": "",
        "redis": {
            "host": "***REMOVED SENSITIVE VALUE***",
            "port": 6379
        },
        "memcache.local": "\\OC\\Memcache\\Redis",
        "memcache.locking": "\\OC\\Memcache\\Redis",
        "trusted_proxies": "***REMOVED SENSITIVE VALUE***",
        "theme": "",
        "maintenance": false,
        "ldapProviderFactory": "OCA\\User_LDAP\\LDAPProviderFactory"
    }
}
Browser

Browser name: Firefox/Chrome/Safari/…

Browser version: 124/125/…

Operating system: Windows/Ubuntu/Mac/…

Server log

when the request if rejected then approved by another moderator:

{"reqId":"xo4Nkmn8kWoMaqBHDmyP","level":3,"time":"2026-08-27T00:29:11+00:00","remoteAddr":"192.168.21.4","user":"admin","app":"circles","method":"PUT","url":"/ocs/v2.php/apps/circles/circles/unA8Pi4zzB18bzD9WRsLZ7Yj6JsyjLC/members/Cd2CGENqqHK1dGpa9wFJJ4tKS9kytwL","scriptName":"/ocs/v2.php","message":"{\"circleId\":\"unA8Pi4zzB18bzD9WRsLZ7Yj6JsyjLC\",\"memberId\":\"Cd2CGENqqHK1dGpa9wFJJ4tKS9kytwL\"}","userAgent":"xxx","version":"34.0.3.2","exception":{"Exception":"OCA\\Circles\\Exceptions\\MemberNotFoundException","Message":"","Code":404,"Trace":[{"file":"/var/www/html/apps-extra/circles/lib/Db/MemberRequest.php","line":393,"function":"getItemFromRequest","class":"OCA\\Circles\\Db\\MemberRequestBuilder","type":"->","args":[{"__class__":"OCA\\Circles\\Db\\CoreQueryBuilder"}]},{"file":"/var/www/html/apps-extra/circles/lib/Service/MemberService.php","line":139,"function":"getMemberById","class":"OCA\\Circles\\Db\\MemberRequest","type":"->","args":["Cd2CGENqqHK1dGpa9wFJJ4tKS9kytwL",{"__class__":"OCA\\Circles\\Model\\FederatedUser"},{"__class__":"OCA\\Circles\\Model\\Probes\\MemberProbe"}]},{"file":"/var/www/html/apps-extra/circles/lib/Controller/LocalController.php","line":376,"function":"getMemberById","class":"OCA\\Circles\\Service\\MemberService","type":"->","args":["Cd2CGENqqHK1dGpa9wFJJ4tKS9kytwL","unA8Pi4zzB18bzD9WRsLZ7Yj6JsyjLC"]},{"file":"/var/www/html/lib/private/AppFramework/Http/Dispatcher.php","line":165,"function":"memberConfirm","class":"OCA\\Circles\\Controller\\LocalController","type":"->","args":["unA8Pi4zzB18bzD9WRsLZ7Yj6JsyjLC","Cd2CGENqqHK1dGpa9wFJJ4tKS9kytwL"]},{"file":"/var/www/html/lib/private/AppFramework/Http/Dispatcher.php","line":78,"function":"executeController","class":"OC\\AppFramework\\Http\\Dispatcher","type":"->","args":[{"__class__":"OCA\\Circles\\Controller\\LocalController"},"memberConfirm"]},{"file":"/var/www/html/lib/private/AppFramework/App.php","line":137,"function":"dispatch","class":"OC\\AppFramework\\Http\\Dispatcher","type":"->","args":[{"__class__":"OCA\\Circles\\Controller\\LocalController"},"memberConfirm"]},{"file":"/var/www/html/lib/private/Route/Router.php","line":324,"function":"main","class":"OC\\AppFramework\\App","type":"::","args":["OCA\\Circles\\Controller\\LocalController","memberConfirm",{"__class__":"OC\\AppFramework\\DependencyInjection\\DIContainer"},{"_route":"ocs.circles.local.memberconfirm","circleId":"unA8Pi4zzB18bzD9WRsLZ7Yj6JsyjLC","memberId":"Cd2CGENqqHK1dGpa9wFJJ4tKS9kytwL"}]},{"file":"/var/www/html/ocs/v1.php","line":78,"function":"match","class":"OC\\Route\\Router","type":"->","args":["/ocsapp/apps/circles/circles/unA8Pi4zzB18bzD9WRsLZ7Yj6JsyjLC/members/Cd2CGENqqHK1dGpa9wFJJ4tKS9kytwL"]},{"file":"/var/www/html/ocs/v2.php","line":10,"args":["/var/www/html/ocs/v1.php"],"function":"require_once"}],"File":"/var/www/html/apps-extra/circles/lib/Db/MemberRequestBuilder.php","Line":102,"message":"{\"circleId\":\"unA8Pi4zzB18bzD9WRsLZ7Yj6JsyjLC\",\"memberId\":\"Cd2CGENqqHK1dGpa9wFJJ4tKS9kytwL\"}","exception":"{\"class\":\"OCA\\Circles\\Exceptions\\MemberNotFoundException\",\"message\":\"\",\"code\":404,\"file\":\"/var/www/html/apps-extra/circles/lib/Db/MemberRequestBuilder.php:102\",\"trace\":\"#0 /var/www/html/apps-extra/circles/lib/Db/MemberRequest.php(393): OCA\\Circles\\Db\\MemberRequestBuilder->getItemFromRequest(Object(OCA\\Circles\\Db\\CoreQueryBuilder))\\n#1 /var/www/html/apps-extra/circles/lib/Service/MemberService.php(139): OCA\\Circles\\Db\\MemberRequest->getMemberById('Cd2CGENqqHK1dGp...', Object(OCA\\Circles\\Model\\FederatedUser), Object(OCA\\Circles\\Model\\Probes\\MemberProbe))\\n#2 /var/www/html/apps-extra/circles/lib/Controller/LocalController.php(376): OCA\\Circles\\Service\\MemberService->getMemberById('Cd2CGENqqHK1dGp...', 'unA8Pi4zzB18bzD...')\\n#3 /var/www/html/lib/private/AppFramework/Http/Dispatcher.php(165): OCA\\Circles\\Controller\\LocalController->memberConfirm('unA8Pi4zzB18bzD...', 'Cd2CGENqqHK1dGp...')\\n#4 /var/www/html/lib/private/AppFramework/Http/Dispatcher.php(78): OC\\AppFramework\\Http\\Dispatcher->executeController(Object(OCA\\Circles\\Controller\\LocalController), 'memberConfirm')\\n#5 /var/www/html/lib/private/AppFramework/App.php(137): OC\\AppFramework\\Http\\Dispatcher->dispatch(Object(OCA\\Circles\\Controller\\LocalController), 'memberConfirm')\\n#6 /var/www/html/lib/private/Route/Router.php(324): OC\\AppFramework\\App::main('OCA\\\\Circles\\\\Con...', 'memberConfirm', Object(OC\\AppFramework\\DependencyInjection\\DIContainer), Array)\\n#7 /var/www/html/ocs/v1.php(78): OC\\Route\\Router->match('/ocsapp/apps/ci...')\\n#8 /var/www/html/ocs/v2.php(10): require_once('/var/www/html/o...')\\n#9 {main}\"}","CustomMessage":"{\"circleId\":\"unA8Pi4zzB18bzD9WRsLZ7Yj6JsyjLC\",\"memberId\":\"Cd2CGENqqHK1dGpa9wFJJ4tKS9kytwL\"}"}}

another error for the same case from a different system, maybe it's useful:

{"reqId":"VItLHnJ6lCJ0QHIpGDjd","level":3,"time":"2026-08-19 11:30:50","remoteAddr":"xxx","user":"TeamModerator","app":"circles","method":"PUT","url":"/ocs/v2.php/apps/circles/circles/FjbM8Rj6TjWUNGUUG2TLIqZ7vyJIbkd/members/NeqYFrRKk2srSmIc3niWcsn9Qg1gSx5","scriptName":"/ocs/v2.php","message":"{\"circleId\":\"FjbM8Rj6TjWUNGUUG2TLIqZ7vyJIbkd\",\"memberId\":\"NeqYFrRKk2srSmIc3niWcsn9Qg1gSx5\"}","userAgent":"xxx","version":"34.0.3.2","exception":{"Exception":"OCA\\Circles\\Exceptions\\FederatedItemBadRequestException","Message":"Already member of the team","Code":122,"Trace":[{"file":"/var/www/html/nextcloud/apps/circles/lib/FederatedItems/SingleMemberAdd.php","line":243,"function":"manageMemberStatus","class":"OCA\\Circles\\FederatedItems\\SingleMemberAdd","type":"->"},{"file":"/var/www/html/nextcloud/apps/circles/lib/FederatedItems/SingleMemberAdd.php","line":114,"function":"generateMember","class":"OCA\\Circles\\FederatedItems\\SingleMemberAdd","type":"->"},{"file":"/var/www/html/nextcloud/apps/circles/lib/Service/FederatedEventService.php","line":116,"function":"verify","class":"OCA\\Circles\\FederatedItems\\SingleMemberAdd","type":"->"},{"file":"/var/www/html/nextcloud/apps/circles/lib/Service/MemberService.php","line":223,"function":"newEvent","class":"OCA\\Circles\\Service\\FederatedEventService","type":"->"},{"file":"/var/www/html/nextcloud/apps/circles/lib/Controller/LocalController.php","line":380,"function":"addMember","class":"OCA\\Circles\\Service\\MemberService","type":"->"},{"file":"/var/www/html/nextcloud/lib/private/AppFramework/Http/Dispatcher.php","line":165,"function":"memberConfirm","class":"OCA\\Circles\\Controller\\LocalController","type":"->"},{"file":"/var/www/html/nextcloud/lib/private/AppFramework/Http/Dispatcher.php","line":78,"function":"executeController","class":"OC\\AppFramework\\Http\\Dispatcher","type":"->"},{"file":"/var/www/html/nextcloud/lib/private/AppFramework/App.php","line":137,"function":"dispatch","class":"OC\\AppFramework\\Http\\Dispatcher","type":"->"},{"file":"/var/www/html/nextcloud/lib/private/Route/Router.php","line":324,"function":"main","class":"OC\\AppFramework\\App","type":"::"},{"file":"/var/www/html/nextcloud/ocs/v1.php","line":78,"function":"match","class":"OC\\Route\\Router","type":"->"},{"file":"/var/www/html/nextcloud/ocs/v2.php","line":10,"args":["/var/www/html/nextcloud/ocs/v1.php"],"function":"require_once"}],"File":"/var/www/html/nextcloud/apps/circles/lib/FederatedItems/SingleMemberAdd.php","Line":296,"message":"{\"circleId\":\"FjbM8Rj6TjWUNGUUG2TLIqZ7vyJIbkd\",\"memberId\":\"NeqYFrRKk2srSmIc3niWcsn9Qg1gSx5\"}","exception":"{\"class\":\"OCA\\Circles\\Exceptions\\FederatedItemBadRequestException\",\"message\":\"Already member of the team\",\"code\":122,\"file\":\"/var/www/html/nextcloud/apps/circles/lib/FederatedItems/SingleMemberAdd.php:296\",\"trace\":\"#0 /var/www/html/nextcloud/apps/circles/lib/FederatedItems/SingleMemberAdd.php(243): OCA\\Circles\\FederatedItems\\SingleMemberAdd->manageMemberStatus()\\n#1 /var/www/html/nextcloud/apps/circles/lib/FederatedItems/SingleMemberAdd.php(114): OCA\\Circles\\FederatedItems\\SingleMemberAdd->generateMember()\\n#2 /var/www/html/nextcloud/apps/circles/lib/Service/FederatedEventService.php(116): OCA\\Circles\\FederatedItems\\SingleMemberAdd->verify()\\n#3 /var/www/html/nextcloud/apps/circles/lib/Service/MemberService.php(223): OCA\\Circles\\Service\\FederatedEventService->newEvent()\\n#4 /var/www/html/nextcloud/apps/circles/lib/Controller/LocalController.php(380): OCA\\Circles\\Service\\MemberService->addMember()\\n#5 /var/www/html/nextcloud/lib/private/AppFramework/Http/Dispatcher.php(165): OCA\\Circles\\Controller\\LocalController->memberConfirm()\\n#6 /var/www/html/nextcloud/lib/private/AppFramework/Http/Dispatcher.php(78): OC\\AppFramework\\Http\\Dispatcher->executeController()\\n#7 /var/www/html/nextcloud/lib/private/AppFramework/App.php(137): OC\\AppFramework\\Http\\Dispatcher->dispatch()\\n#8 /var/www/html/nextcloud/lib/private/Route/Router.php(324): OC\\AppFramework\\App::main()\\n#9 /var/www/html/nextcloud/ocs/v1.php(78): OC\\Route\\Router->match()\\n#10 /var/www/html/nextcloud/ocs/v2.php(10): require_once('...')\\n#11 {main}\"}","CustomMessage":"{\"circleId\":\"FjbM8Rj6TjWUNGUUG2TLIqZ7vyJIbkd\",\"memberId\":\"NeqYFrRKk2srSmIc3niWcsn9Qg1gSx5\"}"}}

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at apps-extra/circles/lib/Controller/LocalController.php memberConfirm, then trace MemberService.php, MemberRequest.php, and MemberRequestBuilder.php. Review the approval path and the related SingleMemberAdd.php flow shown in the logs. Done means one moderator’s approval is final, later stale actions do not undo it, and repeated approval produces a useful result instead of a generic 404.

Written by the indexing model from the issue text.

Assessment

Tech stack
php
Domain
api, authorization, backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.