}, "Configuration for the CephFS share")
}
+JOIN_AUTH_SCHEMA = {
+ "resource_type": (str, "ceph.smb.join.auth"),
+ "auth_id": (str, "Unique identifier for the join auth resource"),
+ "intent": (str, "Desired state of the resource, e.g., 'present' or 'removed'"),
+ "auth": ({
+ "username": (str, "Username for authentication"),
+ "password": (str, "Password for authentication")
+ }, "Authentication credentials"),
+ "linked_to_cluster": (str, "Optional string containing a cluster ID. \
+ If set, the resource is linked to the cluster and will be automatically removed \
+ when the cluster is removed")
+}
+
+LIST_JOIN_AUTH_SCHEMA = [JOIN_AUTH_SCHEMA]
+
+USERSGROUPS_SCHEMA = {
+ "resource_type": (str, "ceph.smb.usersgroups"),
+ "users_groups_id": (str, "A short string identifying the usersgroups resource"),
+ "intent": (str, "Desired state of the resource, e.g., 'present' or 'removed'"),
+ "values": ({
+ "users": ([{
+ "name": (str, "The user name"),
+ "password": (str, "The password for the user")
+ }], "List of user objects, each containing a name and password"),
+ "groups": ([{
+ "name": (str, "The name of the group")
+ }], "List of group objects, each containing a name")
+ }, "Required object containing users and groups information"),
+ "linked_to_cluster": (str, "Optional string containing a cluster ID. \
+ If set, the resource is linked to the cluster and will be automatically removed \
+ when the cluster is removed")
+}
+
+LIST_USERSGROUPS_SCHEMA = [USERSGROUPS_SCHEMA]
+
def raise_on_failure(func):
@wraps(func)
return mgr.remote('smb', 'apply_resources', json.dumps(resource)).one().to_simplified()
+@APIRouter('/smb/joinauth', Scope.SMB)
+@APIDoc("SMB Join Auth API", "SMB")
+class SMBJoinAuth(RESTController):
+ _resource: str = 'ceph.smb.join.auth'
+
+ @ReadPermission
+ @EndpointDoc("List smb join authorization resources",
+ responses={200: LIST_JOIN_AUTH_SCHEMA})
+ def list(self, join_auth: str = '') -> List[Share]:
+ """
+ List all smb join auth resources
+
+ :return: Returns list of join auth.
+ :rtype: List[Dict]
+ """
+ res = mgr.remote(
+ 'smb',
+ 'show',
+ [f'{self._resource}.{join_auth}' if join_auth else self._resource])
+ return res['resources'] if 'resources' in res else [res]
+
+
+@APIRouter('/smb/usersgroups', Scope.SMB)
+@APIDoc("SMB Users Groups API", "SMB")
+class SMBUsersgroups(RESTController):
+ _resource: str = 'ceph.smb.usersgroups'
+
+ @ReadPermission
+ @EndpointDoc("List smb user resources",
+ responses={200: LIST_USERSGROUPS_SCHEMA})
+ def list(self, users_groups: str = '') -> List[Share]:
+ """
+ List all smb usersgroups resources
+
+ :return: Returns list of usersgroups.
+ :rtype: List[Dict]
+ """
+ res = mgr.remote(
+ 'smb',
+ 'show',
+ [f'{self._resource}.{users_groups}' if users_groups else self._resource])
+ return res['resources'] if 'resources' in res else [res]
+
+
@UIRouter('/smb')
class SMBStatus(RESTController):
@EndpointDoc("Get SMB feature status")
import { MultiClusterComponent } from './ceph/cluster/multi-cluster/multi-cluster.component';
import { MultiClusterListComponent } from './ceph/cluster/multi-cluster/multi-cluster-list/multi-cluster-list.component';
import { MultiClusterDetailsComponent } from './ceph/cluster/multi-cluster/multi-cluster-details/multi-cluster-details.component';
-import { SmbClusterListComponent } from './ceph/smb/smb-cluster-list/smb-cluster-list.component';
import { SmbClusterFormComponent } from './ceph/smb/smb-cluster-form/smb-cluster-form.component';
+import { SmbTabsComponent } from './ceph/smb/smb-tabs/smb-tabs.component';
@Injectable()
export class PerformanceCounterBreadcrumbsResolver extends BreadcrumbsResolver {
breadcrumbs: 'File/SMB'
},
children: [
- { path: '', component: SmbClusterListComponent },
+ { path: '', component: SmbTabsComponent },
{
path: `${URLVerbs.CREATE}`,
component: SmbClusterFormComponent,
CLUSTERING,
PLACEMENT,
RequestModel,
- RESOURCE_TYPE,
+ CLUSTER_RESOURCE,
RESOURCE,
DomainSettings,
JoinSource
const requestModel: RequestModel = {
cluster_resource: {
- resource_type: RESOURCE_TYPE,
+ resource_type: CLUSTER_RESOURCE,
cluster_id: rawFormValue.cluster_id,
auth_mode: rawFormValue.auth_mode
}
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { SmbClusterTabsComponent } from './smb-cluster-tabs.component';
-import { RESOURCE_TYPE, SMBCluster } from '../smb.model';
+import { CLUSTER_RESOURCE, SMBCluster } from '../smb.model';
import { By } from '@angular/platform-browser';
describe('SmbClusterTabsComponent', () => {
const selectedSmbCluster = (clusterId: string) => {
const smbCluster: SMBCluster = {
- resource_type: RESOURCE_TYPE,
+ resource_type: CLUSTER_RESOURCE,
cluster_id: clusterId,
auth_mode: 'user'
};
--- /dev/null
+<cd-table
+ [data]="joinAuth$ | async"
+ columnMode="flex"
+ [columns]="columns"
+ selectionType="single"
+ [hasDetails]="false"
+ (fetchData)="loadJoinAuth()"
+>
+</cd-table>
--- /dev/null
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { SmbJoinAuthListComponent } from './smb-join-auth-list.component';
+import { HttpClientTestingModule } from '@angular/common/http/testing';
+import { RouterTestingModule } from '@angular/router/testing';
+import { ToastrModule } from 'ngx-toastr';
+import { SharedModule } from '~/app/shared/shared.module';
+
+describe('SmbJoinAuthListComponent', () => {
+ let component: SmbJoinAuthListComponent;
+ let fixture: ComponentFixture<SmbJoinAuthListComponent>;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [SmbJoinAuthListComponent],
+ imports: [SharedModule, HttpClientTestingModule, ToastrModule.forRoot(), RouterTestingModule]
+ }).compileComponents();
+
+ fixture = TestBed.createComponent(SmbJoinAuthListComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
--- /dev/null
+import { Component, OnInit } from '@angular/core';
+import { Observable, BehaviorSubject, of } from 'rxjs';
+import { switchMap, catchError } from 'rxjs/operators';
+import { SmbService } from '~/app/shared/api/smb.service';
+import { ActionLabelsI18n } from '~/app/shared/constants/app.constants';
+import { CdTableAction } from '~/app/shared/models/cd-table-action';
+import { CdTableColumn } from '~/app/shared/models/cd-table-column';
+import { CdTableFetchDataContext } from '~/app/shared/models/cd-table-fetch-data-context';
+import { Permission } from '~/app/shared/models/permissions';
+import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
+import { SMBJoinAuth } from '../smb.model';
+
+@Component({
+ selector: 'cd-smb-join-auth-list',
+ templateUrl: './smb-join-auth-list.component.html',
+ styleUrls: ['./smb-join-auth-list.component.scss']
+})
+export class SmbJoinAuthListComponent implements OnInit {
+ columns: CdTableColumn[];
+ permission: Permission;
+ tableActions: CdTableAction[];
+ context: CdTableFetchDataContext;
+
+ joinAuth$: Observable<SMBJoinAuth[]>;
+ subject$ = new BehaviorSubject<SMBJoinAuth[]>([]);
+
+ constructor(
+ private authStorageService: AuthStorageService,
+ public actionLabels: ActionLabelsI18n,
+ private smbService: SmbService
+ ) {
+ this.permission = this.authStorageService.getPermissions().smb;
+ }
+
+ ngOnInit() {
+ this.columns = [
+ {
+ name: $localize`ID`,
+ prop: 'auth_id',
+ flexGrow: 2
+ },
+ {
+ name: $localize`Username`,
+ prop: 'auth.username',
+ flexGrow: 2
+ },
+ {
+ name: $localize`Linked to Cluster`,
+ prop: 'linked_to_cluster',
+ flexGrow: 2
+ }
+ ];
+
+ this.joinAuth$ = this.subject$.pipe(
+ switchMap(() =>
+ this.smbService.listJoinAuths().pipe(
+ catchError(() => {
+ this.context.error();
+ return of(null);
+ })
+ )
+ )
+ );
+ }
+
+ loadJoinAuth() {
+ this.subject$.next([]);
+ }
+}
--- /dev/null
+<legend
+ *ngIf="selectedTab == Tabs.clusters"
+ i18n
+>
+ Clusters
+ <cd-help-text>
+ Logical management units that may map to one or more managed Samba service
+ </cd-help-text>
+</legend>
+
+<legend
+ *ngIf="selectedTab == Tabs.joinAuths"
+ i18n
+>
+ Active directory access resources
+ <cd-help-text>
+ Logical management units for authorization on Active Directory servers
+ </cd-help-text>
+</legend>
+
+<legend
+ *ngIf="selectedTab == Tabs.usersgroups"
+ i18n
+>
+ Standalone access resources
+ <cd-help-text>
+ Logical management units for authorization on Standalone servers
+ </cd-help-text>
+</legend>
+
+<cds-tabs
+ type="contained"
+ followFocus="true"
+ isNavigation="true"
+ [cacheActive]="false">
+ <cds-tab
+ heading="Clusters"
+ [tabContent]="clusters"
+ i18n-heading
+ (selected)="onSelected(Tabs.clusters)">
+ </cds-tab>
+ <cds-tab
+ heading="Active Directory"
+ [tabContent]="joinAuth"
+ i18n-heading
+ (selected)="onSelected(Tabs.joinAuths)">
+ </cds-tab>
+ <cds-tab
+ heading="Standalone"
+ [tabContent]="usersgroups"
+ i18n-heading
+ (selected)="onSelected(Tabs.usersgroups)">
+ </cds-tab>
+</cds-tabs>
+
+<ng-template #clusters>
+ <cd-smb-cluster-list></cd-smb-cluster-list>
+</ng-template>
+
+<ng-template #joinAuth>
+ <cd-smb-join-auth-list></cd-smb-join-auth-list>
+</ng-template>
+
+<ng-template #usersgroups>
+ <cd-smb-users-list></cd-smb-users-list>
+</ng-template>
--- /dev/null
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { SmbTabsComponent } from './smb-tabs.component';
+import { By } from '@angular/platform-browser';
+
+describe('SmbTabsComponent', () => {
+ let component: SmbTabsComponent;
+ let fixture: ComponentFixture<SmbTabsComponent>;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [SmbTabsComponent]
+ }).compileComponents();
+
+ fixture = TestBed.createComponent(SmbTabsComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+
+ it('should display the heading text in the tab', () => {
+ const tabs = fixture.debugElement.queryAll(By.css('cds-tab'));
+ expect(tabs.length).toBe(3);
+ expect(tabs[0].attributes['heading']).toBe('Clusters');
+ expect(tabs[1].attributes['heading']).toBe('Active Directory');
+ expect(tabs[2].attributes['heading']).toBe('Standalone');
+ });
+
+ // If the tabs cacheActive is set to true data for all tabs will be fetched at once,
+ // smb mgr module might hit recursive error when doing multiple request to the db
+ it('should have cache disabled', () => {
+ const tabs = fixture.nativeElement.querySelector('cds-tabs');
+ expect(tabs.cacheActive).toBeFalsy();
+ });
+});
--- /dev/null
+import { Component } from '@angular/core';
+
+enum TABS {
+ 'clusters',
+ 'joinAuths',
+ 'usersgroups'
+}
+
+@Component({
+ selector: 'cd-smb-tabs',
+ templateUrl: './smb-tabs.component.html',
+ styleUrls: ['./smb-tabs.component.scss']
+})
+export class SmbTabsComponent {
+ selectedTab: TABS;
+
+ onSelected(tab: TABS) {
+ this.selectedTab = tab;
+ }
+
+ public get Tabs(): typeof TABS {
+ return TABS;
+ }
+}
--- /dev/null
+<cds-tabs
+ type="contained"
+ followFocus="true"
+ isNavigation="true"
+ [cacheActive]="false"
+>
+ <cds-tab
+ heading="Users"
+ i18n-heading>
+ <cd-table
+ [data]="selection?.values.users"
+ columnMode="flex"
+ [columns]="columns"
+ selectionType="single"
+ [hasDetails]="false"
+ >
+ </cd-table>
+ </cds-tab>
+</cds-tabs>
--- /dev/null
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { SmbUsersgroupsDetailsComponent } from './smb-usersgroups-details.component';
+
+describe('SmbUsersgroupsDetailsComponent', () => {
+ let component: SmbUsersgroupsDetailsComponent;
+ let fixture: ComponentFixture<SmbUsersgroupsDetailsComponent>;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [SmbUsersgroupsDetailsComponent]
+ }).compileComponents();
+
+ fixture = TestBed.createComponent(SmbUsersgroupsDetailsComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
--- /dev/null
+import { Component, Input, OnInit } from '@angular/core';
+import { CdTableColumn } from '~/app/shared/models/cd-table-column';
+import { SMBUsersGroups } from '../smb.model';
+
+@Component({
+ selector: 'cd-smb-usersgroups-details',
+ templateUrl: './smb-usersgroups-details.component.html',
+ styleUrls: ['./smb-usersgroups-details.component.scss']
+})
+export class SmbUsersgroupsDetailsComponent implements OnInit {
+ @Input()
+ selection: SMBUsersGroups;
+ columns: CdTableColumn[];
+
+ ngOnInit() {
+ this.columns = [
+ {
+ name: $localize`Username`,
+ prop: 'name',
+ flexGrow: 2
+ }
+ ];
+ }
+}
--- /dev/null
+<ng-container *ngIf="usersGroups$ | async as usersGroups">
+ <cd-table
+ [data]="usersGroups"
+ columnMode="flex"
+ [columns]="columns"
+ selectionType="single"
+ [hasDetails]="true"
+ (setExpandedRow)="setExpandedRow($event)"
+ (fetchData)="loadUsersGroups()"
+>
+ <cd-smb-usersgroups-details
+ *cdTableDetail
+ [selection]="expandedRow"
+ ></cd-smb-usersgroups-details>
+</cd-table>
+</ng-container>
+
+<ng-template
+ #groupsNamesTpl
+ let-row="data.row"
+>
+ <cds-tag
+ *ngFor="let group of row?.values.groups"
+ size="md"
+ >
+ {{ group.name }}
+ </cds-tag>
+</ng-template>
--- /dev/null
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { SmbUsersgroupsListComponent } from './smb-usersgroups-list.component';
+import { HttpClientTestingModule } from '@angular/common/http/testing';
+import { RouterTestingModule } from '@angular/router/testing';
+import { ToastrModule } from 'ngx-toastr';
+import { SharedModule } from '~/app/shared/shared.module';
+
+describe('SmbUsersgroupsListComponent', () => {
+ let component: SmbUsersgroupsListComponent;
+ let fixture: ComponentFixture<SmbUsersgroupsListComponent>;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [SmbUsersgroupsListComponent],
+ imports: [SharedModule, HttpClientTestingModule, ToastrModule.forRoot(), RouterTestingModule]
+ }).compileComponents();
+
+ fixture = TestBed.createComponent(SmbUsersgroupsListComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
--- /dev/null
+import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';
+import { catchError, switchMap } from 'rxjs/operators';
+import { BehaviorSubject, Observable, of } from 'rxjs';
+
+import _ from 'lodash';
+
+import { ActionLabelsI18n } from '~/app/shared/constants/app.constants';
+import { CdTableAction } from '~/app/shared/models/cd-table-action';
+import { CdTableColumn } from '~/app/shared/models/cd-table-column';
+import { CdTableFetchDataContext } from '~/app/shared/models/cd-table-fetch-data-context';
+import { ListWithDetails } from '~/app/shared/classes/list-with-details.class';
+import { Permission } from '~/app/shared/models/permissions';
+
+import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
+import { SmbService } from '~/app/shared/api/smb.service';
+import { SMBUsersGroups } from '../smb.model';
+
+@Component({
+ selector: 'cd-smb-users-list',
+ templateUrl: './smb-usersgroups-list.component.html',
+ styleUrls: ['./smb-usersgroups-list.component.scss']
+})
+export class SmbUsersgroupsListComponent extends ListWithDetails implements OnInit {
+ @ViewChild('groupsNamesTpl', { static: true })
+ groupsNamesTpl: TemplateRef<any>;
+ columns: CdTableColumn[];
+ permission: Permission;
+ tableActions: CdTableAction[];
+ context: CdTableFetchDataContext;
+
+ usersGroups$: Observable<SMBUsersGroups[]>;
+ subject$ = new BehaviorSubject<SMBUsersGroups[]>([]);
+
+ constructor(
+ private authStorageService: AuthStorageService,
+ public actionLabels: ActionLabelsI18n,
+ private smbService: SmbService
+ ) {
+ super();
+ this.permission = this.authStorageService.getPermissions().smb;
+ }
+
+ ngOnInit() {
+ this.columns = [
+ {
+ name: $localize`ID`,
+ prop: 'users_groups_id',
+ flexGrow: 2
+ },
+ {
+ name: $localize`Number of users`,
+ prop: 'values.users.length',
+ flexGrow: 2
+ },
+ {
+ name: $localize`Groups`,
+ prop: 'values.groups',
+ cellTemplate: this.groupsNamesTpl,
+ flexGrow: 2
+ },
+ {
+ name: $localize`Linked to`,
+ prop: 'values.linked_to_cluster',
+ flexGrow: 2
+ }
+ ];
+
+ this.usersGroups$ = this.subject$.pipe(
+ switchMap(() =>
+ this.smbService.listUsersGroups().pipe(
+ catchError(() => {
+ this.context.error();
+ return of(null);
+ })
+ )
+ )
+ );
+ }
+
+ loadUsersGroups() {
+ this.subject$.next([]);
+ }
+}
label: 'label'
};
-export const RESOURCE_TYPE = 'ceph.smb.cluster';
-
export interface SMBShare {
cluster_id: string;
share_id: string;
access: 'read' | 'read-write' | 'none' | 'admin';
category?: 'user' | 'group';
}
+
+export interface SMBJoinAuth {
+ resource_type: string;
+ auth_id: string;
+ intent: Intent;
+ auth: Auth;
+ linked_to_cluster?: string;
+}
+
+export interface SMBUsersGroups {
+ resource_type: string;
+ users_groups_id: string;
+ intent: Intent;
+ values: Value;
+ linked_to_cluster?: string;
+}
+
+interface Auth {
+ username: string;
+ password: string;
+}
+
+interface User {
+ name: string;
+ password: string;
+}
+
+interface Group {
+ name: string;
+}
+
+interface Value {
+ users: User[];
+ groups: Group[];
+}
+
+type Intent = 'present' | 'removed';
+
+export const CLUSTER_RESOURCE = 'ceph.smb.cluster';
import { SmbClusterListComponent } from './smb-cluster-list/smb-cluster-list.component';
import { SmbClusterFormComponent } from './smb-cluster-form/smb-cluster-form.component';
import { AppRoutingModule } from '~/app/app-routing.module';
-import { provideCharts, withDefaultRegisterables, BaseChartDirective } from 'ng2-charts';
import { DataTableModule } from '~/app/shared/datatable/datatable.module';
import { SmbDomainSettingModalComponent } from './smb-domain-setting-modal/smb-domain-setting-modal.component';
import { SmbClusterTabsComponent } from './smb-cluster-tabs/smb-cluster-tabs.component';
NumberModule,
PlaceholderModule,
SelectModule,
- TabsModule
+ TabsModule,
+ TagModule
} from 'carbon-components-angular';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';
import { NgModule } from '@angular/core';
+import { SmbUsersgroupsListComponent } from './smb-usersgroups-list/smb-usersgroups-list.component';
+import { SmbTabsComponent } from './smb-tabs/smb-tabs.component';
+import { SmbJoinAuthListComponent } from './smb-join-auth-list/smb-join-auth-list.component';
+import { SmbUsersgroupsDetailsComponent } from './smb-usersgroups-details/smb-usersgroups-details.component';
+
@NgModule({
imports: [
ReactiveFormsModule,
CommonModule,
SharedModule,
AppRoutingModule,
- BaseChartDirective,
CommonModule,
FormsModule,
ReactiveFormsModule,
GridModule,
SelectModule,
TabsModule,
+ TagModule,
InputModule,
CheckboxModule,
SelectModule,
SmbClusterFormComponent,
SmbDomainSettingModalComponent,
SmbClusterTabsComponent,
- SmbShareListComponent
- ],
- providers: [provideCharts(withDefaultRegisterables())]
+ SmbShareListComponent,
+ SmbUsersgroupsListComponent,
+ SmbUsersgroupsDetailsComponent,
+ SmbTabsComponent,
+ SmbJoinAuthListComponent
+ ]
})
export class SmbModule {
constructor(private iconService: IconService) {
import { TestBed } from '@angular/core/testing';
-import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
+import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing';
import { SmbService } from './smb.service';
import { configureTestBed } from '~/testing/unit-test-helper';
+import { provideHttpClient } from '@angular/common/http';
describe('SmbService', () => {
let service: SmbService;
let httpTesting: HttpTestingController;
configureTestBed({
- providers: [SmbService],
- imports: [HttpClientTestingModule]
+ providers: [SmbService, provideHttpClient(), provideHttpClientTesting()]
});
beforeEach(() => {
const req = httpTesting.expectOne('api/smb/share?cluster_id=tango');
expect(req.request.method).toBe('GET');
});
+
+ it('should call list join auth', () => {
+ service.listJoinAuths().subscribe();
+ const req = httpTesting.expectOne('api/smb/joinauth');
+ expect(req.request.method).toBe('GET');
+ });
+
+ it('should call list usersgroups', () => {
+ service.listUsersGroups().subscribe();
+ const req = httpTesting.expectOne('api/smb/usersgroups');
+ expect(req.request.method).toBe('GET');
+ });
});
import { Injectable } from '@angular/core';
import { Observable, Subject } from 'rxjs';
-import { DomainSettings, SMBCluster, SMBShare } from '~/app/ceph/smb/smb.model';
+import {
+ DomainSettings,
+ SMBCluster,
+ SMBJoinAuth,
+ SMBShare,
+ SMBUsersGroups
+} from '~/app/ceph/smb/smb.model';
@Injectable({
providedIn: 'root'
listShares(clusterId: string): Observable<SMBShare[]> {
return this.http.get<SMBShare[]>(`${this.baseURL}/share?cluster_id=${clusterId}`);
}
+
+ listJoinAuths(): Observable<SMBJoinAuth[]> {
+ return this.http.get<SMBJoinAuth[]>(`${this.baseURL}/joinauth`);
+ }
+
+ listUsersGroups(): Observable<SMBUsersGroups[]> {
+ return this.http.get<SMBUsersGroups[]>(`${this.baseURL}/usersgroups`);
+ }
}
summary: Get an smb cluster
tags:
- SMB
+ /api/smb/joinauth:
+ get:
+ description: "\n List all smb join auth resources\n\n :return:\
+ \ Returns list of join auth.\n :rtype: List[Dict]\n "
+ parameters:
+ - default: ''
+ in: query
+ name: join_auth
+ schema:
+ type: string
+ responses:
+ '200':
+ content:
+ application/vnd.ceph.api.v1.0+json:
+ schema:
+ items:
+ properties:
+ auth:
+ description: Authentication credentials
+ properties:
+ password:
+ description: Password for authentication
+ type: string
+ username:
+ description: Username for authentication
+ type: string
+ required:
+ - username
+ - password
+ type: object
+ auth_id:
+ description: Unique identifier for the join auth resource
+ type: string
+ intent:
+ description: Desired state of the resource, e.g., 'present'
+ or 'removed'
+ type: string
+ linked_to_cluster:
+ description: Optional string containing a cluster ID. If
+ set, the resource is linked to the cluster and will be automatically
+ removed when the cluster is removed
+ type: string
+ resource_type:
+ description: ceph.smb.join.auth
+ type: string
+ type: object
+ required:
+ - resource_type
+ - auth_id
+ - intent
+ - auth
+ - linked_to_cluster
+ type: array
+ description: OK
+ '400':
+ description: Operation exception. Please check the response body for details.
+ '401':
+ description: Unauthenticated access. Please login first.
+ '403':
+ description: Unauthorized access. Please check your permissions.
+ '500':
+ description: Unexpected error. Please check the response body for the stack
+ trace.
+ security:
+ - jwt: []
+ summary: List smb join authorization resources
+ tags:
+ - SMB
/api/smb/share:
get:
description: "\n List all smb shares or all shares for a given cluster\n\
summary: Remove an smb share
tags:
- SMB
+ /api/smb/usersgroups:
+ get:
+ description: "\n List all smb usersgroups resources\n\n :return:\
+ \ Returns list of usersgroups.\n :rtype: List[Dict]\n "
+ parameters:
+ - default: ''
+ in: query
+ name: users_groups
+ schema:
+ type: string
+ responses:
+ '200':
+ content:
+ application/vnd.ceph.api.v1.0+json:
+ schema:
+ items:
+ properties:
+ intent:
+ description: Desired state of the resource, e.g., 'present'
+ or 'removed'
+ type: string
+ linked_to_cluster:
+ description: Optional string containing a cluster ID. If
+ set, the resource is linked to the cluster and will be automatically
+ removed when the cluster is removed
+ type: string
+ resource_type:
+ description: ceph.smb.usersgroups
+ type: string
+ users_groups_id:
+ description: A short string identifying the usersgroups resource
+ type: string
+ values:
+ description: Required object containing users and groups information
+ properties:
+ groups:
+ description: List of group objects, each containing a name
+ items:
+ properties:
+ name:
+ description: The name of the group
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ users:
+ description: List of user objects, each containing a name
+ and password
+ items:
+ properties:
+ name:
+ description: The user name
+ type: string
+ password:
+ description: The password for the user
+ type: string
+ required:
+ - name
+ - password
+ type: object
+ type: array
+ required:
+ - users
+ - groups
+ type: object
+ type: object
+ required:
+ - resource_type
+ - users_groups_id
+ - intent
+ - values
+ - linked_to_cluster
+ type: array
+ description: OK
+ '400':
+ description: Operation exception. Please check the response body for details.
+ '401':
+ description: Unauthenticated access. Please login first.
+ '403':
+ description: Unauthorized access. Please check your permissions.
+ '500':
+ description: Unexpected error. Please check the response body for the stack
+ trace.
+ security:
+ - jwt: []
+ summary: List smb user resources
+ tags:
+ - SMB
/api/summary:
get:
parameters: []
import json
from unittest.mock import Mock
-from dashboard.controllers.smb import SMBCluster, SMBShare
+from dashboard.controllers.smb import SMBCluster, SMBJoinAuth, SMBShare, SMBUsersgroups
from .. import mgr
from ..tests import ControllerTestCase
self._delete(f'{self._endpoint}/smbCluster1/share1')
self.assertStatus(204)
mgr.remote.assert_called_once_with('smb', 'apply_resources', json.dumps(_res_simplified))
+
+
+class SMBJoinAuthTest(ControllerTestCase):
+ _endpoint = '/api/smb/joinauth'
+
+ _join_auths = {
+ "resources": [
+ {
+ "resource_type": "ceph.smb.join.auth",
+ "auth_id": "join1-admin",
+ "intent": "present",
+ "auth": {
+ "username": "Administrator",
+ "password": "Passw0rd"
+ }
+ },
+ {
+ "resource_type": "ceph.smb.join.auth",
+ "auth_id": "ja2",
+ "intent": "present",
+ "auth": {
+ "username": "user123",
+ "password": "foobar"
+ }
+ }
+ ]
+ }
+
+ @classmethod
+ def setup_server(cls):
+ cls.setup_controllers([SMBJoinAuth])
+
+ def test_list_one_join_auth(self):
+ mgr.remote = Mock(return_value=self._join_auths['resources'][0])
+
+ self._get(self._endpoint)
+ self.assertStatus(200)
+ self.assertJsonBody([self._join_auths['resources'][0]])
+
+ def test_list_multiple_clusters(self):
+ mgr.remote = Mock(return_value=self._join_auths)
+
+ self._get(self._endpoint)
+ self.assertStatus(200)
+ self.assertJsonBody(self._join_auths['resources'])
+
+
+class SMBUsersgroupsTest(ControllerTestCase):
+ _endpoint = '/api/smb/usersgroups'
+
+ _usersgroups = {
+ "resources": [
+ {
+ "resource_type": "ceph.smb.usersgroups",
+ "users_groups_id": "u2",
+ "intent": "present",
+ "values": {
+ "users": [
+ {
+ "name": "user3",
+ "password": "pass"
+ }
+ ],
+ "groups": []
+ }
+ },
+ {
+ "resource_type": "ceph.smb.usersgroups",
+ "users_groups_id": "u1",
+ "intent": "present",
+ "values": {
+ "users": [
+ {
+ "name": "user2",
+ "password": "pass"
+ }
+ ],
+ "groups": []
+ }
+ }
+ ]
+ }
+
+ @classmethod
+ def setup_server(cls):
+ cls.setup_controllers([SMBUsersgroups])
+
+ def test_list_one_usersgroups(self):
+ mgr.remote = Mock(return_value=self._usersgroups['resources'][0])
+
+ self._get(self._endpoint)
+ self.assertStatus(200)
+ self.assertJsonBody([self._usersgroups['resources'][0]])
+
+ def test_list_multiple_usersgroups(self):
+ mgr.remote = Mock(return_value=self._usersgroups)
+
+ self._get(self._endpoint)
+ self.assertStatus(200)
+ self.assertJsonBody(self._usersgroups['resources'])