Repository: owncloud/ocis
Pull request #5625 (2023-02-22 12:25:44)

graph: Allow disabling user by adding to local group
## Description
This allows for a local admin to disable a user by adding to a local group

* A new config option for disabling users with the options "none", "attribute" and "group".
* Disable/enable group name DN as config parameter
* Adding/removing users to specified group on user update
* Changing log level for service initialization failure to error

## Related Issue
- This adds the management of the local disabling of users for #5554 

## How Has This Been Tested?
This has been tested locally using curl and adding unit tests.

## Types of changes
- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
- [ ] Technical debt
- [ ] Tests only (no source changes)

## Checklist:
- [x] Code changes
- [x] Unit tests added
- [ ] Acceptance tests added
- [ ] Documentation ticket raised: <link> 


update-docs[bot]: Thanks for opening this pull request! The maintainers of this repository would appreciate it if you would create a [changelog](https://github.com/owncloud/ocis/blob/master/changelog/README.md) item based on your changes.

sonarcloud[bot]: SonarCloud Quality Gate failed.&nbsp; &nbsp; [![Quality Gate failed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/failed-16px.png 'Quality Gate failed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=5625)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=BUG) [![C](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/C-16px.png 'C')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=BUG) [1 Bug](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=5625&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=5625&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=5625&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=CODE_SMELL)

[![4.8%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/0-16px.png '4.8%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_coverage&view=list) [4.8% Coverage](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_coverage&view=list)  
[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=5625)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=5625&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=5625&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=5625&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=CODE_SMELL)

[![4.8%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/0-16px.png '4.8%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_coverage&view=list) [4.8% Coverage](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_coverage&view=list)  
[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_duplicated_lines_density&view=list)


mmattel: Just one question, what happens if the boolean is true but no group was configured?
Excds: > 

There's a default value. I should probably add that to the description...
mmattel: > There's a default value.

that's ok, but what happens if one overwrites the default by empty?
is there an error logged, or does the service go mad, or nothing happens, etc?
I am just asking because there is the dependency to the binary setting.
rhafer: @Excds BTW, would you mind removing the issue number from the commit messages summary. It's better to add a line with:

```
Fixes: #<issuenumber>
```

To the body of the commit message. That way the issue will be automatically closed once the PR is merged.
sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=5625)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=5625&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=5625&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=5625&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=CODE_SMELL) [1 Code Smell](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=CODE_SMELL)

[![49.3%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/40-16px.png '49.3%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_coverage&view=list) [49.3% Coverage](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_coverage&view=list)  
[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: SonarCloud Quality Gate failed.&nbsp; &nbsp; [![Quality Gate failed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/failed-16px.png 'Quality Gate failed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=5625)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=BUG) [![C](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/C-16px.png 'C')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=BUG) [3 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=5625&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=5625&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=5625&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=CODE_SMELL) [3 Code Smells](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=CODE_SMELL)

[![63.2%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/60-16px.png '63.2%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_coverage&view=list) [63.2% Coverage](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_coverage&view=list)  
[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=5625)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=5625&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=5625&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=5625&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=CODE_SMELL) [3 Code Smells](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=CODE_SMELL)

[![63.2%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/60-16px.png '63.2%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_coverage&view=list) [63.2% Coverage](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_coverage&view=list)  
[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_duplicated_lines_density&view=list)


mmattel: Pls check the open text changes and commit them before merging. Post working on them is an unnecessary job.
sonarcloud[bot]: SonarCloud Quality Gate failed.&nbsp; &nbsp; [![Quality Gate failed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/failed-16px.png 'Quality Gate failed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=5625)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=BUG) [![C](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/C-16px.png 'C')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=BUG) [2 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=5625&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=5625&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=5625&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=CODE_SMELL) [1 Code Smell](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=CODE_SMELL)

[![62.5%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/60-16px.png '62.5%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_coverage&view=list) [62.5% Coverage](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_coverage&view=list)  
[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=5625)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=5625&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=5625&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=5625&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=CODE_SMELL) [1 Code Smell](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=CODE_SMELL)

[![62.5%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/60-16px.png '62.5%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_coverage&view=list) [62.5% Coverage](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_coverage&view=list)  
[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_duplicated_lines_density&view=list)


Excds: > Pls check the open text changes and commit them before merging. Post working on them is an unnecessary job.

I updated with your suggestions, but updated to the slightly changed functionality.
sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=5625)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=5625&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=5625&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=5625&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=CODE_SMELL) [1 Code Smell](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=CODE_SMELL)

[![62.5%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/60-16px.png '62.5%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_coverage&view=list) [62.5% Coverage](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_coverage&view=list)  
[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=5625)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=5625&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=5625&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=5625&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=CODE_SMELL) [1 Code Smell](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=CODE_SMELL)

[![62.8%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/60-16px.png '62.8%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_coverage&view=list) [62.8% Coverage](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_coverage&view=list)  
[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=5625)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=5625&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=5625&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=5625&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=CODE_SMELL) [1 Code Smell](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=CODE_SMELL)

[![62.8%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/60-16px.png '62.8%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_coverage&view=list) [62.8% Coverage](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_coverage&view=list)  
[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_duplicated_lines_density&view=list)


mmattel: Addes a description for `none` as discussed 😄 

Descriptions look ok 👍 
sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=5625)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=5625&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=5625&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=5625&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=CODE_SMELL) [1 Code Smell](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=CODE_SMELL)

[![62.8%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/60-16px.png '62.8%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_coverage&view=list) [62.8% Coverage](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_coverage&view=list)  
[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=5625)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=5625&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=5625&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=5625&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=CODE_SMELL) [1 Code Smell](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=CODE_SMELL)

[![62.8%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/60-16px.png '62.8%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_coverage&view=list) [62.8% Coverage](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_coverage&view=list)  
[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_duplicated_lines_density&view=list)


mmattel: @Excds The latest commit has undone the description text changes most likely by not rebasing before doing the change. Pls commit teh suggestions and rebase to avoid that this comes up again. 
sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=5625)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=5625&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=5625&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=5625&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=CODE_SMELL) [1 Code Smell](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=CODE_SMELL)

[![64.3%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/60-16px.png '64.3%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_coverage&view=list) [64.3% Coverage](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_coverage&view=list)  
[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=5625)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=5625&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=5625&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=5625&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=CODE_SMELL) [14 Code Smells](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=CODE_SMELL)

[![64.3%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/60-16px.png '64.3%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_coverage&view=list) [64.3% Coverage](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_coverage&view=list)  
[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=5625)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=5625&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=5625&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=5625&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=CODE_SMELL) [1 Code Smell](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=CODE_SMELL)

[![64.3%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/60-16px.png '64.3%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_coverage&view=list) [64.3% Coverage](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_coverage&view=list)  
[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_duplicated_lines_density&view=list)


ownclouders: :boom: Acceptance test [<strong>Web-Tests-ocis-smoke-ocis-storage-2</strong>](https://drone.owncloud.com/owncloud/ocis/19737/63/1) failed. Further test are cancelled...
<!-- id: pr-5625 -->

sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=5625)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=5625&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=5625&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=5625&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=CODE_SMELL) [1 Code Smell](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=CODE_SMELL)

[![64.3%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/60-16px.png '64.3%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_coverage&view=list) [64.3% Coverage](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_coverage&view=list)  
[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=5625)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=5625&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=5625&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=5625&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=CODE_SMELL) [14 Code Smells](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=5625&resolved=false&types=CODE_SMELL)

[![64.8%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/60-16px.png '64.8%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_coverage&view=list) [64.8% Coverage](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_coverage&view=list)  
[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=5625&metric=new_duplicated_lines_density&view=list)



Patch:
diff --git a/services/graph/pkg/command/server.go b/services/graph/pkg/command/server.go
index e962eb29a2d..44a9600bf57 100644
--- a/services/graph/pkg/command/server.go
+++ b/services/graph/pkg/command/server.go
@@ -60,7 +60,7 @@ func Server(cfg *config.Config) *cli.Command {
 				)
 
 				if err != nil {
-					logger.Info().Err(err).Str("transport", "http").Msg("Failed to initialize server")
+					logger.Error().Err(err).Str("transport", "http").Msg("Failed to initialize server")
 					return err
 				}
 
diff --git a/services/graph/pkg/config/config.go b/services/graph/pkg/config/config.go
index cd2c8be5c75..385de2bab52 100644
--- a/services/graph/pkg/config/config.go
+++ b/services/graph/pkg/config/config.go
@@ -62,6 +62,8 @@ type LDAP struct {
 	UserNameAttribute        string `yaml:"user_name_attribute" env:"LDAP_USER_SCHEMA_USERNAME;GRAPH_LDAP_USER_NAME_ATTRIBUTE" desc:"LDAP Attribute to use for username of users."`
 	UserIDAttribute          string `yaml:"user_id_attribute" env:"LDAP_USER_SCHEMA_ID;GRAPH_LDAP_USER_UID_ATTRIBUTE" desc:"LDAP Attribute to use as the unique ID for users. This should be a stable globally unique ID like a UUID."`
 	UserEnabledAttribute     string `yaml:"user_enabled_attribute" env:"LDAP_USER_ENABLED_ATTRIBUTE;GRAPH_USER_ENABLED_ATTRIBUTE" desc:"LDAP Attribute to use as a flag telling if the user is enabled or disabled."`
+	DisableUserMechanism     string `yaml:"disable_user_mechanism" env:"LDAP_DISABLE_USER_MECHANISM;GRAPH_DISABLE_USER_MECHANISM" desc:"An option to control the behavior for disabling users. Valid options are 'none', 'attribute' and 'group'. If set to 'group', disabling a user via API will add the user to the configured group for disabled users, if set to 'attribute' this will be done in the ldap user entry, if set to 'none' the disable request is not processed. Default is 'attribute'."`
+	LdapDisabledUsersGroupDN string `yaml:"ldap_disabled_users_group_dn" env:"LDAP_DISABLED_USERS_GROUP_DN;GRAPH_DISABLED_USERS_GROUP_DN" desc:"The distinguished name of the group to which added users will be classified as disabled when 'disable_user_mechanism' is set to 'group'."`
 
 	GroupBaseDN        string `yaml:"group_base_dn" env:"LDAP_GROUP_BASE_DN;GRAPH_LDAP_GROUP_BASE_DN" desc:"Search base DN for looking up LDAP groups."`
 	GroupSearchScope   string `yaml:"group_search_scope" env:"LDAP_GROUP_SCOPE;GRAPH_LDAP_GROUP_SEARCH_SCOPE" desc:"LDAP search scope to use when looking up groups. Supported scopes are 'base', 'one' and 'sub'."`
diff --git a/services/graph/pkg/config/defaults/defaultconfig.go b/services/graph/pkg/config/defaults/defaultconfig.go
index 0819334ebaf..7b9a4563db5 100644
--- a/services/graph/pkg/config/defaults/defaultconfig.go
+++ b/services/graph/pkg/config/defaults/defaultconfig.go
@@ -69,6 +69,8 @@ func DefaultConfig() *config.Config {
 				//        ideally this needs to	be constant for the lifetime of a users
 				UserIDAttribute:           "owncloudUUID",
 				UserEnabledAttribute:      "ownCloudUserEnabled",
+				DisableUserMechanism:      "attribute",
+				LdapDisabledUsersGroupDN:  "cn=DisabledUsersGroup,ou=groups,o=libregraph-idm",
 				GroupBaseDN:               "ou=groups,o=libregraph-idm",
 				GroupSearchScope:          "sub",
 				GroupFilter:               "",
diff --git a/services/graph/pkg/identity/ldap.go b/services/graph/pkg/identity/ldap.go
index b126204b5cb..7d1d19e5f27 100644
--- a/services/graph/pkg/identity/ldap.go
+++ b/services/graph/pkg/identity/ldap.go
@@ -20,10 +20,29 @@ import (
 )
 
 const (
-	givenNameAttribute = "givenname"
-	surNameAttribute   = "sn"
+	_givenNameAttribute        = "givenname"
+	_surNameAttribute          = "sn"
+	_ldapGroupOfNamesAttribute = "(objectClass=groupOfNames)"
+	_ldapGroupMemberAttribute  = "member"
 )
 
+// DisableUserMechanismType is used instead of directly using the string values from the configuration.
+type DisableUserMechanismType int64
+
+// The different DisableMechanism* constants are used for managing the enabling/disabling of users.
+const (
+	DisableMechanismNone DisableUserMechanismType = iota
+	DisableMechanismAttribute
+	DisableMechanismGroup
+)
+
+var mechanismMap = map[string]DisableUserMechanismType{
+	"":          DisableMechanismNone,
+	"none":      DisableMechanismNone,
+	"attribute": DisableMechanismAttribute,
+	"group":     DisableMechanismGroup,
+}
+
 type LDAP struct {
 	useServerUUID   bool
 	writeEnabled    bool
@@ -36,6 +55,9 @@ type LDAP struct {
 	userScope        int
 	userAttributeMap userAttributeMap
 
+	disableUserMechanism    DisableUserMechanismType
+	localUserDisableGroupDN string
+
 	groupBaseDN       string
 	groupFilter       string
 	groupObjectClass  string
@@ -60,6 +82,17 @@ type userAttributeMap struct {
 
 type ldapAttributeValues map[string][]string
 
+// ParseDisableMechanismType checks that the configuration option for how to disable users is correct.
+func ParseDisableMechanismType(disableMechanism string) (DisableUserMechanismType, error) {
+	disableMechanism = strings.ToLower(disableMechanism)
+	t, ok := mechanismMap[disableMechanism]
+	if !ok {
+		return -1, errors.New("invalid configuration option for disable user mechanism")
+	}
+
+	return t, nil
+}
+
 func NewLDAPBackend(lc ldap.Client, config config.LDAP, logger *log.Logger) (*LDAP, error) {
 	if config.UserDisplayNameAttribute == "" || config.UserIDAttribute == "" ||
 		config.UserEmailAttribute == "" || config.UserNameAttribute == "" {
@@ -71,8 +104,8 @@ func NewLDAPBackend(lc ldap.Client, config config.LDAP, logger *log.Logger) (*LD
 		mail:           config.UserEmailAttribute,
 		userName:       config.UserNameAttribute,
 		accountEnabled: config.UserEnabledAttribute,
-		givenName:      givenNameAttribute,
-		surname:        surNameAttribute,
+		givenName:      _givenNameAttribute,
+		surname:        _surNameAttribute,
 	}
 
 	if config.GroupNameAttribute == "" || config.GroupIDAttribute == "" {
@@ -81,7 +114,7 @@ func NewLDAPBackend(lc ldap.Client, config config.LDAP, logger *log.Logger) (*LD
 	gam := groupAttributeMap{
 		name:         config.GroupNameAttribute,
 		id:           config.GroupIDAttribute,
-		member:       "member",
+		member:       _ldapGroupMemberAttribute,
 		memberSyntax: "dn",
 	}
 
@@ -100,24 +133,31 @@ func NewLDAPBackend(lc ldap.Client, config config.LDAP, logger *log.Logger) (*LD
 		return nil, fmt.Errorf("error setting up education resource config: %w", err)
 	}
 
+	disableMechanismType, err := ParseDisableMechanismType(config.DisableUserMechanism)
+	if err != nil {
+		return nil, fmt.Errorf("error configuring disable user mechanism: %w", err)
+	}
+
 	return &LDAP{
-		useServerUUID:     config.UseServerUUID,
-		usePwModifyExOp:   config.UsePasswordModExOp,
-		userBaseDN:        config.UserBaseDN,
-		userFilter:        config.UserFilter,
-		userObjectClass:   config.UserObjectClass,
-		userScope:         userScope,
-		userAttributeMap:  uam,
-		groupBaseDN:       config.GroupBaseDN,
-		groupFilter:       config.GroupFilter,
-		groupObjectClass:  config.GroupObjectClass,
-		groupScope:        groupScope,
-		groupAttributeMap: gam,
-		educationConfig:   educationConfig,
-		logger:            logger,
-		conn:              lc,
-		writeEnabled:      config.WriteEnabled,
-		refintEnabled:     config.RefintEnabled,
+		useServerUUID:           config.UseServerUUID,
+		usePwModifyExOp:         config.UsePasswordModExOp,
+		userBaseDN:              config.UserBaseDN,
+		userFilter:              config.UserFilter,
+		userObjectClass:         config.UserObjectClass,
+		userScope:               userScope,
+		userAttributeMap:        uam,
+		groupBaseDN:             config.GroupBaseDN,
+		groupFilter:             config.GroupFilter,
+		groupObjectClass:        config.GroupObjectClass,
+		groupScope:              groupScope,
+		groupAttributeMap:       gam,
+		educationConfig:         educationConfig,
+		disableUserMechanism:    disableMechanismType,
+		localUserDisableGroupDN: config.LdapDisabledUsersGroupDN,
+		logger:                  logger,
+		conn:                    lc,
+		writeEnabled:            config.WriteEnabled,
+		refintEnabled:           config.RefintEnabled,
 	}, nil
 }
 
@@ -131,6 +171,10 @@ func (i *LDAP) CreateUser(ctx context.Context, user libregraph.User) (*libregrap
 		return nil, ErrReadOnly
 	}
 
+	if user.AccountEnabled != nil && i.disableUserMechanism == DisableMechanismNone {
+		return nil, errors.New("accountEnabled option not compatible with backend disable user mechanism")
+	}
+
 	ar, err := i.userToAddRequest(user)
 	if err != nil {
 		return nil, err
@@ -254,14 +298,34 @@ func (i *LDAP) UpdateUser(ctx context.Context, nameOrID string, user libregraph.
 		}
 	}
 
+	// Behavior of enabling/disabling of users depends on the "disableUserMechanism" config option:
+	//
+	// "attribute": For the upstream user management service which modifies accountEnabled on the user entry
+	// "group": Makes it possible for local admins to disable users by adding them to a special group
 	if user.AccountEnabled != nil {
-		boolString := strings.ToUpper(strconv.FormatBool(*user.AccountEnabled))
-		ldapValue := e.GetEqualFoldAttributeValue(i.userAttributeMap.accountEnabled)
-		updateNeeded = true
-		if ldapValue != "" {
-			mr.Replace(i.userAttributeMap.accountEnabled, []string{boolString})
-		} else {
-			mr.Add(i.userAttributeMap.accountEnabled, []string{boolString})
+		switch i.disableUserMechanism {
+		case DisableMechanismNone:
+			return nil, errors.New("accountEnabled option not compatible with backend disable user mechanism")
+		case DisableMechanismAttribute:
+			boolString := strings.ToUpper(strconv.FormatBool(user.GetAccountEnabled()))
+			ldapValue := e.GetEqualFoldAttributeValue(i.userAttributeMap.accountEnabled)
+			if ldapValue != "" {
+				mr.Replace(i.userAttributeMap.accountEnabled, []string{boolString})
+			} else {
+				mr.Add(i.userAttributeMap.accountEnabled, []string{boolString})
+			}
+			updateNeeded = true
+		case DisableMechanismGroup:
+			if user.GetAccountEnabled() {
+				err = i.enableUser(logger, e.DN)
+			} else {
+				err = i.disableUser(logger, e.DN)
+			}
+
+			if err != nil {
+				return nil, err
+			}
+			updateNeeded = true
 		}
 	}
 
@@ -276,7 +340,16 @@ func (i *LDAP) UpdateUser(ctx context.Context, nameOrID string, user libregraph.
 	if err != nil {
 		return nil, err
 	}
-	return i.createUserModelFromLDAP(e), nil
+
+	returnUser := i.createUserModelFromLDAP(e)
+
+	// To avoid an ldap lookup for group membership, set the enabled flag to same as input value
+	// since this would have been updated with group membership from the input anyway.
+	if user.AccountEnabled != nil && i.disableUserMechanism != DisableMechanismNone {
+		returnUser.AccountEnabled = user.AccountEnabled
+	}
+
+	return returnUser, nil
 }
 
 func (i *LDAP) getUserByDN(dn string) (*ldap.Entry, error) {
@@ -320,7 +393,7 @@ func (i *LDAP) getEntryByDN(dn string, attrs []string, filter string) (*ldap.Ent
 		Msg("getEntryByDN")
 	res, err := i.conn.Search(searchRequest)
 	if err != nil {
-		i.logger.Error().Err(err).Str("backend", "ldap").Str("dn", dn).Msg("Search user by DN failed")
+		i.logger.Error().Err(err).Str("backend", "ldap").Str("dn", dn).Msg("Search ldap by DN failed")
 		return nil, errorcode.New(errorcode.ItemNotFound, err.Error())
 	}
 	if len(res.Entries) == 0 {
@@ -404,6 +477,13 @@ func (i *LDAP) GetUser(ctx context.Context, nameOrID string, oreq *godata.GoData
 		return nil, ErrNotFound
 	}
 
+	if i.disableUserMechanism != DisableMechanismNone {
+		userEnabled, err := i.UserEnabled(e)
+		if err == nil {
+			u.AccountEnabled = &userEnabled
+		}
+	}
+
 	exp, err := GetExpandValues(oreq.Query)
 	if err != nil {
 		return nil, err
@@ -472,6 +552,10 @@ func (i *LDAP) GetUsers(ctx context.Context, oreq *godata.GoDataRequest) ([]*lib
 	}
 
 	users := make([]*libregraph.User, 0, len(res.Entries))
+	usersEnabledState, err := i.usersEnabledState(res.Entries)
+	if err != nil {
+		return nil, err
+	}
 
 	for _, e := range res.Entries {
 		u := i.createUserModelFromLDAP(e)
@@ -479,6 +563,12 @@ func (i *LDAP) GetUsers(ctx context.Context, oreq *godata.GoDataRequest) ([]*lib
 		if u == nil {
 			continue
 		}
+
+		if i.disableUserMechanism != DisableMechanismNone {
+			userEnabled := usersEnabledState[e.DN]
+			u.AccountEnabled = &userEnabled
+		}
+
 		if slices.Contains(exp, "memberOf") {
 			userGroups, err := i.getGroupsForUser(e.DN)
 			if err != nil {
@@ -802,3 +892,135 @@ func replaceDN(fullDN *ldap.DN, newDN string) (string, error) {
 
 	return newDN, nil
 }
+
+// CreateLDAPGroupByDN is a helper method specifically intended for creating a "system" group
+// for managing locally disabled users on service startup
+func (i *LDAP) CreateLDAPGroupByDN(dn string) error {
+	ar := ldap.NewAddRequest(dn, nil)
+
+	attrs := map[string][]string{
+		"objectClass": {"groupOfNames", "top"},
+		"member":      {""},
+	}
+
+	for attrType, values := range attrs {
+		ar.Attribute(attrType, values)
+	}
+
+	return i.conn.Add(ar)
+}
+
+func (i *LDAP) disableUser(logger log.Logger, userDN string) (err error) {
+	group, err := i.getEntryByDN(i.localUserDisableGroupDN, []string{_ldapGroupMemberAttribute}, _ldapGroupOfNamesAttribute)
+
+	if err != nil {
+		return err
+	}
+
+	mr := ldap.ModifyRequest{DN: group.DN}
+	mr.Add(_ldapGroupMemberAttribute, []string{userDN})
+
+	err = i.conn.Modify(&mr)
+	var lerr *ldap.Error
+	if errors.As(err, &lerr) {
+		// If the user is already in the group, just log a message and return
+		if lerr.ResultCode == ldap.LDAPResultAttributeOrValueExists {
+			logger.Info().Msg("User already in group for disabled users")
+			return nil
+		}
+	}
+
+	return err
+}
+
+func (i *LDAP) enableUser(logger log.Logger, userDN string) (err error) {
+	group, err := i.getEntryByDN(i.localUserDisableGroupDN, []string{_ldapGroupMemberAttribute}, _ldapGroupOfNamesAttribute)
+
+	if err != nil {
+		return err
+	}
+
+	mr := ldap.ModifyRequest{DN: group.DN}
+	mr.Delete(_ldapGroupMemberAttribute, []string{userDN})
+
+	err = i.conn.Modify(&mr)
+	var lerr *ldap.Error
+	if errors.As(err, &lerr) {
+		// If the user is not in the group, just log a message and return
+		if lerr.ResultCode == ldap.LDAPResultNoSuchAttribute {
+			logger.Info().Msg("User was not in group for disabled users")
+			return nil
+		}
+	}
+
+	return err
+}
+
+func (i *LDAP) userEnabledByAttribute(user *ldap.Entry) bool {
+	enabledAttribute := booleanOrNil(user.GetEqualFoldAttributeValue(i.userAttributeMap.accountEnabled))
+
+	if enabledAttribute == nil {
+		return true
+	}
+
+	return *enabledAttribute
+}
+
+func (i *LDAP) usersEnabledStateFromGroup(users []string) (usersEnabledState map[string]bool, err error) {
+	group, err := i.getEntryByDN(i.localUserDisableGroupDN, []string{_ldapGroupMemberAttribute}, _ldapGroupOfNamesAttribute)
+
+	if err != nil {
+		return nil, err
+	}
+
+	usersEnabledState = make(map[string]bool, len(users))
+	for _, user := range users {
+		usersEnabledState[user] = true
+	}
+
+	for _, memberDN := range group.GetEqualFoldAttributeValues(_ldapGroupMemberAttribute) {
+		usersEnabledState[memberDN] = false
+	}
+
+	return usersEnabledState, err
+}
+
+// UserEnabled returns if a user is enabled. This can depend on a flag in the user entry or group membership
+func (i *LDAP) UserEnabled(user *ldap.Entry) (bool, error) {
+	usersEnabledState, err := i.usersEnabledState([]*ldap.Entry{user})
+
+	if err != nil {
+		return false, err
+	}
+
+	return usersEnabledState[user.DN], nil
+}
+
+func (i *LDAP) usersEnabledState(users []*ldap.Entry) (usersEnabledState map[string]bool, err error) {
+	usersEnabledState = make(map[string]bool, len(users))
+	keys := make([]string, len(users))
+	for index, user := range users {
+		usersEnabledState[user.DN] = true
+		keys[index] = user.DN
+	}
+
+	switch i.disableUserMechanism {
+	case DisableMechanismAttribute:
+		for _, user := range users {
+			usersEnabledState[user.DN] = i.userEnabledByAttribute(user)
+		}
+
+	case DisableMechanismGroup:
+		userDisabledGroupState, err := i.usersEnabledStateFromGroup(keys)
+
+		if err != nil {
+			return nil, err
+		}
+
+		for _, user := range keys {
+			usersEnabledState[user] = userDisabledGroupState[user]
+		}
+	}
+
+	return usersEnabledState, nil
+}
diff --git a/services/graph/pkg/service/v0/service.go b/services/graph/pkg/service/v0/service.go
index 9cc30c0ba90..e1233661336 100644
--- a/services/graph/pkg/service/v0/service.go
+++ b/services/graph/pkg/service/v0/service.go
@@ -3,6 +3,7 @@ package svc
 import (
 	"crypto/tls"
 	"crypto/x509"
+	"errors"
 	"fmt"
 	"net/http"
 	"os"
@@ -12,6 +13,7 @@ import (
 
 	"github.com/go-chi/chi/v5"
 	"github.com/go-chi/chi/v5/middleware"
+	ldapv3 "github.com/go-ldap/ldap/v3"
 	"github.com/jellydator/ttlcache/v3"
 	libregraph "github.com/owncloud/libre-graph-api-go"
 	ocisldap "github.com/owncloud/ocis/v2/ocis-pkg/ldap"
@@ -366,6 +368,35 @@ func setIdentityBackends(options Options, svc *Graph) error {
 					svc.identityEducationBackend = errEduBackend
 				}
 			}
+
+			disableMechanismType, err := identity.ParseDisableMechanismType(options.Config.Identity.LDAP.DisableUserMechanism)
+			if err != nil {
+				options.Logger.Error().Err(err).Msg("Error initializing LDAP Backend")
+				return err
+			}
+
+			if disableMechanismType == identity.DisableMechanismGroup {
+				options.Logger.Info().Msg("LocalUserDisable is true, will create group if not exists")
+				err := lb.CreateLDAPGroupByDN(options.Config.Identity.LDAP.LdapDisabledUsersGroupDN)
+				if err != nil {
+					isAnError := false
+					var lerr *ldapv3.Error
+					if errors.As(err, &lerr) {
+						if lerr.ResultCode != ldapv3.LDAPResultEntryAlreadyExists {
+							isAnError = true
+						}
+					} else {
+						isAnError = true
+					}
+
+					if isAnError {
+						msg := "error adding group for disabling users"
+						options.Logger.Error().Err(err).Str("local_user_disable", options.Config.Identity.LDAP.LdapDisabledUsersGroupDN).Msg(msg)
+						return err
+					}
+				}
+			}
+
 		default:
 			err := fmt.Errorf("unknown identity backend: '%s'", options.Config.Identity.Backend)
 			options.Logger.Err(err)


Test patch:
diff --git a/services/graph/pkg/identity/ldap_test.go b/services/graph/pkg/identity/ldap_test.go
index 04f5f479f23..bf99dca8160 100644
--- a/services/graph/pkg/identity/ldap_test.go
+++ b/services/graph/pkg/identity/ldap_test.go
@@ -21,6 +21,11 @@ func getMockedBackend(l ldap.Client, lc config.LDAP, logger *log.Logger) (*LDAP,
 	return NewLDAPBackend(l, lc, logger)
 }
 
+const (
+	disableUsersGroup = "cn=DisabledUsersGroup,ou=groups,o=testing"
+	groupSearchFilter = "(objectClass=groupOfNames)"
+)
+
 var lconfig = config.LDAP{
 	UserBaseDN:               "ou=people,dc=test",
 	UserObjectClass:          "inetOrgPerson",
@@ -31,6 +36,8 @@ var lconfig = config.LDAP{
 	UserEmailAttribute:       "mail",
 	UserNameAttribute:        "uid",
 	UserEnabledAttribute:     "userEnabledAttribute",
+	LdapDisabledUsersGroupDN: disableUsersGroup,
+	DisableUserMechanism:     "attribute",
 
 	GroupBaseDN:        "ou=groups,dc=test",
 	GroupObjectClass:   "groupOfNames",
@@ -285,16 +292,20 @@ func TestGetUsers(t *testing.T) {
 }
 
 func TestUpdateUser(t *testing.T) {
+	falseBool := false
+	trueBool := true
+
 	type userProps struct {
 		id                       string
 		mail                     string
 		displayName              string
 		onPremisesSamAccountName string
-		accountEnabled           bool
+		accountEnabled           *bool
 	}
 	type args struct {
-		nameOrID  string
-		userProps userProps
+		nameOrID             string
+		userProps            userProps
+		disableUserMechanism string
 	}
 	type mockInputs struct {
 		funcName string
@@ -359,7 +370,7 @@ func TestUpdateUser(t *testing.T) {
 				mail:                     "testuser@example.org",
 				displayName:              "testUser",
 				onPremisesSamAccountName: "testUser",
-				accountEnabled:           true,
+				accountEnabled:           nil,
 			},
 			assertion: func(t assert.TestingT, err error, args ...interface{}) bool {
 				return assert.Nil(t, err, args...)
@@ -397,7 +408,7 @@ func TestUpdateUser(t *testing.T) {
 										},
 										{
 											Name:   lconfig.UserEnabledAttribute,
-											Values: []string{"TRUE"},
+											Values: []string{""},
 										},
 									},
 								},
@@ -445,7 +456,7 @@ func TestUpdateUser(t *testing.T) {
 										},
 										{
 											Name:   lconfig.UserEnabledAttribute,
-											Values: []string{"TRUE"},
+											Values: []string{""},
 										},
 									},
 								},
@@ -488,7 +499,7 @@ func TestUpdateUser(t *testing.T) {
 				mail:                     "testuser@example.org",
 				displayName:              "newName",
 				onPremisesSamAccountName: "testUser",
-				accountEnabled:           true,
+				accountEnabled:           nil,
 			},
 			assertion: func(t assert.TestingT, err error, args ...interface{}) bool {
 				return assert.Nil(t, err, args...)
@@ -526,7 +537,7 @@ func TestUpdateUser(t *testing.T) {
 										},
 										{
 											Name:   lconfig.UserEnabledAttribute,
-											Values: []string{"TRUE"},
+											Values: []string{""},
 										},
 									},
 								},
@@ -574,7 +585,7 @@ func TestUpdateUser(t *testing.T) {
 										},
 										{
 											Name:   lconfig.UserEnabledAttribute,
-											Values: []string{"TRUE"},
+											Values: []string{""},
 										},
 									},
 								},
@@ -617,7 +628,7 @@ func TestUpdateUser(t *testing.T) {
 				mail:                     "testuser@example.org",
 				displayName:              "newName",
 				onPremisesSamAccountName: "newName",
-				accountEnabled:           true,
+				accountEnabled:           nil,
 			},
 			assertion: func(t assert.TestingT, err error, args ...interface{}) bool {
 				return assert.Nil(t, err, args...)
@@ -659,7 +670,7 @@ func TestUpdateUser(t *testing.T) {
 										},
 										{
 											Name:   lconfig.UserEnabledAttribute,
-											Values: []string{"TRUE"},
+											Values: []string{""},
 										},
 									},
 								},
@@ -755,7 +766,7 @@ func TestUpdateUser(t *testing.T) {
 										},
 										{
 											Name:   lconfig.UserEnabledAttribute,
-											Values: []string{"TRUE"},
+											Values: []string{""},
 										},
 									},
 								},
@@ -797,15 +808,16 @@ func TestUpdateUser(t *testing.T) {
 			args: args{
 				nameOrID: "testUser",
 				userProps: userProps{
-					accountEnabled: false,
+					accountEnabled: &falseBool,
 				},
+				disableUserMechanism: "attribute",
 			},
 			want: &userProps{
 				id:                       "testUser",
 				mail:                     "testuser@example.org",
 				displayName:              "testUser",
 				onPremisesSamAccountName: "testUser",
-				accountEnabled:           false,
+				accountEnabled:           &falseBool,
 			},
 			assertion: func(t assert.TestingT, err error, args ...interface{}) bool {
 				return assert.Nil(t, err, args...)
@@ -921,6 +933,338 @@ func TestUpdateUser(t *testing.T) {
 				},
 			},
 		},
+		{
+			name: "Test disabling user as local admin",
+			args: args{
+				nameOrID: "testUser",
+				userProps: userProps{
+					accountEnabled: &falseBool,
+				},
+				disableUserMechanism: "group",
+			},
+			want: &userProps{
+				id:                       "testUser",
+				mail:                     "testuser@example.org",
+				displayName:              "testUser",
+				onPremisesSamAccountName: "testUser",
+				accountEnabled:           &falseBool,
+			},
+			assertion: func(t assert.TestingT, err error, args ...interface{}) bool {
+				return assert.Nil(t, err, args...)
+			},
+			ldapMocks: []mockInputs{
+				{
+					funcName: "Search",
+					args: []interface{}{
+						ldap.NewSearchRequest(
+							"ou=people,dc=test",
+							ldap.ScopeWholeSubtree,
+							ldap.NeverDerefAliases, 1, 0, false,
+							"(&(objectClass=inetOrgPerson)(|(uid=testUser)(entryUUID=testUser)))",
+							[]string{"displayname", "entryUUID", "mail", "uid", "sn", "givenname", "userEnabledAttribute"},
+							nil,
+						),
+					},
+					returns: []interface{}{
+						&ldap.SearchResult{
+							Entries: []*ldap.Entry{
+								{
+									DN: "uid=name",
+									Attributes: []*ldap.EntryAttribute{
+										{
+											Name:   "displayname",
+											Values: []string{"testuser@example.org"},
+										},
+										{
+											Name:   "entryUUID",
+											Values: []string{"testUser"},
+										},
+										{
+											Name:   "mail",
+											Values: []string{"testuser@example.org"},
+										},
+										{
+											Name:   lconfig.UserEnabledAttribute,
+											Values: []string{"TRUE"},
+										},
+									},
+								},
+							},
+						},
+						nil,
+					},
+				},
+				{
+					funcName: "Search",
+					args: []interface{}{
+						&ldap.SearchRequest{
+							BaseDN:       disableUsersGroup,
+							Scope:        0,
+							DerefAliases: 0,
+							SizeLimit:    1,
+							TimeLimit:    0,
+							TypesOnly:    false,
+							Filter:       groupSearchFilter,
+							Attributes:   []string{"member"},
+							Controls:     []ldap.Control(nil),
+						},
+					},
+					returns: []interface{}{
+						&ldap.SearchResult{
+							Entries: []*ldap.Entry{
+								{
+									DN: "uid=name",
+									Attributes: []*ldap.EntryAttribute{
+										{
+											Name:   "member",
+											Values: []string{""},
+										},
+									},
+								},
+							},
+						},
+						nil,
+					},
+				},
+				{
+					funcName: "Modify",
+					args: []interface{}{
+						&ldap.ModifyRequest{
+							DN: "uid=name",
+							Changes: []ldap.Change{
+								{
+									Operation: ldap.AddAttribute,
+									Modification: ldap.PartialAttribute{
+										Type: "member",
+										Vals: []string{"uid=name"},
+									},
+								},
+							},
+							Controls: []ldap.Control(nil),
+						},
+					},
+					returns: []interface{}{nil},
+				},
+				{
+					funcName: "Modify",
+					args: []interface{}{
+						&ldap.ModifyRequest{
+							DN:       "uid=name",
+							Changes:  []ldap.Change(nil),
+							Controls: []ldap.Control(nil),
+						},
+					},
+					returns: []interface{}{nil},
+				},
+				{
+					funcName: "Search",
+					args: []interface{}{
+						ldap.NewSearchRequest(
+							"uid=name",
+							ldap.ScopeBaseObject,
+							ldap.NeverDerefAliases, 1, 0, false,
+							"(objectClass=inetOrgPerson)",
+							[]string{"displayname", "entryUUID", "mail", "uid", "sn", "givenname", "userEnabledAttribute"},
+							[]ldap.Control(nil),
+						),
+					},
+					returns: []interface{}{
+						&ldap.SearchResult{
+							Entries: []*ldap.Entry{
+								{
+									DN: "uid=name",
+									Attributes: []*ldap.EntryAttribute{
+										{
+											Name:   "uid",
+											Values: []string{"testUser"},
+										},
+										{
+											Name:   "displayname",
+											Values: []string{"testUser"},
+										},
+										{
+											Name:   "entryUUID",
+											Values: []string{"testUser"},
+										},
+										{
+											Name:   "mail",
+											Values: []string{"testuser@example.org"},
+										},
+									},
+								},
+							},
+						},
+						nil,
+					},
+				},
+			},
+		},
+		{
+			name: "Test enabling user as local admin",
+			args: args{
+				nameOrID: "testUser",
+				userProps: userProps{
+					accountEnabled: &trueBool,
+				},
+				disableUserMechanism: "group",
+			},
+			want: &userProps{
+				id:                       "testUser",
+				mail:                     "testuser@example.org",
+				displayName:              "testUser",
+				onPremisesSamAccountName: "testUser",
+				accountEnabled:           &trueBool,
+			},
+			assertion: func(t assert.TestingT, err error, args ...interface{}) bool {
+				return assert.Nil(t, err, args...)
+			},
+			ldapMocks: []mockInputs{
+				{
+					funcName: "Search",
+					args: []interface{}{
+						ldap.NewSearchRequest(
+							"ou=people,dc=test",
+							ldap.ScopeWholeSubtree,
+							ldap.NeverDerefAliases, 1, 0, false,
+							"(&(objectClass=inetOrgPerson)(|(uid=testUser)(entryUUID=testUser)))",
+							[]string{"displayname", "entryUUID", "mail", "uid", "sn", "givenname", "userEnabledAttribute"},
+							nil,
+						),
+					},
+					returns: []interface{}{
+						&ldap.SearchResult{
+							Entries: []*ldap.Entry{
+								{
+									DN: "uid=name",
+									Attributes: []*ldap.EntryAttribute{
+										{
+											Name:   "displayname",
+											Values: []string{"testuser@example.org"},
+										},
+										{
+											Name:   "entryUUID",
+											Values: []string{"testUser"},
+										},
+										{
+											Name:   "mail",
+											Values: []string{"testuser@example.org"},
+										},
+										{
+											Name:   lconfig.UserEnabledAttribute,
+											Values: []string{"TRUE"},
+										},
+									},
+								},
+							},
+						},
+						nil,
+					},
+				},
+				{
+					funcName: "Search",
+					args: []interface{}{
+						&ldap.SearchRequest{
+							BaseDN:       disableUsersGroup,
+							Scope:        0,
+							DerefAliases: 0,
+							SizeLimit:    1,
+							TimeLimit:    0,
+							TypesOnly:    false,
+							Filter:       groupSearchFilter,
+							Attributes:   []string{"member"},
+							Controls:     []ldap.Control(nil),
+						},
+					},
+					returns: []interface{}{
+						&ldap.SearchResult{
+							Entries: []*ldap.Entry{
+								{
+									DN: "uid=name",
+									Attributes: []*ldap.EntryAttribute{
+										{
+											Name:   "member",
+											Values: []string{""},
+										},
+									},
+								},
+							},
+						},
+						nil,
+					},
+				},
+				{
+					funcName: "Modify",
+					args: []interface{}{
+						&ldap.ModifyRequest{
+							DN: "uid=name",
+							Changes: []ldap.Change{
+								{
+									Operation: ldap.DeleteAttribute,
+									Modification: ldap.PartialAttribute{
+										Type: "member",
+										Vals: []string{"uid=name"},
+									},
+								},
+							},
+							Controls: []ldap.Control(nil),
+						},
+					},
+					returns: []interface{}{nil},
+				},
+				{
+					funcName: "Modify",
+					args: []interface{}{
+						&ldap.ModifyRequest{
+							DN:       "uid=name",
+							Changes:  []ldap.Change(nil),
+							Controls: []ldap.Control(nil),
+						},
+					},
+					returns: []interface{}{nil},
+				},
+				{
+					funcName: "Search",
+					args: []interface{}{
+						ldap.NewSearchRequest(
+							"uid=name",
+							ldap.ScopeBaseObject,
+							ldap.NeverDerefAliases, 1, 0, false,
+							"(objectClass=inetOrgPerson)",
+							[]string{"displayname", "entryUUID", "mail", "uid", "sn", "givenname", "userEnabledAttribute"},
+							[]ldap.Control(nil),
+						),
+					},
+					returns: []interface{}{
+						&ldap.SearchResult{
+							Entries: []*ldap.Entry{
+								{
+									DN: "uid=name",
+									Attributes: []*ldap.EntryAttribute{
+										{
+											Name:   "uid",
+											Values: []string{"testUser"},
+										},
+										{
+											Name:   "displayname",
+											Values: []string{"testUser"},
+										},
+										{
+											Name:   "entryUUID",
+											Values: []string{"testUser"},
+										},
+										{
+											Name:   "mail",
+											Values: []string{"testuser@example.org"},
+										},
+									},
+								},
+							},
+						},
+						nil,
+					},
+				},
+			},
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
@@ -928,13 +1272,17 @@ func TestUpdateUser(t *testing.T) {
 			for _, mock := range tt.ldapMocks {
 				lm.On(mock.funcName, mock.args...).Return(mock.returns...)
 			}
-			i, _ := getMockedBackend(lm, lconfig, &logger)
+
+			ldapConfig := lconfig
+			ldapConfig.DisableUserMechanism = tt.args.disableUserMechanism
+			i, _ := getMockedBackend(lm, ldapConfig, &logger)
 
 			user := libregraph.User{
 				Id:                       &tt.args.userProps.id,
 				Mail:                     &tt.args.userProps.mail,
 				DisplayName:              &tt.args.userProps.displayName,
 				OnPremisesSamAccountName: &tt.args.userProps.onPremisesSamAccountName,
+				AccountEnabled:           tt.args.userProps.accountEnabled,
 			}
 
 			emptyString := ""
@@ -947,7 +1295,10 @@ func TestUpdateUser(t *testing.T) {
 					OnPremisesSamAccountName: &tt.want.onPremisesSamAccountName,
 					Surname:                  &emptyString,
 					GivenName:                &emptyString,
-					AccountEnabled:           &tt.want.accountEnabled,
+				}
+
+				if tt.want.accountEnabled != nil {
+					want.AccountEnabled = *&tt.want.accountEnabled
 				}
 			}
 
@@ -957,3 +1308,214 @@ func TestUpdateUser(t *testing.T) {
 		})
 	}
 }
+
+func TestUsersEnabledState(t *testing.T) {
+	aliceEnabled := ldap.Entry{
+		DN: "alice",
+		Attributes: []*ldap.EntryAttribute{
+			{
+				Name:   lconfig.UserEnabledAttribute,
+				Values: []string{"TRUE"},
+			},
+		},
+	}
+
+	bobDisabled := ldap.Entry{
+		DN: "bob",
+		Attributes: []*ldap.EntryAttribute{
+			{
+				Name:   lconfig.UserEnabledAttribute,
+				Values: []string{"FALSE"},
+			},
+		},
+	}
+
+	carolImplicitlyEnabled := ldap.Entry{
+		DN: "carol",
+		Attributes: []*ldap.EntryAttribute{
+			{
+				Name: lconfig.UserEnabledAttribute,
+			},
+		},
+	}
+
+	type args struct {
+		usersToCheck         []*ldap.Entry
+		expectedUsers        map[string]bool
+		disableUserMechanism string
+	}
+	type mockInputs struct {
+		funcName string
+		args     []interface{}
+		returns  []interface{}
+	}
+	tests := []struct {
+		name      string
+		args      args
+		want      map[string]bool
+		assertion assert.ErrorAssertionFunc
+		ldapMocks []mockInputs
+	}{
+		{
+			name: "Test no users",
+			args: args{
+				usersToCheck:         []*ldap.Entry{},
+				expectedUsers:        map[string]bool{},
+				disableUserMechanism: "attribute",
+			},
+			want: map[string]bool{},
+			assertion: func(t assert.TestingT, err error, args ...interface{}) bool {
+				return assert.Nil(t, err, args...)
+			},
+			ldapMocks: []mockInputs{},
+		},
+		{
+			name: "Test attribute enabled users",
+			args: args{
+				usersToCheck:         []*ldap.Entry{&aliceEnabled, &bobDisabled, &carolImplicitlyEnabled},
+				expectedUsers:        map[string]bool{"alice": true, "bob": false, "carol": true},
+				disableUserMechanism: "attribute",
+			},
+			want: map[string]bool{"alice": true, "bob": false, "carol": true},
+			assertion: func(t assert.TestingT, err error, args ...interface{}) bool {
+				return assert.Nil(t, err, args...)
+			},
+			ldapMocks: []mockInputs{},
+		},
+		{
+			name: "Test attribute enabled users not in disabled group",
+			args: args{
+				usersToCheck:         []*ldap.Entry{&aliceEnabled, &bobDisabled, &carolImplicitlyEnabled},
+				expectedUsers:        map[string]bool{"alice": true, "bob": false, "carol": true},
+				disableUserMechanism: "group",
+			},
+			want: map[string]bool{"alice": true, "bob": true, "carol": true},
+			assertion: func(t assert.TestingT, err error, args ...interface{}) bool {
+				return assert.Nil(t, err, args...)
+			},
+			ldapMocks: []mockInputs{
+				{
+					funcName: "Search",
+					args: []interface{}{
+						ldap.NewSearchRequest(
+							disableUsersGroup,
+							ldap.ScopeBaseObject,
+							ldap.NeverDerefAliases, 1, 0, false,
+							groupSearchFilter,
+							[]string{"member"},
+							[]ldap.Control(nil),
+						),
+					},
+					returns: []interface{}{
+						&ldap.SearchResult{
+							Entries: []*ldap.Entry{
+								{
+									DN: "cn=DisabledGroup",
+									Attributes: []*ldap.EntryAttribute{
+										{
+											Name:   "member",
+											Values: []string{""},
+										},
+									},
+								},
+							},
+						},
+						nil,
+					},
+				},
+			},
+		},
+		{
+			name: "Test attribute enabled users in disabled group",
+			args: args{
+				usersToCheck:         []*ldap.Entry{&aliceEnabled, &bobDisabled, &carolImplicitlyEnabled},
+				expectedUsers:        map[string]bool{"alice": true, "bob": false, "carol": true},
+				disableUserMechanism: "group",
+			},
+			want: map[string]bool{"alice": false, "bob": true, "carol": false},
+			assertion: func(t assert.TestingT, err error, args ...interface{}) bool {
+				return assert.Nil(t, err, args...)
+			},
+			ldapMocks: []mockInputs{
+				{
+					funcName: "Search",
+					args: []interface{}{
+						ldap.NewSearchRequest(
+							disableUsersGroup,
+							ldap.ScopeBaseObject,
+							ldap.NeverDerefAliases, 1, 0, false,
+							groupSearchFilter,
+							[]string{"member"},
+							[]ldap.Control(nil),
+						),
+					},
+					returns: []interface{}{
+						&ldap.SearchResult{
+							Entries: []*ldap.Entry{
+								{
+									DN: "cn=DisabledGroup",
+									Attributes: []*ldap.EntryAttribute{
+										{
+											Name:   "member",
+											Values: []string{"alice", "carol"},
+										},
+									},
+								},
+							},
+						},
+						nil,
+					},
+				},
+			},
+		},
+		{
+			name: "Test local group disable when ldap is throwing error",
+			args: args{
+				usersToCheck:         []*ldap.Entry{&aliceEnabled, &bobDisabled, &carolImplicitlyEnabled},
+				expectedUsers:        map[string]bool{"alice": true, "bob": false, "carol": true},
+				disableUserMechanism: "group",
+			},
+			want: nil,
+			assertion: func(t assert.TestingT, err error, args ...interface{}) bool {
+				return assert.NotNil(t, err, args...)
+			},
+			ldapMocks: []mockInputs{
+				{
+					funcName: "Search",
+					args: []interface{}{
+						ldap.NewSearchRequest(
+							disableUsersGroup,
+							ldap.ScopeBaseObject,
+							ldap.NeverDerefAliases, 1, 0, false,
+							groupSearchFilter,
+							[]string{"member"},
+							[]ldap.Control(nil),
+						),
+					},
+					returns: []interface{}{
+						nil,
+						&ldap.Error{
+							Err: fmt.Errorf("Very Problematic Problems"),
+						},
+					},
+				},
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			lm := &mocks.Client{}
+			for _, mock := range tt.ldapMocks {
+				lm.On(mock.funcName, mock.args...).Return(mock.returns...)
+			}
+
+			ldapConfig := lconfig
+			ldapConfig.DisableUserMechanism = tt.args.disableUserMechanism
+			i, _ := getMockedBackend(lm, ldapConfig, &logger)
+
+			got, err := i.usersEnabledState(tt.args.usersToCheck)
+			tt.assertion(t, err)
+			assert.Equal(t, tt.want, got)
+		})
+	}
+}