-
Notifications
You must be signed in to change notification settings - Fork 84
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
> 🚀 Added implementation related to [granularToken](https://docs.npmjs.com/about-access-tokens#about-granular-access-tokens), mainly used for web authorization scenarios. * 📝 Added `1.14.0.sql` to add fields and `token_packages` for granularToken. * 🛣️ Added gat related routes, including `create`, `query`, and `delete` api. * 🌟 Added `tokenService` to check granularToken access. * 🔄 Modified Token to perform options and data attribute conversions internally in the model. ----------- > 🚀 新增 [granularToken](https://docs.npmjs.com/about-access-tokens#about-granular-access-tokens) 相关实现,主要用于 web 端授权场景 * 📝 新增 `1.14.0.sql` 添加 granularToken 相关字段及 `token_packages` 中间表 * 🛣️ 新增 gat 相关路由,包括`创建`、`查询`、`删除`接口 * 🌟 新增 `tokenService` ,处理 granularToken 鉴权 * 🔄 修改 Token ,在 model 内部进行 options 和 data 属性转换
- Loading branch information
Showing
14 changed files
with
722 additions
and
35 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import dayjs from 'dayjs'; | ||
import { | ||
AccessLevel, | ||
SingletonProto, | ||
Inject, | ||
} from '@eggjs/tegg'; | ||
import { isEmpty } from 'lodash'; | ||
import { AbstractService } from '../../common/AbstractService'; | ||
import { Token, isGranularToken } from '../entity/Token'; | ||
import { TokenPackage as TokenPackageModel } from '../../../app/repository/model/TokenPackage'; | ||
import { Package as PackageModel } from '../../../app/repository/model/Package'; | ||
import { ModelConvertor } from '../../../app/repository/util/ModelConvertor'; | ||
import { Package as PackageEntity } from '../entity/Package'; | ||
import { ForbiddenError, UnauthorizedError } from 'egg-errors'; | ||
import { getScopeAndName } from '../../../app/common/PackageUtil'; | ||
|
||
@SingletonProto({ | ||
accessLevel: AccessLevel.PUBLIC, | ||
}) | ||
export class TokenService extends AbstractService { | ||
@Inject() | ||
private readonly TokenPackage: typeof TokenPackageModel; | ||
@Inject() | ||
private readonly Package: typeof PackageModel; | ||
|
||
public async listTokenPackages(token: Token) { | ||
if (isGranularToken(token)) { | ||
const models = await this.TokenPackage.find({ tokenId: token.tokenId }); | ||
const packages = await this.Package.find({ packageId: models.map(m => m.packageId) }); | ||
return packages.map(pkg => ModelConvertor.convertModelToEntity(pkg, PackageEntity)); | ||
} | ||
return null; | ||
} | ||
|
||
public async checkGranularTokenAccess(token: Token, fullname: string) { | ||
// skip classic token | ||
if (!isGranularToken(token)) { | ||
return true; | ||
} | ||
|
||
// check for expires | ||
if (dayjs(token.expiredAt).isBefore(new Date())) { | ||
throw new UnauthorizedError('Token expired'); | ||
} | ||
|
||
// check for scope whitelist | ||
const [ scope, name ] = getScopeAndName(fullname); | ||
// check for packages whitelist | ||
const allowedPackages = await this.listTokenPackages(token); | ||
|
||
// check for scope & packages access | ||
if (isEmpty(allowedPackages) && isEmpty(token.allowedScopes)) { | ||
return true; | ||
} | ||
|
||
const existPkgConfig = allowedPackages?.find(pkg => pkg.scope === scope && pkg.name === name); | ||
if (existPkgConfig) { | ||
return true; | ||
} | ||
|
||
const existScopeConfig = token.allowedScopes?.find(s => s === scope); | ||
if (existScopeConfig) { | ||
return true; | ||
} | ||
|
||
throw new ForbiddenError(`can't access package "${fullname}"`); | ||
|
||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.