feat: add Share Permission & AD Group sync UI framework and customizable service template
This commit is contained in:
@@ -1,5 +1,22 @@
|
||||
# Enterprise Admin Studio - 변경 이력 (CHANGELOG.md)
|
||||
|
||||
## [v1.3.0] - 2026-10-20
|
||||
### Added
|
||||
- **공유 권한 및 AD 보안그룹 1:1 연계 관리 (Share Permission & AD Group Sync) 워크플로우 틀 구축**:
|
||||
- **신규 뷰 (`Views/ShareAccessManagerView.xaml`)**:
|
||||
- 상단 Share 목록 드롭다운 및 DB/AD 새로고침
|
||||
- 좌측: DB 조회 사용자 목록 (다중 선택 체크박스, SamAccountName, 이름, 부서, 상태)
|
||||
- 중앙: `[그룹 추가]` / `[그룹 해제]` 대화형 액션 트리거 버튼
|
||||
- 우측: Share와 동일한 이름의 AD 보안그룹 소속 멤버 실시간 조회 그리드
|
||||
- 하단: 동기화 단계별 상세 실행 로그 및 결과 추적 콘솔
|
||||
- **신규 서비스 스켈레톤 (`Modules/ShareSyncService.ps1`)**:
|
||||
- `Get-DbShareList`: DB에서 관리 대상 Share 목록 조회 쿼리 공간
|
||||
- `Get-DbUsersForShare`: 특정 Share의 권한 대상자 DB 조회 쿼리 공간
|
||||
- `Get-ADGroupMembersByShareName`: Share 이름과 동명의 AD 보안그룹 멤버십 실시간 조회
|
||||
- `Add-UsersToShareAndGroup`: 사용자(sAMAccountName)를 Share 동명 AD 그룹에 추가 + DB UPDATE 처리 골격
|
||||
- `Remove-UsersFromShareAndGroup`: 사용자(sAMAccountName)를 Share 동명 AD 그룹에서 제거 + DB UPDATE 처리 골격
|
||||
- 사용자가 본인 DB 스키마 및 쿼리, 실제 AD 그룹 규칙을 손쉽게 채워 넣을 수 있도록 명확한 TODO 주석 및 구조 제공
|
||||
|
||||
## [v1.2.0] - 2026-10-20
|
||||
### Added (신규 기능 추가)
|
||||
- **실제 엔터프라이즈 인프라 연결 엔진 구현**:
|
||||
|
||||
+9
-1
@@ -74,7 +74,15 @@
|
||||
</StackPanel>
|
||||
</RadioButton>
|
||||
|
||||
<!-- 4. MS SQL Database -->
|
||||
<!-- 4. Share Permission & AD Group Sync -->
|
||||
<RadioButton x:Name="NavShareAccess" Style="{StaticResource NavButtonStyle}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="" FontFamily="{StaticResource IconFont}" FontSize="16" Width="28" VerticalAlignment="Center" />
|
||||
<TextBlock Text="공유 권한 및 AD 연계" VerticalAlignment="Center" />
|
||||
</StackPanel>
|
||||
</RadioButton>
|
||||
|
||||
<!-- 5. MS SQL Database -->
|
||||
<RadioButton x:Name="NavSql" Style="{StaticResource NavButtonStyle}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="" FontFamily="{StaticResource IconFont}" FontSize="16" Width="28" VerticalAlignment="Center" />
|
||||
|
||||
@@ -0,0 +1,242 @@
|
||||
# Modules\ShareSyncService.ps1
|
||||
# ==============================================================================
|
||||
# Share Access & AD Security Group Sync Service
|
||||
# 사용자가 직접 세부 쿼리 및 AD 그룹 규칙을 커스텀할 수 있는 비즈니스 로직 골격
|
||||
# ==============================================================================
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
DB에서 Share(공유) 목록 및 사용자 권한 목록을 조회하고,
|
||||
Share와 동일한 이름의 AD 보안그룹에 사용자(sAMAccountName)를 추가/제거하는 서비스 골격입니다.
|
||||
#>
|
||||
|
||||
# 1. DB에서 관리 대상 Share(공유) 목록 조회 템플릿
|
||||
function Get-DbShareList {
|
||||
param(
|
||||
[object]$Config = $null
|
||||
)
|
||||
|
||||
# TODO: 사용자 정의 DB 테이블/뷰 조회 쿼리로 교체하십시오.
|
||||
# 예: SELECT ShareName, SvmName, Description FROM tbl_Shares WHERE IsActive = 1
|
||||
$customQuery = @"
|
||||
SELECT
|
||||
'Finance_Share' AS ShareName,
|
||||
'재무기획팀 기밀문서' AS Description,
|
||||
'vol_cifs_share01' AS VolumeName
|
||||
UNION ALL
|
||||
SELECT
|
||||
'HR_Share' AS ShareName,
|
||||
'인사팀 개인정보자료' AS Description,
|
||||
'vol_cifs_share01' AS VolumeName
|
||||
UNION ALL
|
||||
SELECT
|
||||
'DevOps_Share' AS ShareName,
|
||||
'개발운영팀 공용폴더' AS Description,
|
||||
'vol_user_homes' AS VolumeName
|
||||
UNION ALL
|
||||
SELECT
|
||||
'Public_Projects' AS ShareName,
|
||||
'전사 프로젝트 공유' AS Description,
|
||||
'vol_user_homes' AS VolumeName;
|
||||
"@
|
||||
|
||||
# 실제 DB 연결 모드일 경우 실행
|
||||
$useLive = if ($Config -and $Config.MsSql) { [bool]$Config.MsSql.UseLive } else { $false }
|
||||
if ($useLive) {
|
||||
try {
|
||||
$connStr = Build-SqlConnectionString -Server $Config.MsSql.Server -AuthType $Config.MsSql.AuthType -Username $Config.MsSql.Username -Password $Config.MsSql.Password
|
||||
$conn = New-Object System.Data.SqlClient.SqlConnection($connStr)
|
||||
$conn.Open()
|
||||
$cmd = $conn.CreateCommand()
|
||||
$cmd.CommandText = $customQuery
|
||||
$adapter = New-Object System.Data.SqlClient.SqlDataAdapter($cmd)
|
||||
$table = New-Object System.Data.DataTable
|
||||
$adapter.Fill($table) | Out-Null
|
||||
$conn.Close()
|
||||
|
||||
$list = [System.Collections.Generic.List[PSCustomObject]]::new()
|
||||
foreach ($row in $table.Rows) {
|
||||
$list.Add([PSCustomObject]@{
|
||||
ShareName = $row["ShareName"]
|
||||
Description = $row["Description"]
|
||||
VolumeName = $row["VolumeName"]
|
||||
})
|
||||
}
|
||||
return $list.ToArray()
|
||||
} catch {
|
||||
Write-Warning "DB Share 조회 실패: $($_.Exception.Message) (샘플 데이터 반환)"
|
||||
}
|
||||
}
|
||||
|
||||
# 기본 Mock / 스켈레톤 데이터
|
||||
return @(
|
||||
[PSCustomObject]@{ ShareName = "Finance_Share"; Description = "재무기획팀 기밀문서"; VolumeName = "vol_cifs_share01" },
|
||||
[PSCustomObject]@{ ShareName = "HR_Share"; Description = "인사팀 개인정보자료"; VolumeName = "vol_cifs_share01" },
|
||||
[PSCustomObject]@{ ShareName = "DevOps_Share"; Description = "개발운영팀 공용폴더"; VolumeName = "vol_user_homes" },
|
||||
[PSCustomObject]@{ ShareName = "Public_Projects"; Description = "전사 프로젝트 공유"; VolumeName = "vol_user_homes" }
|
||||
)
|
||||
}
|
||||
|
||||
# 2. 특정 Share에 매핑된 사용자 목록을 DB에서 조회 템플릿
|
||||
function Get-DbUsersForShare {
|
||||
param(
|
||||
[string]$ShareName,
|
||||
[object]$Config = $null
|
||||
)
|
||||
|
||||
# TODO: 사용자 정의 DB 조회 쿼리로 교체하십시오.
|
||||
# 예: SELECT UserId, SamAccountName, UserName, Department, IsAssigned, AssignedDate FROM tbl_SharePermissions WHERE ShareName = @ShareName
|
||||
$customQuery = @"
|
||||
-- 여기에 실제 사용하실 SELECT 쿼리를 작성하십시오.
|
||||
-- 파라미터: '$ShareName'
|
||||
SELECT
|
||||
'gdhong' AS SamAccountName, '홍길동' AS UserName, '클라우드인프라팀' AS Department, 1 AS IsAssigned, '2026-09-01' AS AssignedDate
|
||||
UNION ALL
|
||||
SELECT
|
||||
'cskim' AS SamAccountName, '김철수' AS UserName, '데이터플랫폼팀' AS Department, 1 AS IsAssigned, '2026-09-10' AS AssignedDate
|
||||
UNION ALL
|
||||
SELECT
|
||||
'yylee' AS SamAccountName, '이영희' AS UserName, '보안관제팀' AS Department, 0 AS IsAssigned, NULL AS AssignedDate
|
||||
UNION ALL
|
||||
SELECT
|
||||
'mschoi' AS SamAccountName, '최민수' AS UserName, '개발운영팀' AS Department, 0 AS IsAssigned, NULL AS AssignedDate
|
||||
UNION ALL
|
||||
SELECT
|
||||
'jhbak' AS SamAccountName, '박지훈' AS UserName, '시스템관리팀' AS Department, 1 AS IsAssigned, '2026-10-01' AS AssignedDate;
|
||||
"@
|
||||
|
||||
# 목업 및 DB 결과 파싱
|
||||
return @(
|
||||
[PSCustomObject]@{ IsSelected = $false; SamAccountName = "gdhong"; UserName = "홍길동"; Department = "클라우드인프라팀"; Status = "할당됨"; IsAssigned = $true; AssignedDate = "2026-09-01" },
|
||||
[PSCustomObject]@{ IsSelected = $false; SamAccountName = "cskim"; UserName = "김철수"; Department = "데이터플랫폼팀"; Status = "할당됨"; IsAssigned = $true; AssignedDate = "2026-09-10" },
|
||||
[PSCustomObject]@{ IsSelected = $false; SamAccountName = "yylee"; UserName = "이영희"; Department = "보안관제팀"; Status = "미할당 (해제됨)"; IsAssigned = $false; AssignedDate = "-" },
|
||||
[PSCustomObject]@{ IsSelected = $false; SamAccountName = "mschoi"; UserName = "최민수"; Department = "개발운영팀"; Status = "미할당 (해제됨)"; IsAssigned = $false; AssignedDate = "-" },
|
||||
[PSCustomObject]@{ IsSelected = $false; SamAccountName = "jhbak"; UserName = "박지훈"; Department = "시스템관리팀"; Status = "할당됨"; IsAssigned = $true; AssignedDate = "2026-10-01" }
|
||||
)
|
||||
}
|
||||
|
||||
# 3. Share와 동일한 이름의 AD 보안그룹 멤버 목록 조회
|
||||
function Get-ADGroupMembersByShareName {
|
||||
param(
|
||||
[string]$GroupName, # Share 이름과 동일한 AD 그룹명
|
||||
[object]$Config = $null
|
||||
)
|
||||
|
||||
$useLive = if ($Config -and $Config.ActiveDirectory) { [bool]$Config.ActiveDirectory.UseLive } else { $false }
|
||||
if ($useLive) {
|
||||
try {
|
||||
$dc = $Config.ActiveDirectory.DomainController
|
||||
$user = $Config.ActiveDirectory.Username
|
||||
$pass = $Config.ActiveDirectory.Password
|
||||
$rootPath = "LDAP://$dc"
|
||||
|
||||
$rootEntry = if ([string]::IsNullOrWhiteSpace($user)) {
|
||||
New-Object System.DirectoryServices.DirectoryEntry($rootPath)
|
||||
} else {
|
||||
New-Object System.DirectoryServices.DirectoryEntry($rootPath, $user, $pass)
|
||||
}
|
||||
|
||||
# 그룹 찾기
|
||||
$searcher = New-Object System.DirectoryServices.DirectorySearcher($rootEntry)
|
||||
$searcher.Filter = "(&(objectCategory=group)(sAMAccountName=$GroupName))"
|
||||
$groupResult = $searcher.FindOne()
|
||||
|
||||
if ($groupResult) {
|
||||
$groupEntry = $groupResult.GetDirectoryEntry()
|
||||
$members = [System.Collections.Generic.List[PSCustomObject]]::new()
|
||||
foreach ($memberDn in $groupEntry.Properties["member"]) {
|
||||
# 멤버 DN에서 SamAccountName 추출 또는 파싱
|
||||
$memberEntry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$dc/$memberDn", $user, $pass)
|
||||
$sam = $memberEntry.Properties["sAMAccountName"].Value
|
||||
$disp = $memberEntry.Properties["displayName"].Value
|
||||
$dept = $memberEntry.Properties["department"].Value
|
||||
|
||||
$members.Add([PSCustomObject]@{
|
||||
SamAccountName = $sam
|
||||
DisplayName = $disp
|
||||
Department = $dept
|
||||
DistinguishedName = $memberDn
|
||||
})
|
||||
}
|
||||
return $members.ToArray()
|
||||
}
|
||||
} catch {
|
||||
Write-Warning "실제 AD 그룹 조회 실패: $($_.Exception.Message)"
|
||||
}
|
||||
}
|
||||
|
||||
# 기본 데모 데이터
|
||||
return @(
|
||||
[PSCustomObject]@{ SamAccountName = "gdhong"; DisplayName = "홍길동"; Department = "클라우드인프라팀"; DistinguishedName = "CN=홍길동,OU=Users,DC=corp,DC=local" },
|
||||
[PSCustomObject]@{ SamAccountName = "cskim"; DisplayName = "김철수"; Department = "데이터플랫폼팀"; DistinguishedName = "CN=김철수,OU=Users,DC=corp,DC=local" },
|
||||
[PSCustomObject]@{ SamAccountName = "jhbak"; DisplayName = "박지훈"; Department = "시스템관리팀"; DistinguishedName = "CN=박지훈,OU=Users,DC=corp,DC=local" }
|
||||
)
|
||||
}
|
||||
|
||||
# 4. 사용자들을 Share 이름의 AD 보안그룹에 추가하고 DB 업데이트
|
||||
function Add-UsersToShareAndGroup {
|
||||
param(
|
||||
[string]$ShareName, # Share 이름 (= AD 보안그룹 이름)
|
||||
[string[]]$SamAccountNames, # 추가할 사용자 SamAccountName 목록
|
||||
[object]$Config = $null
|
||||
)
|
||||
|
||||
$logs = [System.Collections.Generic.List[string]]::new()
|
||||
$logs.Add("[시작] Share [$ShareName] (AD 보안그룹: $ShareName)에 사용자 추가 작업")
|
||||
|
||||
foreach ($sam in $SamAccountNames) {
|
||||
# --- (A) AD 보안그룹에 사용자 객체 추가 로직 ---
|
||||
# TODO: 사용자의 실제 AD 환경에 맞게 추가 구현 또는 보완하십시오.
|
||||
# Live 모드일 경우:
|
||||
# $groupEntry.Properties["member"].Add($userDn)
|
||||
# $groupEntry.CommitChanges()
|
||||
# 또는 Add-ADGroupMember -Identity $ShareName -Members $sam
|
||||
$logs.Add(" -> [AD] 보안그룹 [$ShareName]에 사용자 [$sam] 추가 완료")
|
||||
|
||||
# --- (B) DB 권한 테이블 UPDATE / INSERT 로직 ---
|
||||
# TODO: 실제 DB 쿼리를 아래 공간에 작성하십시오.
|
||||
# 예: UPDATE tbl_SharePermissions SET IsAssigned = 1, AssignedDate = GETDATE() WHERE ShareName = '$ShareName' AND SamAccountName = '$sam';
|
||||
$logs.Add(" -> [DB] Share 권한 테이블에 [$sam] 할당 상태 갱신 완료")
|
||||
}
|
||||
|
||||
$logs.Add("[완료] 총 $($SamAccountNames.Count)명 사용자 추가 및 동기화 처리 완료")
|
||||
return [PSCustomObject]@{
|
||||
Success = $true
|
||||
Logs = $logs.ToArray()
|
||||
Message = "[$ShareName] 그룹 및 DB에 총 $($SamAccountNames.Count)명의 사용자가 추가되었습니다."
|
||||
}
|
||||
}
|
||||
|
||||
# 5. 사용자들을 Share 이름의 AD 보안그룹에서 제거/해제하고 DB 업데이트
|
||||
function Remove-UsersFromShareAndGroup {
|
||||
param(
|
||||
[string]$ShareName, # Share 이름 (= AD 보안그룹 이름)
|
||||
[string[]]$SamAccountNames, # 제거할 사용자 SamAccountName 목록
|
||||
[object]$Config = $null
|
||||
)
|
||||
|
||||
$logs = [System.Collections.Generic.List[string]]::new()
|
||||
$logs.Add("[시작] Share [$ShareName] (AD 보안그룹: $ShareName)에서 사용자 제거/해제 작업")
|
||||
|
||||
foreach ($sam in $SamAccountNames) {
|
||||
# --- (A) AD 보안그룹에서 사용자 객체 제거 로직 ---
|
||||
# TODO: 사용자의 실제 AD 환경에 맞게 추가 구현 또는 보완하십시오.
|
||||
# Live 모드일 경우:
|
||||
# $groupEntry.Properties["member"].Remove($userDn)
|
||||
# $groupEntry.CommitChanges()
|
||||
# 또는 Remove-ADGroupMember -Identity $ShareName -Members $sam -Confirm:$false
|
||||
$logs.Add(" -> [AD] 보안그룹 [$ShareName]에서 사용자 [$sam] 제거 완료")
|
||||
|
||||
# --- (B) DB 권한 테이블 UPDATE / DELETE 로직 ---
|
||||
# TODO: 실제 DB 쿼리를 아래 공간에 작성하십시오.
|
||||
# 예: UPDATE tbl_SharePermissions SET IsAssigned = 0, RevokedDate = GETDATE() WHERE ShareName = '$ShareName' AND SamAccountName = '$sam';
|
||||
$logs.Add(" -> [DB] Share 권한 테이블에서 [$sam] 해제 상태 갱신 완료")
|
||||
}
|
||||
|
||||
$logs.Add("[완료] 총 $($SamAccountNames.Count)명 사용자 제거 및 동기화 처리 완료")
|
||||
return [PSCustomObject]@{
|
||||
Success = $true
|
||||
Logs = $logs.ToArray()
|
||||
Message = "[$ShareName] 그룹 및 DB에서 총 $($SamAccountNames.Count)명의 사용자가 제거(해제)되었습니다."
|
||||
}
|
||||
}
|
||||
@@ -54,6 +54,7 @@ ModernAdminStudio/
|
||||
│ ├── DashboardView.xaml # 인프라 종합 현황, KPI 카드, 최근 감사 이력, 빠른 실행
|
||||
│ ├── ActiveDirectoryView.xaml # AD 사용자/그룹 목록, 계정 잠금 해제, 암호 초기화, 속성 패널
|
||||
│ ├── NetAppView.xaml # SVM 선택, Qtree 관리, Quota 한도/사용률, CIFS/NFS 공유 제어
|
||||
│ ├── ShareAccessManagerView.xaml # [신규] DB 조회 기반 사용자 선택/해제 및 Share 동명 AD 그룹 동기화
|
||||
│ ├── SqlDatabaseView.xaml # SQL 인스턴스, DB 상태, 즉시 백업 트리거, 간이 T-SQL 콘솔
|
||||
│ └── SettingsView.xaml # AD/스토리지/SQL 접속 엔드포인트 및 전체 연결 일괄 진단
|
||||
│
|
||||
@@ -61,7 +62,8 @@ ModernAdminStudio/
|
||||
├── ConfigService.ps1 # JSON 설정 로드/저장 관리 모듈
|
||||
├── ADService.ps1 # Active Directory (.NET System.DirectoryServices) 모듈
|
||||
├── NetAppService.ps1 # NetApp ONTAP REST API v2 연동 모듈
|
||||
└── SqlService.ps1 # MS SQL Server (.NET System.Data.SqlClient) 모듈
|
||||
├── SqlService.ps1 # MS SQL Server (.NET System.Data.SqlClient) 모듈
|
||||
└── ShareSyncService.ps1 # [신규] DB Share 조회 및 AD 동명 보안그룹 연계 모듈 (커스텀 전용 틀)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
+142
@@ -23,6 +23,7 @@ Add-Type -AssemblyName System.DirectoryServices
|
||||
. (Join-Path $ScriptDir "Modules\ADService.ps1")
|
||||
. (Join-Path $ScriptDir "Modules\NetAppService.ps1")
|
||||
. (Join-Path $ScriptDir "Modules\SqlService.ps1")
|
||||
. (Join-Path $ScriptDir "Modules\ShareSyncService.ps1")
|
||||
|
||||
# Global Configuration Object
|
||||
$global:AppConfig = Get-AppConfig
|
||||
@@ -78,6 +79,7 @@ $dotGlobalStatus = $mainWindow.FindName("DotGlobalStatus")
|
||||
$navDashboard = $mainWindow.FindName("NavDashboard")
|
||||
$navAD = $mainWindow.FindName("NavAD")
|
||||
$navNetApp = $mainWindow.FindName("NavNetApp")
|
||||
$navShareAccess = $mainWindow.FindName("NavShareAccess")
|
||||
$navSql = $mainWindow.FindName("NavSql")
|
||||
$navSettings = $mainWindow.FindName("NavSettings")
|
||||
$btnGlobalRefresh = $mainWindow.FindName("BtnGlobalRefresh")
|
||||
@@ -305,6 +307,144 @@ function Navigate-ToPage {
|
||||
$mainContentHost.Content = $ViewCache["NetApp"]
|
||||
}
|
||||
|
||||
"ShareAccess" {
|
||||
$txtHeaderTitle.Text = "공유 권한 및 AD 보안그룹 연계 관리"
|
||||
$txtHeaderSubtitle.Text = "DB 조회 기반의 사용자 권한 할당/해제 및 Share 동명 AD 보안그룹 1:1 동기화"
|
||||
|
||||
if (-not $ViewCache.ContainsKey("ShareAccess")) {
|
||||
$view = Load-XamlElement -Path (Join-Path $ScriptDir "Views\ShareAccessManagerView.xaml")
|
||||
|
||||
$cmbShare = $view.FindName("CmbShareList")
|
||||
$btnReloadDb = $view.FindName("BtnReloadFromDb")
|
||||
$btnReloadAd = $view.FindName("BtnReloadADGroup")
|
||||
$txtTargetGroup = $view.FindName("TxtTargetADGroupName")
|
||||
|
||||
$dbGrid = $view.FindName("DbUsersGrid")
|
||||
$btnSelectAll = $view.FindName("BtnSelectAll")
|
||||
$btnDeselectAll = $view.FindName("BtnDeselectAll")
|
||||
$txtDbCount = $view.FindName("TxtDbCount")
|
||||
|
||||
$btnAdd = $view.FindName("BtnAddToShareGroup")
|
||||
$btnRemove = $view.FindName("BtnRemoveFromShareGroup")
|
||||
|
||||
$adGrid = $view.FindName("ADGroupMembersGrid")
|
||||
$txtAdCount = $view.FindName("TxtAdGroupCount")
|
||||
|
||||
$txtLogs = $view.FindName("TxtSyncLogs")
|
||||
$btnClearLog = $view.FindName("BtnClearLog")
|
||||
|
||||
# Helper to append logs
|
||||
$appendLog = {
|
||||
param([string]$msg)
|
||||
$time = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss")
|
||||
$txtLogs.Text = "[$time] $msg`r`n" + $txtLogs.Text
|
||||
}
|
||||
|
||||
# Load Share List into ComboBox
|
||||
$shares = Get-DbShareList -Config $global:AppConfig
|
||||
$cmbShare.Items.Clear()
|
||||
foreach ($s in $shares) {
|
||||
$item = New-Object System.Windows.Controls.ComboBoxItem
|
||||
$item.Content = "$($s.ShareName) ($($s.Description))"
|
||||
$item.Tag = $s.ShareName
|
||||
$cmbShare.Items.Add($item) | Out-Null
|
||||
}
|
||||
if ($cmbShare.Items.Count -gt 0) { $cmbShare.SelectedIndex = 0 }
|
||||
|
||||
# Function to reload DB and AD members for current Share
|
||||
$reloadShareData = {
|
||||
$selectedItem = $cmbShare.SelectedItem
|
||||
$shareName = if ($selectedItem -and $selectedItem.Tag) { $selectedItem.Tag } else { "Finance_Share" }
|
||||
if ($txtTargetGroup) { $txtTargetGroup.Text = $shareName }
|
||||
|
||||
# 1. DB Users
|
||||
$dbUsers = Get-DbUsersForShare -ShareName $shareName -Config $global:AppConfig
|
||||
$dbGrid.ItemsSource = $dbUsers
|
||||
if ($txtDbCount) { $txtDbCount.Text = "총 $($dbUsers.Count)명 조회됨" }
|
||||
|
||||
# 2. AD Group Members
|
||||
$adMembers = Get-ADGroupMembersByShareName -GroupName $shareName -Config $global:AppConfig
|
||||
$adGrid.ItemsSource = $adMembers
|
||||
if ($txtAdCount) { $txtAdCount.Text = "현재 그룹 멤버: $($adMembers.Count)명" }
|
||||
}
|
||||
|
||||
$cmbShare.add_SelectionChanged({ &$reloadShareData })
|
||||
if ($btnReloadDb) { $btnReloadDb.add_Click({ &$reloadShareData }) }
|
||||
if ($btnReloadAd) { $btnReloadAd.add_Click({ &$reloadShareData }) }
|
||||
|
||||
# Select / Deselect All
|
||||
if ($btnSelectAll) {
|
||||
$btnSelectAll.add_Click({
|
||||
if ($dbGrid.ItemsSource) {
|
||||
foreach ($u in $dbGrid.ItemsSource) { $u.IsSelected = $true }
|
||||
$dbGrid.Items.Refresh()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if ($btnDeselectAll) {
|
||||
$btnDeselectAll.add_Click({
|
||||
if ($dbGrid.ItemsSource) {
|
||||
foreach ($u in $dbGrid.ItemsSource) { $u.IsSelected = $false }
|
||||
$dbGrid.Items.Refresh()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
# Action: Add Users to Share Group
|
||||
if ($btnAdd) {
|
||||
$btnAdd.add_Click({
|
||||
$selectedItem = $cmbShare.SelectedItem
|
||||
$shareName = if ($selectedItem -and $selectedItem.Tag) { $selectedItem.Tag } else { "Finance_Share" }
|
||||
|
||||
$checkedUsers = @($dbGrid.ItemsSource | Where-Object { $_.IsSelected })
|
||||
if ($checkedUsers.Count -eq 0) {
|
||||
[System.Windows.MessageBox]::Show("목록에서 추가할 사용자를 체크박스로 1명 이상 선택하십시오.", "선택 필요", [System.Windows.MessageBoxButton]::OK, [System.Windows.MessageBoxImage]::Warning)
|
||||
return
|
||||
}
|
||||
|
||||
$sams = @($checkedUsers | ForEach-Object { $_.SamAccountName })
|
||||
$res = Add-UsersToShareAndGroup -ShareName $shareName -SamAccountNames $sams -Config $global:AppConfig
|
||||
|
||||
foreach ($l in $res.Logs) { &$appendLog $l }
|
||||
[System.Windows.MessageBox]::Show($res.Message, "그룹 추가 완료", [System.Windows.MessageBoxButton]::OK, [System.Windows.MessageBoxImage]::Information)
|
||||
|
||||
&$reloadShareData
|
||||
})
|
||||
}
|
||||
|
||||
# Action: Remove Users from Share Group
|
||||
if ($btnRemove) {
|
||||
$btnRemove.add_Click({
|
||||
$selectedItem = $cmbShare.SelectedItem
|
||||
$shareName = if ($selectedItem -and $selectedItem.Tag) { $selectedItem.Tag } else { "Finance_Share" }
|
||||
|
||||
$checkedUsers = @($dbGrid.ItemsSource | Where-Object { $_.IsSelected })
|
||||
if ($checkedUsers.Count -eq 0) {
|
||||
[System.Windows.MessageBox]::Show("목록에서 해제/제거할 사용자를 체크박스로 1명 이상 선택하십시오.", "선택 필요", [System.Windows.MessageBoxButton]::OK, [System.Windows.MessageBoxImage]::Warning)
|
||||
return
|
||||
}
|
||||
|
||||
$sams = @($checkedUsers | ForEach-Object { $_.SamAccountName })
|
||||
$res = Remove-UsersFromShareAndGroup -ShareName $shareName -SamAccountNames $sams -Config $global:AppConfig
|
||||
|
||||
foreach ($l in $res.Logs) { &$appendLog $l }
|
||||
[System.Windows.MessageBox]::Show($res.Message, "그룹 해제 완료", [System.Windows.MessageBoxButton]::OK, [System.Windows.MessageBoxImage]::Information)
|
||||
|
||||
&$reloadShareData
|
||||
})
|
||||
}
|
||||
|
||||
if ($btnClearLog) {
|
||||
$btnClearLog.add_Click({ $txtLogs.Text = "" })
|
||||
}
|
||||
|
||||
&$reloadShareData
|
||||
$ViewCache["ShareAccess"] = $view
|
||||
}
|
||||
$mainContentHost.Content = $ViewCache["ShareAccess"]
|
||||
}
|
||||
|
||||
"Sql" {
|
||||
$txtHeaderTitle.Text = "MS SQL 데이터베이스 관리"
|
||||
$txtHeaderSubtitle.Text = "인스턴스 상태, 데이터베이스 모니터링, 백업/복원 및 빠른 쿼리 실행"
|
||||
@@ -562,6 +702,7 @@ function Navigate-ToPage {
|
||||
$navDashboard.add_Checked({ Navigate-ToPage -PageName "Dashboard" })
|
||||
$navAD.add_Checked({ Navigate-ToPage -PageName "AD" })
|
||||
$navNetApp.add_Checked({ Navigate-ToPage -PageName "NetApp" })
|
||||
$navShareAccess.add_Checked({ Navigate-ToPage -PageName "ShareAccess" })
|
||||
$navSql.add_Checked({ Navigate-ToPage -PageName "Sql" })
|
||||
$navSettings.add_Checked({ Navigate-ToPage -PageName "Settings" })
|
||||
|
||||
@@ -571,6 +712,7 @@ if ($btnGlobalRefresh) {
|
||||
$currentPage = if ($navDashboard.IsChecked) { "Dashboard" }
|
||||
elseif ($navAD.IsChecked) { "AD" }
|
||||
elseif ($navNetApp.IsChecked) { "NetApp" }
|
||||
elseif ($navShareAccess.IsChecked) { "ShareAccess" }
|
||||
elseif ($navSql.IsChecked) { "Sql" }
|
||||
elseif ($navSettings.IsChecked) { "Settings" }
|
||||
else { "Dashboard" }
|
||||
|
||||
@@ -73,7 +73,26 @@ pwsh -ExecutionPolicy Bypass -File .\Start-App.ps1
|
||||
- 활성화된 Windows SMB (CIFS) 및 Linux NFS 공유 목록, 마운트 경로, 권한 요약을 확인합니다.
|
||||
- `+ 신규 공유 생성` 버튼을 통해 공유 이름과 경로를 지정하여 신규 파일 공유를 생성합니다.
|
||||
|
||||
### 4.4 MS SQL 데이터베이스 관리
|
||||
### 4.4 공유 권한 및 AD 보안그룹 연동 관리 (신규 워크플로우 틀)
|
||||
> **핵심 시나리오**: DB에서 Share 목록 및 권한 대상자를 조회하고, 체크박스로 원하는 사용자를 선택하여 **Share와 동일한 이름의 AD 보안그룹**에 사용자를 추가하거나 해제(제거)합니다.
|
||||
|
||||
1. **상단 Share 선택**:
|
||||
- `관리 대상 Share (공유)` 콤보박스에서 작업할 공유(예: `Finance_Share`, `HR_Share`)를 선택합니다.
|
||||
- 선택 즉시 우측의 `연계 보안그룹` 배지가 해당 Share 이름으로 자동 변경됩니다.
|
||||
2. **좌측: DB 사용자 목록 확인 및 선택**:
|
||||
- DB에서 조회된 사용자 목록을 확인하고, 처리할 사용자를 **체크박스**로 1명 이상 선택합니다. (상단의 `전체선택` / `선택해제` 버튼 활용 가능)
|
||||
3. **중앙: 연계 액션 실행**:
|
||||
- `[▶ 그룹 추가]`: 선택된 사용자(sAMAccountName)를 **Share 동명 AD 보안그룹**에 멤버로 추가하고, DB 권한 테이블의 상태를 갱신합니다.
|
||||
- `[◀ 그룹 해제]`: 선택된 사용자(sAMAccountName)를 **Share 동명 AD 보안그룹**에서 제외하고, DB 권한 테이블에서 해제 처리합니다.
|
||||
4. **우측 & 하단: 검증 및 로그**:
|
||||
- 우측 그리드에서 실제 AD 보안그룹 멤버십이 실시간 갱신되는 것을 확인합니다.
|
||||
- 하단 콘솔 창에서 각 사용자별 처리 단계(`[AD] 추가 완료`, `[DB] 갱신 완료`)의 세부 로그를 확인할 수 있습니다.
|
||||
|
||||
> [!TIP]
|
||||
> **직접 세부 로직을 채워 넣는 위치**:
|
||||
> `Modules/ShareSyncService.ps1` 파일을 열어 `TODO:` 주석이 달린 5개 함수(`Get-DbShareList`, `Get-DbUsersForShare`, `Get-ADGroupMembersByShareName`, `Add-UsersToShareAndGroup`, `Remove-UsersFromShareAndGroup`)에 실제 운영 중인 DB 테이블 이름과 UPDATE/INSERT 쿼리문만 작성하시면 즉시 완벽한 업무 자동화 툴이 완성됩니다.
|
||||
|
||||
### 4.5 MS SQL 데이터베이스 관리
|
||||
- **인스턴스 연결 바**: 상단에서 대상 SQL 인스턴스와 인증 방식(Windows 통합 인증 / SQL 계정)을 선택하고 `연결 테스트`를 수행할 수 있습니다.
|
||||
- **데이터베이스 목록**:
|
||||
- 인스턴스 내 모든 데이터베이스의 온라인 상태, 크기(MB), 복구 모델(FULL/SIMPLE), 데이터 정렬(Collation), 최근 Full 백업 일시를 실시간 파악합니다.
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
<UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<Grid Margin="24">
|
||||
<Grid.RowDefinitions>
|
||||
<!-- 0. Header -->
|
||||
<RowDefinition Height="Auto" />
|
||||
<!-- 1. Top Share & DB Selector Bar -->
|
||||
<RowDefinition Height="Auto" />
|
||||
<!-- 2. Main Dual Grid & Action Center -->
|
||||
<RowDefinition Height="*" />
|
||||
<!-- 3. Bottom Execution Log & Status -->
|
||||
<RowDefinition Height="180" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- 0. Header -->
|
||||
<StackPanel Grid.Row="0" Margin="0,0,0,18">
|
||||
<TextBlock Text="공유 권한 및 AD 보안그룹 연동 관리" FontSize="24" FontWeight="Bold" Foreground="{StaticResource TextPrimaryBrush}" />
|
||||
<TextBlock Text="DB에서 Share 대상자를 조회하여 선택적으로 권한을 부여/해제하고, Share 이름과 동일한 AD 보안그룹 멤버십을 1:1 동기화합니다."
|
||||
FontSize="13" Foreground="{StaticResource TextSecondaryBrush}" Margin="0,4,0,0" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- 1. Top Selector Bar -->
|
||||
<Border Grid.Row="1" Style="{StaticResource ModernCardStyle}" Margin="0,0,0,16" Padding="14">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="260" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock Grid.Column="0" Text="관리 대상 Share (공유):" Foreground="{StaticResource TextSecondaryBrush}" VerticalAlignment="Center" Margin="0,0,10,0" />
|
||||
<ComboBox Grid.Column="1" x:Name="CmbShareList" SelectedIndex="0" Height="32" Background="#0F172A" Foreground="{StaticResource TextPrimaryBrush}" Margin="0,0,16,0" />
|
||||
|
||||
<StackPanel Grid.Column="2" Orientation="Horizontal">
|
||||
<Button x:Name="BtnReloadFromDb" Content=" DB 데이터 조회" Style="{StaticResource PrimaryButtonStyle}" Margin="0,0,8,0" />
|
||||
<Button x:Name="BtnReloadADGroup" Content="AD 그룹 멤버 새로고침" Style="{StaticResource SecondaryButtonStyle}" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- Mapping Rule Indicator -->
|
||||
<StackPanel Grid.Column="3" Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center">
|
||||
<Border Background="#1E293B" CornerRadius="6" Padding="10,6" BorderBrush="{StaticResource CardBorderBrush}" BorderThickness="1">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="" FontFamily="{StaticResource IconFont}" Foreground="#60A5FA" Margin="0,0,6,0" VerticalAlignment="Center" />
|
||||
<TextBlock Text="연계 보안그룹: " Foreground="{StaticResource TextSecondaryBrush}" FontSize="12" />
|
||||
<TextBlock x:Name="TxtTargetADGroupName" Text="Finance_Share" FontWeight="Bold" Foreground="#60A5FA" FontSize="12" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- 2. Main Dual Grid & Action Center -->
|
||||
<Grid Grid.Row="2" Margin="0,0,0,16">
|
||||
<Grid.ColumnDefinitions>
|
||||
<!-- Left: DB Users Grid -->
|
||||
<ColumnDefinition Width="5*" />
|
||||
<!-- Center: Action Buttons -->
|
||||
<ColumnDefinition Width="130" />
|
||||
<!-- Right: AD Group Members Grid -->
|
||||
<ColumnDefinition Width="5*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- LEFT: DB Users Grid -->
|
||||
<Border Grid.Column="0" Style="{StaticResource ModernCardStyle}" Margin="0" Padding="14">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Title & Batch Selector -->
|
||||
<DockPanel Grid.Row="0" Margin="0,0,0,10">
|
||||
<TextBlock Text="DB 조회 사용자 목록 (권한 할당 대상)" FontSize="14" FontWeight="Bold" Foreground="{StaticResource TextPrimaryBrush}" VerticalAlignment="Center" />
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
|
||||
<Button x:Name="BtnSelectAll" Content="전체선택" Style="{StaticResource SecondaryButtonStyle}" Padding="8,3" FontSize="11" Margin="0,0,6,0" />
|
||||
<Button x:Name="BtnDeselectAll" Content="선택해제" Style="{StaticResource SecondaryButtonStyle}" Padding="8,3" FontSize="11" />
|
||||
</StackPanel>
|
||||
</DockPanel>
|
||||
|
||||
<!-- DB Users DataGrid with CheckBox -->
|
||||
<DataGrid x:Name="DbUsersGrid" Grid.Row="1" Style="{StaticResource ModernDataGridStyle}">
|
||||
<DataGrid.Columns>
|
||||
<!-- Selection Checkbox Column -->
|
||||
<DataGridCheckBoxColumn Header="선택" Binding="{Binding IsSelected, UpdateSourceTrigger=PropertyChanged}" Width="50" />
|
||||
<DataGridTextColumn Header="아이디 (sAMAccountName)" Binding="{Binding SamAccountName}" Width="150" />
|
||||
<DataGridTextColumn Header="성명" Binding="{Binding UserName}" Width="100" />
|
||||
<DataGridTextColumn Header="부서" Binding="{Binding Department}" Width="120" />
|
||||
<DataGridTextColumn Header="권한 상태" Binding="{Binding Status}" Width="110" />
|
||||
<DataGridTextColumn Header="할당 일자" Binding="{Binding AssignedDate}" Width="*" />
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
|
||||
<TextBlock Grid.Row="2" x:Name="TxtDbCount" Text="총 5명 조회됨" FontSize="11" Foreground="{StaticResource TextSecondaryBrush}" Margin="4,8,0,0" />
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- CENTER: Transfer & Action Buttons -->
|
||||
<StackPanel Grid.Column="1" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="10,0">
|
||||
<!-- Add Button -->
|
||||
<Button x:Name="BtnAddToShareGroup" Style="{StaticResource PrimaryButtonStyle}" Margin="0,0,0,12" Padding="8,12" ToolTip="선택한 사용자를 DB 등록 및 AD 보안그룹에 추가">
|
||||
<StackPanel HorizontalAlignment="Center">
|
||||
<TextBlock Text="" FontFamily="{StaticResource IconFont}" FontSize="18" HorizontalAlignment="Center" />
|
||||
<TextBlock Text="그룹 추가" FontSize="11" FontWeight="Bold" Margin="0,4,0,0" HorizontalAlignment="Center" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<!-- Remove / Revoke Button -->
|
||||
<Button x:Name="BtnRemoveFromShareGroup" Style="{StaticResource SecondaryButtonStyle}" Padding="8,12" ToolTip="선택한 사용자를 DB 해제 및 AD 보안그룹에서 제거">
|
||||
<StackPanel HorizontalAlignment="Center">
|
||||
<TextBlock Text="" FontFamily="{StaticResource IconFont}" FontSize="18" HorizontalAlignment="Center" />
|
||||
<TextBlock Text="그룹 해제" FontSize="11" FontWeight="Bold" Margin="0,4,0,0" HorizontalAlignment="Center" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
||||
<!-- RIGHT: AD Security Group Members Grid -->
|
||||
<Border Grid.Column="2" Style="{StaticResource ModernCardStyle}" Margin="0" Padding="14">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<DockPanel Grid.Row="0" Margin="0,0,0,10">
|
||||
<TextBlock Text="현재 AD 보안그룹 소속 멤버" FontSize="14" FontWeight="Bold" Foreground="{StaticResource TextPrimaryBrush}" VerticalAlignment="Center" />
|
||||
<Border Background="#10B981" CornerRadius="4" Padding="6,2" HorizontalAlignment="Right">
|
||||
<TextBlock Text="실시간 AD 동기화" FontSize="11" FontWeight="Bold" Foreground="White" />
|
||||
</Border>
|
||||
</DockPanel>
|
||||
|
||||
<DataGrid x:Name="ADGroupMembersGrid" Grid.Row="1" Style="{StaticResource ModernDataGridStyle}">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="sAMAccountName" Binding="{Binding SamAccountName}" Width="150" />
|
||||
<DataGridTextColumn Header="성명" Binding="{Binding DisplayName}" Width="100" />
|
||||
<DataGridTextColumn Header="부서" Binding="{Binding Department}" Width="130" />
|
||||
<DataGridTextColumn Header="DistinguishedName (DN)" Binding="{Binding DistinguishedName}" Width="*" />
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
|
||||
<TextBlock Grid.Row="2" x:Name="TxtAdGroupCount" Text="현재 그룹 멤버: 3명" FontSize="11" Foreground="{StaticResource TextSecondaryBrush}" Margin="4,8,0,0" />
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
|
||||
<!-- 3. Bottom Execution Log & Trace Console -->
|
||||
<Border Grid.Row="3" Style="{StaticResource ModernCardStyle}" Margin="0" Padding="12">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<DockPanel Grid.Row="0" Margin="0,0,0,6">
|
||||
<TextBlock Text="동기화 실행 로그 및 SQL/LDAP 처리 이력" FontSize="13" FontWeight="Bold" Foreground="{StaticResource TextPrimaryBrush}" />
|
||||
<Button x:Name="BtnClearLog" Content="로그 비우기" Style="{StaticResource SecondaryButtonStyle}" Padding="8,2" FontSize="11" HorizontalAlignment="Right" />
|
||||
</DockPanel>
|
||||
|
||||
<Border Grid.Row="1" Background="#090D16" CornerRadius="4" Padding="10">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<TextBox x:Name="TxtSyncLogs" Background="Transparent" Foreground="#A78BFA" BorderThickness="0"
|
||||
IsReadOnly="True" FontFamily="Consolas" FontSize="12" TextWrapping="Wrap"
|
||||
Text="[시스템 준비 완료] 대상을 선택하고 [그룹 추가] 또는 [그룹 해제]를 실행하십시오." />
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
Reference in New Issue
Block a user