diff --git a/backend/src/main/java/ru/micord/ervu/account_applications/controller/RecruitmentController.java b/backend/src/main/java/ru/micord/ervu/account_applications/controller/RecruitmentController.java new file mode 100644 index 00000000..66f928df --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/account_applications/controller/RecruitmentController.java @@ -0,0 +1,32 @@ +package ru.micord.ervu.account_applications.controller; + +import javax.servlet.http.HttpServletRequest; + +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import ru.micord.ervu.account_applications.security.context.SecurityContext; +import ru.micord.ervu.account_applications.service.RecruitmentService; + +/** + * @author gulnaz + */ +@Controller +public class RecruitmentController { + + private final SecurityContext securityContext; + private final RecruitmentService recruitmentService; + + public RecruitmentController(SecurityContext securityContext, RecruitmentService recruitmentService) { + this.securityContext = securityContext; + this.recruitmentService = recruitmentService; + } + + @GetMapping(value = "/allowed") + public ResponseEntity checkAccess(HttpServletRequest request) { + Long appNumber = Long.valueOf(request.getHeader("app-number")); + boolean checkParents = Boolean.parseBoolean(request.getHeader("check-parents")); + String domainId = securityContext.getDomainId(); + return ResponseEntity.ok(recruitmentService.exists(appNumber, domainId, checkParents)); + } +} diff --git a/backend/src/main/java/ru/micord/ervu/account_applications/dao/RecruitmentDao.java b/backend/src/main/java/ru/micord/ervu/account_applications/dao/RecruitmentDao.java index c7dd6947..69366f82 100644 --- a/backend/src/main/java/ru/micord/ervu/account_applications/dao/RecruitmentDao.java +++ b/backend/src/main/java/ru/micord/ervu/account_applications/dao/RecruitmentDao.java @@ -5,12 +5,16 @@ import java.util.Optional; import java.util.UUID; import org.jooq.DSLContext; +import org.jooq.Field; +import org.jooq.Name; +import org.jooq.Select; import org.jooq.Table; import org.jooq.TableField; import org.jooq.impl.DSL; import org.springframework.stereotype.Repository; import ru.micord.ervu.account_applications.db_beans.public_.Tables; import ru.micord.ervu.account_applications.db_beans.public_.tables.Recruitment; +import ru.micord.ervu.account_applications.db_beans.public_.tables.UserApplicationList; import ru.micord.ervu.account_applications.db_beans.public_.tables.records.RecruitmentRecord; /** @@ -78,6 +82,43 @@ public class RecruitmentDao extends AbstractDataDao { setFieldByField(Recruitment.RECRUITMENT.ACTIVE, active, Recruitment.RECRUITMENT.IDM_ID, id); } + public boolean exists(Long appNumber, String domainId, boolean checkParents) { + Recruitment orgTable = Recruitment.RECRUITMENT; + UserApplicationList appTable = UserApplicationList.USER_APPLICATION_LIST; + String parentOrg = "parent_org"; + Name parentOrgName = DSL.name(parentOrg); + Table parentOrgTable = DSL.table(parentOrgName); + Field parentIdmId = DSL.field(DSL.name(parentOrg, orgTable.IDM_ID.getName()), + String.class + ); + Field parentParentId = DSL.field(DSL.name(parentOrg, orgTable.PARENT_ID.getName()), + String.class + ); + Select select = checkParents + ? dsl.withRecursive(parentOrgName) + .as(dsl.select(orgTable.IDM_ID, orgTable.PARENT_ID) + .from(orgTable) + .where(orgTable.IDM_ID.eq( + dsl.select(orgTable.IDM_ID) + .from(orgTable) + .join(appTable).on(appTable.RECRUITMENT_ID.eq(orgTable.ID)) + .where(appTable.NUMBER_APP.eq(appNumber)))) + .unionAll(dsl.select(orgTable.IDM_ID, orgTable.PARENT_ID) + .from(orgTable) + .join(parentOrgTable).on(orgTable.IDM_ID.eq(parentParentId)))) + .select(parentIdmId) + .from(parentOrgTable) + .where(parentIdmId.eq(domainId)) + : dsl.select(orgTable.IDM_ID) + .from(orgTable) + .join(appTable) + .on(appTable.RECRUITMENT_ID.eq(orgTable.ID)) + .where(appTable.NUMBER_APP.eq(appNumber) + .and(orgTable.IDM_ID.eq(domainId))); + + return dsl.fetchExists(select); + } + @Override protected Table getTable() { return Tables.RECRUITMENT; diff --git a/backend/src/main/java/ru/micord/ervu/account_applications/security/config/SecurityConfig.java b/backend/src/main/java/ru/micord/ervu/account_applications/security/config/SecurityConfig.java index eb4c0a3e..bc72aa0f 100644 --- a/backend/src/main/java/ru/micord/ervu/account_applications/security/config/SecurityConfig.java +++ b/backend/src/main/java/ru/micord/ervu/account_applications/security/config/SecurityConfig.java @@ -15,6 +15,8 @@ import org.springframework.security.web.authentication.UsernamePasswordAuthentic import ru.micord.ervu.account_applications.security.filter.JwtAuthenticationFilter; import ru.micord.ervu.account_applications.security.provider.ErvuJwtAuthenticationProvider; +import static ru.micord.ervu.account_applications.security.config.SecurityConstant.CREATE_APPLICATION_AUTHORITY; +import static ru.micord.ervu.account_applications.security.config.SecurityConstant.CREATE_APPLICATION_PERMISSIONS; @Configuration @EnableWebSecurity @@ -30,7 +32,9 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { .authorizeHttpRequests(auth -> { auth.antMatchers("/version").permitAll(); auth.antMatchers("/session").authenticated(); + auth.antMatchers(CREATE_APPLICATION_PERMISSIONS).hasAuthority(CREATE_APPLICATION_AUTHORITY); auth.anyRequest().authenticated(); + }) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS) diff --git a/backend/src/main/java/ru/micord/ervu/account_applications/security/config/SecurityConstant.java b/backend/src/main/java/ru/micord/ervu/account_applications/security/config/SecurityConstant.java new file mode 100644 index 00000000..01865bf5 --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/account_applications/security/config/SecurityConstant.java @@ -0,0 +1,16 @@ +package ru.micord.ervu.account_applications.security.config; + +/** + * @author gulnaz + */ +public class SecurityConstant { + public static final String CREATE_APPLICATION_AUTHORITY = "responsible_for_information_security"; + + public static final String[] CREATE_APPLICATION_PERMISSIONS = { + "/rpc/add_user_application/**", + "/rpc/edit_user_application/**", + "/rpc/block_user_application/**", + "/rpc/unblock_user_application/**", + "/rpc/reset_password/**" + }; +} diff --git a/backend/src/main/java/ru/micord/ervu/account_applications/service/RecruitmentService.java b/backend/src/main/java/ru/micord/ervu/account_applications/service/RecruitmentService.java new file mode 100644 index 00000000..75da6a65 --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/account_applications/service/RecruitmentService.java @@ -0,0 +1,21 @@ +package ru.micord.ervu.account_applications.service; + +import org.springframework.stereotype.Service; +import ru.micord.ervu.account_applications.dao.RecruitmentDao; + +/** + * @author gulnaz + */ +@Service +public class RecruitmentService { + + private final RecruitmentDao recruitmentDao; + + public RecruitmentService(RecruitmentDao recruitmentDao) { + this.recruitmentDao = recruitmentDao; + } + + public boolean exists(Long appNumber, String domainId, boolean checkParents) { + return recruitmentDao.exists(appNumber, domainId, checkParents); + } +} diff --git a/frontend/src/ts/modules/app/app-routing.module.ts b/frontend/src/ts/modules/app/app-routing.module.ts index 412b5f9a..e68e5aa5 100644 --- a/frontend/src/ts/modules/app/app-routing.module.ts +++ b/frontend/src/ts/modules/app/app-routing.module.ts @@ -2,7 +2,7 @@ import {NgModule} from "@angular/core"; import {RouterModule, Routes} from "@angular/router"; import {ConfirmExitGuard} from "@webbpm/base-package"; import {RolesGuard} from "./guard/RolesGuard"; - +import {ErvuRole} from "./enum/ErvuRole"; const appRoutes: Routes = [ { @@ -13,37 +13,58 @@ const appRoutes: Routes = [ { path: 'add_user_application', loadChildren: 'generated-sources/page-add_user_application.module#Pageadd_user_applicationModule', - canActivate: [ConfirmExitGuard, RolesGuard] + canActivate: [ConfirmExitGuard, RolesGuard], + data: { + roles: [ErvuRole.CREATOR] + } }, { path: 'edit_user_application', loadChildren: 'generated-sources/page-edit_user_application.module#Pageedit_user_applicationModule', - canActivate: [ConfirmExitGuard, RolesGuard] + canActivate: [ConfirmExitGuard, RolesGuard], + data: { + roles: [ErvuRole.CREATOR] + } }, { path: 'block_user_application', loadChildren: 'generated-sources/page-block_user_application.module#Pageblock_user_applicationModule', - canActivate: [ConfirmExitGuard, RolesGuard] + canActivate: [ConfirmExitGuard, RolesGuard], + data: { + roles: [ErvuRole.CREATOR] + } }, { path: 'reset_password', loadChildren: 'generated-sources/page-reset_password.module#Pagereset_passwordModule', - canActivate: [ConfirmExitGuard, RolesGuard] + canActivate: [ConfirmExitGuard, RolesGuard], + data: { + roles: [ErvuRole.CREATOR] + } }, { path: 'process_application/:id', loadChildren: 'generated-sources/page-process_application.module#Pageprocess_applicationModule', - canActivate: [ConfirmExitGuard, RolesGuard] + canActivate: [ConfirmExitGuard, RolesGuard], + data: { + checkOrg: true + } }, { path: 'process_application_edit_user/:id', loadChildren: 'generated-sources/page-process_application_edit_user.module#Pageprocess_application_edit_userModule', - canActivate: [ConfirmExitGuard, RolesGuard] + canActivate: [ConfirmExitGuard, RolesGuard], + data: { + checkOrg: true + } }, { path: 'unblock_user_application', loadChildren: 'generated-sources/page-unblock_user_application.module#Pageunblock_user_applicationModule', - canActivate: [ConfirmExitGuard, RolesGuard] + canActivate: [ConfirmExitGuard, RolesGuard], + data: { + roles: [ErvuRole.CREATOR] + } } ]; diff --git a/frontend/src/ts/modules/app/enum/ErvuRole.ts b/frontend/src/ts/modules/app/enum/ErvuRole.ts new file mode 100644 index 00000000..24e46f2d --- /dev/null +++ b/frontend/src/ts/modules/app/enum/ErvuRole.ts @@ -0,0 +1,5 @@ +export enum ErvuRole { + CREATOR = 'responsible_for_information_security', + REVIEWER = 'responsible_for_internal_control', + APPROVER = 'security_administrator' +} diff --git a/frontend/src/ts/modules/app/guard/RolesGuard.ts b/frontend/src/ts/modules/app/guard/RolesGuard.ts index c43a2fc8..f67430cb 100644 --- a/frontend/src/ts/modules/app/guard/RolesGuard.ts +++ b/frontend/src/ts/modules/app/guard/RolesGuard.ts @@ -8,15 +8,17 @@ import { import {Injectable} from "@angular/core"; import {AuthorizationService} from "../service/authorization.service"; import {TokenProvider} from "../provider/token.provider"; +import {HttpClient} from "@angular/common/http"; +import {ErvuRole} from "../enum/ErvuRole"; @Injectable({providedIn: 'root'}) export class RolesGuard implements CanActivate{ - protected readonly allowedRoles: string[] = []; + private allowedRoles: string[]; constructor(protected authService: AuthorizationService, protected tokenProvider: TokenProvider, - protected router: Router) { + protected router: Router, private httpClient: HttpClient) { } async canActivate( @@ -25,14 +27,21 @@ export class RolesGuard implements CanActivate{ if (!await this.tokenProvider.getToken()) { return this.getUrlOnFailure() } + this.allowedRoles = route.data && route.data.roles ? route.data.roles : []; + let checkOrg = route.data && route.data.checkOrg; if (!this.authService.isAuthorized()) { return this.authService.getCurrentSession() - .then(() => this.checkRoles() ? true : this.getUrlOnFailure()) + .then(() => { + if (checkOrg) { + return this.checkOrgByAppId(route.params.id); + } + return this.checkRoles() ? true : this.getUrlOnFailure(); + }) .catch(() => this.getUrlOnFailure()); } else { - return this.checkRoles(); + return checkOrg ? this.checkOrgByAppId(route.params.id) : this.checkRoles(); } } @@ -44,4 +53,26 @@ export class RolesGuard implements CanActivate{ return this.allowedRoles.length === 0 || this.authService.hasAnyRole(this.allowedRoles); } -} \ No newline at end of file + + private checkOrgByAppId(id: string): Promise { + if (this.authService.hasAnyRole([ErvuRole.CREATOR, ErvuRole.REVIEWER])) { + return this.httpClient.get("allowed", { + headers: { + 'app-number': id, + 'check-parents': (!this.authService.hasRole(ErvuRole.CREATOR)).toString() + }, + observe: 'response' + }) + .toPromise() + .then(response => { + return response.body ? true : this.getUrlOnFailure(); + }) + .catch(() => this.getUrlOnFailure()); + } + else if (this.authService.hasRole(ErvuRole.APPROVER)) { + return Promise.resolve(true); + } + + return Promise.resolve(this.getUrlOnFailure()); + } +}