【エラー】SpringでMongoDBに接続したけどCRUD操作できない

# はじめに
Spring BootでMongoDBのCRUD操作を実行しようとしたところ、エラーが発生しました。
エラー発生から解決に至るまでを紹介します。

### 使っているもの

Springが提供する[公式ガイド](https://spring.pleiades.io/guides/gs/accessing-data-mongodb/)を試していました。

- Spring Boot（3.2.0）
- Spring Boot Starter Data MongoDB（3.2.1）

# 【問題】delete操作を呼び出すと”not authorized”と言われてしまう

▼ エラー発生箇所のコードです。`repository.deleteAll()`の部分でエラーが発生します。

```AccessingDataMongodbApplication.java
@SpringBootApplication
public class AccessingDataMongodbApplication implements CommandLineRunner {

	public static void main(String[] args) {
		SpringApplication.run(AccessingDataMongodbApplication.class, args);
	}
	
	@Autowired
	CustomerRepository repository;

	@Override
	public void run(String... args) throws Exception {
		repository.deleteAll();
		// 省略
	}
}
```

▼ コンソールに「**MongoCommandException**」が出力されます。
```
Caused by: com.mongodb.MongoCommandException: Command failed with error 13 (Unauthorized): 'not authorized on test to execute command { delete: "customer", ordered: true, $db: "test", lsid: { id: UUID("（省略）") } }' on server localhost:27017. The full response is {"ok": 0.0, "errmsg": "not authorized on test to execute command { delete: \"customer\", ordered: true, $db: \"test\", lsid: { id: UUID(\"（省略）\") } }", "code": 13, "codeName": "Unauthorized"}
```

▼ エラーメッセージのJSON形式の部分は以下の通りです。

```json
{
  "ok": 0.0, 
  "errmsg": "not authorized on test to execute command { 
    delete: 'customer',
    ordered: true, 
    $db: 'test', 
    lsid: { id: UUID('（省略）') } 
  }", 
  "code": 13, 
  "codeName": "Unauthorized"
}
```
明らかに認証による問題が発生していることがわかります。


▼ アプリケーション⇔MongoDBの接続設定は以下の通りです。

```application.properties
# MONGODB (MongoProperties)
spring.data.mongodb.uri=mongodb://user:pass@localhost:27017/test
```
ユーザ「user」でアクセス中です。

# **【原因】アクセス中のユーザにdeleteを実行する権限が無かった**

同じ問題にぶち当たっている方がいました。原因は**ユーザの権限**にあるようです。

https://stackoverflow.com/questions/57744557/getting-mongocommandexception-command-failed-with-error-18-authenticationfaile

▼ mongoshから、ユーザ「user」の情報を確認してみます。

```bash
test> db.getUser("user")
{
  _id: 'test.user',
  userId: new UUID("（省略）"),
  user: 'user',
  db: 'test',
  roles: [ { role: 'userAdmin', db: 'test' } ],
  mechanisms: [ 'SCRAM-SHA-1', 'SCRAM-SHA-256' ]
}
test>
```

[**userAdmin権限**](https://www.mongodb.com/docs/manual/reference/built-in-roles/#mongodb-authrole-userAdmin)のユーザでアクセスしていました。
userAdmin権限は、ロールとユーザーの作成・変更はできますが、**データ操作はできません。**

つまり、**delete操作を行える権限をもつユーザでアクセスする**よう、修正しなければならないようです。

# 【解決方法】ユーザの権限を変更する

ユーザの権限を[**readWrite権限**](https://www.mongodb.com/docs/manual/reference/built-in-roles/#mongodb-authrole-readWrite)に変更します。
readWrite権限は、**全コレクションのデータ操作が可能**です。すなわち、delete操作を実行できます。

### MongoDBのコマンドを実行

mongoshから以下のコマンドを実行し、権限を変更します。

▼ ユーザの「userAdmin」権限を削除するコマンド

```json
test> db.revokeRolesFromUser(
   "user",
   [
     { role: "userAdmin", db: "test" }
   ]
)
{ ok: 1 }
test>
```

▼ ユーザに「readWrite」権限を付与するコマンド

```json
test> db.grantRolesToUser(
   "user",
   [
     { role: "readWrite", db: "test" }
   ]
)
{ ok: 1 }
test>
```

▼ ユーザの情報を取得して、変更できているか確認する

```bash
test> db.getUser("user")
{
  _id: 'test.user',
  userId: new UUID("（省略）"),
  user: 'user',
  db: 'test',
  roles: [ { role: 'readWrite', db: 'test' } ],
  mechanisms: [ 'SCRAM-SHA-1', 'SCRAM-SHA-256' ]
}
test>
```
roleの値が「readWrite」に変更されたことがわかります。

以上の変更を行ったところ、無事エラーは解消されました。

# おわりに

MongoDBには他にもいろんな種類の権限があるようです。
今回は学習目的ですが、実務で扱う際は、ユースケースやセキュリティ要件に応じて適切な権限を検討しなければなりませんね。大変勉強になりました。

お読みいただきありがとうございました。この記事が誰かのお役に立つことを祈っております。

# 参考文献
- [入門 | MongoDB でデータアクセス (pleiades.io)](https://spring.pleiades.io/guides/gs/accessing-data-mongodb/)
- [MongoDBのユーザ管理 #MongoDB - Qiita](https://qiita.com/t2hk/items/45a04875dc6d8be9b004)
- [java - Getting MongoCommandException: Command failed with error 18 (AuthenticationFailed): 'Authentication failed.' on server localhost:27017 - Stack Overflow](https://stackoverflow.com/questions/57744557/getting-mongocommandexception-command-failed-with-error-18-authenticationfaile)
- [Built-In Roles — MongoDB Manual](https://www.mongodb.com/docs/manual/reference/built-in-roles/#built-in-roles)