-
Notifications
You must be signed in to change notification settings - Fork 5.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
361 additions
and
0 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,28 @@ | ||
import { OrgMemberRole } from './constant'; | ||
|
||
type postCreateOrgData = { | ||
name: string; | ||
parentId: string; | ||
avatar?: string; | ||
}; | ||
|
||
type putUpdateOrgMembersData = { | ||
orgId: string; | ||
members: { | ||
tmbId: string; | ||
role: `${OrgMemberRole}`; | ||
}[]; | ||
}; | ||
|
||
type putChnageOrgOwnerData = { | ||
orgId: string; | ||
tmbId: string; | ||
toAdmin?: boolean; | ||
}; | ||
|
||
type putUpdateOrgData = { | ||
orgId: string; | ||
name?: string; | ||
avatar?: string; | ||
parentId?: string; | ||
}; |
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,10 @@ | ||
export const OrgCollectionName = 'team_orgs'; | ||
export const OrgMemberCollectionName = 'team_org_members'; | ||
|
||
export enum OrgMemberRole { | ||
owner = 'owner', | ||
admin = 'admin', | ||
member = 'member' | ||
} | ||
|
||
export const RootOrgName = 'ROOT'; |
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,19 @@ | ||
import { OrgMemberRole } from './constant'; | ||
import { ResourcePermissionType } from '../type'; | ||
|
||
type OrgSchemaType = { | ||
_id: string; | ||
teamId: string; | ||
path: string; | ||
name: string; | ||
avatar: string; | ||
updateTime: Date; | ||
members: OrgMemberSchemaType[] | undefined; | ||
permissions: ResourcePermissionType[] | undefined; | ||
}; | ||
|
||
type OrgMemberSchemaType = { | ||
orgId: string | undefined; | ||
tmbId: string; | ||
role: `${OrgMemberRole}`; | ||
}; |
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,173 @@ | ||
import { MongoOrgMemberModel } from './orgMemberSchema'; | ||
import { MongoOrgModel } from './orgSchema'; | ||
import { OrgMemberRole } from '@fastgpt/global/support/user/team/org/constant'; | ||
import { AuthModeType, AuthResponseType } from '../type'; | ||
import { parseHeaderCert } from '../controller'; | ||
import { TeamPermission } from '@fastgpt/global/support/permission/user/controller'; | ||
import { getTmbInfoByTmbId } from '../../user/team/controller'; | ||
import { OrgSchemaType } from '@fastgpt/global/support/user/team/org/type'; | ||
import { TeamErrEnum } from '@fastgpt/global/common/error/code/team'; | ||
import { ClientSession } from 'mongoose'; | ||
|
||
// if role1 > role2, return 1 | ||
// if role1 < role2, return -1 | ||
// else return 0 | ||
export const compareRole = (role1: OrgMemberRole, role2: OrgMemberRole) => { | ||
if (role1 === OrgMemberRole.owner) { | ||
if (role2 === OrgMemberRole.owner) { | ||
return 0; | ||
} | ||
return 1; | ||
} | ||
if (role2 === OrgMemberRole.owner) { | ||
return -1; | ||
} | ||
if (role1 === OrgMemberRole.admin) { | ||
if (role2 === OrgMemberRole.admin) { | ||
return 0; | ||
} | ||
return 1; | ||
} | ||
if (role2 === OrgMemberRole.admin) { | ||
return -1; | ||
} | ||
return 0; | ||
}; | ||
|
||
export const checkOrgRole = (role: OrgMemberRole, targetRole: OrgMemberRole) => { | ||
return compareRole(role, targetRole) >= 0; | ||
}; | ||
|
||
export const getOrgsByTeamId = async (teamId: string) => { | ||
const orgs = await MongoOrgModel.find({ | ||
teamId | ||
}) | ||
.populate('members') | ||
.lean(); | ||
|
||
return orgs; | ||
}; | ||
|
||
export const getOrgAndChildren = async ({ | ||
orgId, | ||
session | ||
}: { | ||
orgId: string; | ||
session?: ClientSession; | ||
}) => { | ||
const org = await MongoOrgModel.findById(orgId, undefined, { session }).lean(); | ||
if (!org) { | ||
return Promise.reject(TeamErrEnum.orgNotExist); | ||
} | ||
const children = await MongoOrgModel.find( | ||
{ | ||
path: { | ||
$regex: `^${org.path}/${org._id}` | ||
} | ||
}, | ||
undefined, | ||
{ session } | ||
).lean(); | ||
return { org, children }; | ||
}; | ||
|
||
export const getOrgMemberRole = async ({ | ||
orgId, | ||
tmbId | ||
}: { | ||
orgId: string; | ||
tmbId: string; | ||
}): Promise<OrgMemberRole | undefined> => { | ||
let role: OrgMemberRole | undefined; | ||
const orgMember = await MongoOrgMemberModel.findOne({ | ||
orgId, | ||
tmbId | ||
}) | ||
.populate('orgId') | ||
.lean(); | ||
if (orgMember) { | ||
role = OrgMemberRole[orgMember.role]; | ||
} else { | ||
return role; | ||
} | ||
if (role == OrgMemberRole.owner) { | ||
return role; | ||
} | ||
// Check the parent orgs | ||
const org = orgMember.orgId as unknown as OrgSchemaType; | ||
if (!org) { | ||
return Promise.reject(TeamErrEnum.orgNotExist); | ||
} | ||
const parentIds = org.path.split('/').filter((id) => id); | ||
if (parentIds.length === 0) { | ||
return role; | ||
} | ||
const parentOrgMembers = await MongoOrgMemberModel.find({ | ||
orgId: { | ||
$in: parentIds | ||
}, | ||
tmbId | ||
}).lean(); | ||
// Update the role to the highest role | ||
for (const parentOrgMember of parentOrgMembers) { | ||
const parentRole = OrgMemberRole[parentOrgMember.role]; | ||
if (parentRole === OrgMemberRole.owner) { | ||
role = parentRole; | ||
break; | ||
} else if (parentRole === OrgMemberRole.admin && role === OrgMemberRole.member) { | ||
role = parentRole; | ||
} | ||
} | ||
return role; | ||
}; | ||
|
||
export const authOrgMember = async ({ | ||
orgIds, | ||
role, | ||
req, | ||
authToken = false, | ||
authRoot = false, | ||
authApiKey = false | ||
}: { | ||
orgIds: string | string[]; | ||
role: `${OrgMemberRole}` | OrgMemberRole; | ||
} & AuthModeType): Promise<AuthResponseType> => { | ||
const result = await parseHeaderCert({ req, authToken, authApiKey, authRoot }); | ||
const { teamId, tmbId, isRoot } = result; | ||
if (isRoot) { | ||
return { | ||
teamId, | ||
tmbId, | ||
appId: result.appId, | ||
apikey: result.apikey, | ||
isRoot, | ||
authType: result.authType, | ||
permission: new TeamPermission({ isOwner: true }) | ||
}; | ||
} | ||
|
||
if (!Array.isArray(orgIds)) { | ||
orgIds = [orgIds]; | ||
} | ||
const promises = orgIds.map((orgId) => getOrgMemberRole({ orgId, tmbId })); | ||
|
||
const [tmb, ...orgRoles] = await Promise.all([getTmbInfoByTmbId({ tmbId }), ...promises]); | ||
if (tmb.permission.hasManagePer) { | ||
return { | ||
...result, | ||
permission: tmb.permission | ||
}; | ||
} | ||
|
||
let targetRole = OrgMemberRole[role]; | ||
for (const orgRole of orgRoles) { | ||
if (!orgRole || checkOrgRole(orgRole, targetRole)) { | ||
return Promise.reject(TeamErrEnum.unAuthTeam); | ||
} | ||
} | ||
|
||
return { | ||
...result, | ||
permission: tmb.permission | ||
}; | ||
}; |
48 changes: 48 additions & 0 deletions
48
packages/service/support/permission/org/orgMemberSchema.ts
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,48 @@ | ||
import { OrgCollectionName, OrgMemberRole } from '@fastgpt/global/support/user/team/org/constant'; | ||
import { connectionMongo, getMongoModel } from '../../../common/mongo'; | ||
import { TeamMemberCollectionName } from '@fastgpt/global/support/user/team/constant'; | ||
import { OrgMemberSchemaType } from '@fastgpt/global/support/user/team/org/type'; | ||
const { Schema } = connectionMongo; | ||
|
||
export const OrgMemberCollectionName = 'team_org_members'; | ||
|
||
export const OrgMemberSchema = new Schema({ | ||
orgId: { | ||
type: Schema.Types.ObjectId, | ||
ref: OrgCollectionName, | ||
required: true | ||
}, | ||
tmbId: { | ||
type: Schema.Types.ObjectId, | ||
ref: TeamMemberCollectionName, | ||
required: true | ||
}, | ||
role: { | ||
type: String, | ||
enum: Object.values(OrgMemberRole), | ||
required: true, | ||
default: OrgMemberRole.member | ||
} | ||
}); | ||
|
||
try { | ||
OrgMemberSchema.index( | ||
{ | ||
orgId: 1, | ||
tmbId: 1 | ||
}, | ||
{ | ||
unique: true | ||
} | ||
); | ||
OrgMemberSchema.index({ | ||
tmbId: 1 | ||
}); | ||
} catch (error) { | ||
console.log(error); | ||
} | ||
|
||
export const MongoOrgMemberModel = getMongoModel<OrgMemberSchemaType>( | ||
OrgMemberCollectionName, | ||
OrgMemberSchema | ||
); |
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,71 @@ | ||
import { TeamCollectionName } from '@fastgpt/global/support/user/team/constant'; | ||
import { connectionMongo, getMongoModel } from '../../../common/mongo'; | ||
import { OrgMemberCollectionName } from './orgMemberSchema'; | ||
import { ResourcePermissionCollectionName } from '../schema'; | ||
import { OrgSchemaType } from '@fastgpt/global/support/user/team/org/type'; | ||
import { OrgCollectionName } from '@fastgpt/global/support/user/team/org/constant'; | ||
const { Schema } = connectionMongo; | ||
|
||
function requiredStringPath(this: OrgSchemaType) { | ||
return typeof this.path === 'string' ? false : true; | ||
} | ||
|
||
export const OrgSchema = new Schema( | ||
{ | ||
teamId: { | ||
type: Schema.Types.ObjectId, | ||
ref: TeamCollectionName, | ||
required: true | ||
}, | ||
path: { | ||
type: String, | ||
required: requiredStringPath | ||
}, | ||
name: { | ||
type: String, | ||
required: true | ||
}, | ||
avatar: { | ||
type: String | ||
}, | ||
updateTime: { | ||
type: Date, | ||
default: () => new Date() | ||
} | ||
}, | ||
{ | ||
// Auto update updateTime | ||
timestamps: { | ||
updatedAt: 'updateTime' | ||
} | ||
} | ||
); | ||
|
||
OrgSchema.virtual('members', { | ||
ref: OrgMemberCollectionName, | ||
localField: '_id', | ||
foreignField: 'orgId' | ||
}); | ||
OrgSchema.virtual('permissions', { | ||
ref: ResourcePermissionCollectionName, | ||
localField: '_id', | ||
foreignField: 'orgId' | ||
}); | ||
|
||
try { | ||
OrgSchema.index( | ||
{ | ||
teamId: 1, | ||
path: 1, | ||
name: 1 | ||
}, | ||
{ | ||
unique: true | ||
} | ||
); | ||
OrgSchema.index({ path: 1 }); | ||
} catch (error) { | ||
console.log(error); | ||
} | ||
|
||
export const MongoOrgModel = getMongoModel<OrgSchemaType>(OrgCollectionName, OrgSchema); |
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