Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

client credentials grantを利用できるように #2433

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions migration/current.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ func Migrations() []*gormigrate.Migration {
v33(), // 未読テーブルにチャンネルIDカラムを追加 / インデックス類の更新 / 不要なレコードの削除
v34(), // 未読テーブルのcreated_atカラムをメッセージテーブルを元に更新 / カラム名を変更
v35(), // OIDC実装のため、openid, profileロール、get_oidc_userinfo権限を追加
v36(), // OAuth Client Credentials Grantの対応のため、clientロールを追加
}
}

Expand Down
68 changes: 68 additions & 0 deletions migration/v36.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package migration

import (
"github.com/go-gormigrate/gormigrate/v2"
"gorm.io/gorm"
)

// v36 OAuth Client Credentials Grantの対応のため、clientロールを追加
func v36() *gormigrate.Migration {
return &gormigrate.Migration{
ID: "36",
Migrate: func(db *gorm.DB) error {
roles := []v36UserRole{
{
Name: "client",
Oauth2Scope: false,
System: true,
Permissions: []v36RolePermission{
{
Role: "client",
Permission: "get_user",
},
{
Role: "client",
Permission: "get_user_tag",
},
{
Role: "client",
Permission: "get_user_group",
},
{
Role: "client",
Permission: "get_stamp",
},
},
},
}
for _, role := range roles {
err := db.Create(&role).Error
if err != nil {
return err
}
}
return nil
},
}
}

type v36UserRole struct {
Name string `gorm:"type:varchar(30);not null;primaryKey"`
Oauth2Scope bool `gorm:"type:boolean;not null;default:false"`
System bool `gorm:"type:boolean;not null;default:false"`

Permissions []v36RolePermission `gorm:"constraint:user_role_permissions_role_user_roles_name_foreign,OnUpdate:CASCADE,OnDelete:CASCADE;foreignKey:Role;references:Name"`
}

func (*v36UserRole) TableName() string {
return "user_roles"
}

type v36RolePermission struct {
Role string `gorm:"type:varchar(30);not null;primaryKey"`
Permission string `gorm:"type:varchar(30);not null;primaryKey"`
}

func (*v36RolePermission) TableName() string {
return "user_role_permissions"
}
11 changes: 11 additions & 0 deletions router/middlewares/access_control.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/traPtitech/traQ/service/message"
"github.com/traPtitech/traQ/service/rbac"
"github.com/traPtitech/traQ/service/rbac/permission"
"github.com/traPtitech/traQ/service/rbac/role"
)

// AccessControlMiddlewareGenerator アクセスコントロールミドルウェアのジェネレーターを返します
Expand All @@ -33,6 +34,16 @@ func AccessControlMiddlewareGenerator(r rbac.RBAC) func(p ...permission.Permissi

// ユーザー権限検証
user := c.Get(consts.KeyUser).(model.UserInfo)
if user == nil {
for _, v := range p {
if !r.IsGranted(role.Client, v) {
// NG
return echo.NewHTTPError(http.StatusForbidden, fmt.Sprintf("you are not permitted to request to '%s'", c.Request().URL.Path))
}
}

return next(c) // OK
}
for _, v := range p {
if !r.IsGranted(user.GetRole(), v) {
// NG
Expand Down
6 changes: 6 additions & 0 deletions router/middlewares/user_authenticate.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@ func UserAuthenticate(repo repository.Repository, sessStore session.Store) echo.
}

c.Set(consts.KeyOAuth2AccessScopes, token.Scopes)
if token.UserID == uuid.Nil {
// client credentials grant の場合ユーザーが存在しない
c.Set(consts.KeyUser, nil)
c.Set(consts.KeyUserID, uuid.Nil)
return next(c)
}
uid = token.UserID
} else {
// Authorizationヘッダーがないためセッションを確認する
Expand Down
18 changes: 18 additions & 0 deletions service/rbac/role/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package role

import (
"github.com/traPtitech/traQ/service/rbac/permission"
)

// Client Clientロール (for OAuth2 client credentials grant)
const Client = "client"

// 自分自身以外の参照系は許可するようにしたいが、https://github.com/traPtitech/traQ/pull/2433#discussion_r1649383346
// の事情から許可できる権限が限られる
// https://github.com/traPtitech/traQ/issues/2463 で権限を増やせるよう対応予定
var clientPerms = []permission.Permission{
permission.GetUser,
permission.GetUserTag,
permission.GetUserGroup,
permission.GetStamp,
}
Comment on lines +13 to +18
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

knoQで使いたいのでGetUsersやGetUserGroupsがあると助かります:pray:
Draftで仮置きしてるだけかもですが

Copy link
Member Author

@kyosu-1 kyosu-1 Jun 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GetUsersとGetUserGroupsのAPIはそれぞれpermission.GetUserとpermission.GetUserGroupを持っていれば叩けますね。(APIと一対一対応ではなく、一つのpermissionが複数のAPIを対象として許可しています。)
ref:

apiUsers.GET("", h.GetUsers, requires(permission.GetUser))

apiGroups.GET("", h.GetUserGroups, requires(permission.GetUserGroup))

一応permissionのListは

var List = []Permission{
ここの一覧として載っています。

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

少なくない?そんなことない?
自身のユーザーに関連しないものは許可してよさそう
https://github.com/traPtitech/traQ/blob/master/service/rbac/role/read.go

Copy link
Member Author

@kyosu-1 kyosu-1 Jun 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

見落としあるかもしれませんが、確認した限りはこれら以外のpermissionで許可されるエンドポイントの中には getRequestUserID が 含まれてそうな感じでした。
e.g. permission.GetMessage で許可されるエンドポイントの中にGetDirectMessages が含まれる

func (h *Handlers) GetDirectMessages(c echo.Context) error {

apiUsersUID.GET("/messages", h.GetDirectMessages, requires(permission.GetMessage))

permissionで許可するエンドポイントの対応関係を上手く考え直せば与えられる許可は増やせそうですが、影響範囲大きくなるのでひとまずこれぐらいでも良いかなと思ってます。

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

うーん、なるほど あまりきめ細やかに制御されてなくて難しいんですね
そうしたらその権限の制御のやり方を考え直す(より細やかにする)か、client credentials grantにそもそもユーザーをどうにかして紐づけるか、とかを考えたいところですね

少なくとも今やらない(できなかった)のなら、その理由をコメントとかで書いておくべきだと思います

Copy link
Member Author

@kyosu-1 kyosu-1 Jun 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issue立てつつコメント残す形にしました

5 changes: 5 additions & 0 deletions service/rbac/role/role.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ func GetSystemRoles() Roles {
oauth2Scope: true,
permissions: permission.PermissionsFromArray(profilePerms),
},
Client: &systemRole{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

新しくrbacのロールとpermission一覧を更新するときはmigrationが必須です

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

memo:
https://github.com/traPtitech/traQ/blob/4ac16404c3a12e44ef1503b0d8cd9b2899deeede/migration/v35.go

v35(), // OIDC実装のため、openid, profileロール、get_oidc_userinfo権限を追加

ここら辺と同じように

name: Client,
oauth2Scope: false,
permissions: permission.PermissionsFromArray(clientPerms),
},
}
}

Expand Down
Loading