動画で学ぶJHipster (9) （ロックダウン）

# 参考動画
https://www.youtube.com/watch?v=6lf64CctDAQ

11:19～

## テキスト
https://github.com/mraible/jhipster7-demo/blob/main/README.adoc#lock-it-down

# ロックダウンの意味
テキストに**Lock It Down!**と書いてあるのでロックダウンというタイトルにしましたが、意味がよくわからない汗

https://ejje.weblio.jp/content/lock+down

>ロックダウン（英語: lockdown）は、危険や差し迫った脅威・リスクなどを理由に、建物やエリアへ入ったり、そこから出たり、その中を移動したり（そのいずれか一つまたは複数）が自由にできない緊急の状況をいう。

よくコロナのニュースで海外の国の都市が「**ロックダウン**」してるとか聴くけどあれのことか。

https://www.bloomberg.co.jp/news/articles/2021-11-12/R2GXW1T0G1KY01

もうちょい調べると、

https://www.itmedia.co.jp/news/articles/2011/24/news049.html

> lockdownの文字通りの意味は鍵をかけて閉じ込めること。Collinsによると、もともとは刑務所で使う用語だったとか。

元々刑務所用語だったのか。

> 一方、コンピュータ用語でlockdownといえば、主にセキュリティ対策を目的としたシステムの封鎖、つまり機能制限のこと。こちらはlock downという動詞として使うことも多いようだ。

コンピュータ用語でもよく使われているのか。知らなかった。

# 現状
Adminでログインして、ある投稿の詳細の画面を表示します。
![1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1284134/f888fd7f-98bc-77e9-9a80-6d1d55cac83c.png)

ここで、直接URLを編集します。

```
http://localhost:8080/post/5/view
```

**5**を**6**に編集し、

```
http://localhost:8080/post/6/view
```
として、Enterを押すと、

![2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1284134/89d8c4b5-9718-6de7-a87f-fcf6b0936af1.png)

このように、画面が表示されますが、この投稿は、ログイン中のAdminアカウントのものではなく、User アカウントの投稿なので、本来は表示されてはいけない画面のはずです。（要は、**Adminでログインしてるのに、URL直打ちすると、Userのポストがみえてしまう！**ということ）

今回は、ここに、アクセス制限をかけて、自分のアカウント以外の画面にはアクセスできなくする、見えなくするという作業をします。

# コードの修正

以下はテキストに載ってる疑似コードですが、こんな感じのロジックで、ログインしているユーザー自身以外のページは見えないようにします。

```java:疑似コード
Optional<Blog> blog = blogRepository.findById(id);
if (blog.isPresent() && <user does not match current user>) {
    return new ResponseEntity<>("error.http.403", HttpStatus.FORBIDDEN);
}
return ResponseUtil.wrapOrNotFound(blog);
```
`BlogResource.java`(src/main/java/org/jhipster/blog/web/rest/BlogResource.java)については、修正後のコードは以下のようになります。

```java:修正後のBlogResource.java
@PostMapping("/blogs")
public ResponseEntity<?> createBlog(@Valid @RequestBody Blog blog) throws URISyntaxException {
    log.debug("REST request to save Blog : {}", blog);
    if (blog.getId() != null) {
        throw new BadRequestAlertException("A new blog cannot already have an ID", ENTITY_NAME, "idexists");
    }
    if (!blog.getUser().getLogin().equals(SecurityUtils.getCurrentUserLogin().orElse(""))) {
        return new ResponseEntity<>("error.http.403", HttpStatus.FORBIDDEN);
    }
    Blog result = blogRepository.save(blog);
    return ResponseEntity
        .created(new URI("/api/blogs/" + result.getId()))
        .headers(HeaderUtil.createEntityCreationAlert(applicationName, true, ENTITY_NAME, result.getId().toString()))
        .body(result);
}

@PutMapping("/blogs/{id}")
public ResponseEntity<?> updateBlog(@PathVariable(value = "id", required = false) final Long id, @Valid @RequestBody Blog blog)
    throws URISyntaxException {
    log.debug("REST request to update Blog : {}, {}", id, blog);
    if (blog.getId() == null) {
        throw new BadRequestAlertException("Invalid id", ENTITY_NAME, "idnull");
    }
    if (!Objects.equals(id, blog.getId())) {
        throw new BadRequestAlertException("Invalid ID", ENTITY_NAME, "idinvalid");
    }
    if (!blogRepository.existsById(id)) {
        throw new BadRequestAlertException("Entity not found", ENTITY_NAME, "idnotfound");
    }
    if (blog.getUser() != null && !blog.getUser().getLogin().equals(SecurityUtils.getCurrentUserLogin().orElse(""))) {
        return new ResponseEntity<>("error.http.403", HttpStatus.FORBIDDEN);
    }
    Blog result = blogRepository.save(blog);
    return ResponseEntity
        .ok()
        .headers(HeaderUtil.createEntityUpdateAlert(applicationName, true, ENTITY_NAME, blog.getId().toString()))
        .body(result);
}

@PatchMapping(value = "/blogs/{id}", consumes = "application/merge-patch+json")
public ResponseEntity<?> partialUpdateBlog(
    @PathVariable(value = "id", required = false) final Long id,
    @NotNull @RequestBody Blog blog
) throws URISyntaxException {
    log.debug("REST request to partial update Blog partially : {}, {}", id, blog);
    if (blog.getId() == null) {
        throw new BadRequestAlertException("Invalid id", ENTITY_NAME, "idnull");
    }
    if (!Objects.equals(id, blog.getId())) {
        throw new BadRequestAlertException("Invalid ID", ENTITY_NAME, "idinvalid");
    }
    if (!blogRepository.existsById(id)) {
        throw new BadRequestAlertException("Entity not found", ENTITY_NAME, "idnotfound");
    }
    if (blog.getUser() != null && !blog.getUser().getLogin().equals(SecurityUtils.getCurrentUserLogin().orElse(""))) {
        return new ResponseEntity<>("error.http.403", HttpStatus.FORBIDDEN);
    }
    Optional<Blog> result = blogRepository
        .findById(blog.getId())
        .map(
            existingBlog -> {
                if (blog.getName() != null) {
                    existingBlog.setName(blog.getName());
                }
                if (blog.getHandle() != null) {
                    existingBlog.setHandle(blog.getHandle());
                }

                return existingBlog;
            }
        )
        .map(blogRepository::save);

    return ResponseUtil.wrapOrNotFound(
        result,
        HeaderUtil.createEntityUpdateAlert(applicationName, true, ENTITY_NAME, blog.getId().toString())
    );
}

@GetMapping("/blogs/{id}")
public ResponseEntity<?> getBlog(@PathVariable Long id) {
    log.debug("REST request to get Blog : {}", id);
    Optional<Blog> blog = blogRepository.findById(id);
    if (
        blog.isPresent() &&
        blog.get().getUser() != null &&
        !blog.get().getUser().getLogin().equals(SecurityUtils.getCurrentUserLogin().orElse(""))
    ) {
        return new ResponseEntity<>("error.http.403", HttpStatus.FORBIDDEN);
    }
    return ResponseUtil.wrapOrNotFound(blog);
}

@DeleteMapping("/blogs/{id}")
public ResponseEntity<?> deleteBlog(@PathVariable Long id) {
    log.debug("REST request to delete Blog : {}", id);
    Optional<Blog> blog = blogRepository.findById(id);
    if (
        blog.isPresent() &&
        blog.get().getUser() != null &&
        !blog.get().getUser().getLogin().equals(SecurityUtils.getCurrentUserLogin().orElse(""))
    ) {
        return new ResponseEntity<>("error.http.403", HttpStatus.FORBIDDEN);
    }
    blogRepository.deleteById(id);
    return ResponseEntity
        .noContent()
        .headers(HeaderUtil.createEntityDeletionAlert(applicationName, true, ENTITY_NAME, id.toString()))
        .build();
}
```

`createBlog`、`updateBlog`、`partialUpdateBlog`、`getBlog`、`deleteBlog`の各メソッドが修正されています。

各メソッドに、疑似コードのロジックが入っているのに加えて、メソッドの戻り値の型が、`ResponseEntity<Blog>`や`ResponseEntity<Void>`から、`ResponseEntity<?>`に変更になっています。

`PostResource.java` にも同様の変更を行います。
(`createPost`、`updatePost`、`partialUpdatePost`、`getPost`、`deletePost`を修正する）

またそれぞれのテスト用のクラスの変更も行わなくちゃいけないらしい(`src/test/java/org/jhipster/blog/web/rest/BlogResourceIT.java`と`PostResourceIT.java`。`BlogResourceIT.java`は`createEntity`を、`PostResourceIT.java`は`getAllPosts`を修正する。ただこれだけだとcypressテストに失敗します。元のテストはadminでログインしてuser所有のblogを作成しようとするテストになっているので、このロックダウンに対応しておらずエラーになる。)

このページで、今回の全ての変更箇所を確認できるようです。

https://github.com/mraible/jhipster7-demo/commit/5dcfcb7ede4ec796829e55135683ebfb9d8389fa

# 修正後は
修正前と同じように、adminでログインし、URL直打ちで、userのpostを開こうとすると開けず、下記のようにログイン画面にリダイレクトされました。
![3.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1284134/8c9edbe1-1dfb-9095-a345-6a0e04ea4878.png)
ちゃんと見えなくすることができたようです。

# 今回のコード
https://github.com/sayuu-tf/learning_jhipster_3/commit/b10bc460d75af8f08855a314eca5318cc2f8edcf