or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

application-management.mdartifact-management.mdconfiguration.mddata-operations.mddataset-operations.mdindex.mdmetrics-monitoring.mdprogram-control.mdschedule-management.mdsecurity-administration.mdservice-management.md

security-administration.mddocs/

0

# Security and Administration

1

2

Security and administrative operations including namespace management, secure key storage, metadata management, authorization, and various utility functions for managing CDAP resources.

3

4

## NamespaceClient

5

6

```java { .api }

7

public class NamespaceClient {

8

// Constructors

9

public NamespaceClient(ClientConfig config);

10

public NamespaceClient(ClientConfig config, RESTClient restClient);

11

12

// Inherits namespace CRUD operations from AbstractNamespaceClient

13

// Common operations include:

14

// - create(NamespaceMeta namespaceMeta)

15

// - get(NamespaceId namespaceId)

16

// - list()

17

// - delete(NamespaceId namespaceId)

18

// - updateProperties(NamespaceId namespaceId, Map<String, String> properties)

19

}

20

```

21

22

## SecureStoreClient

23

24

```java { .api }

25

public class SecureStoreClient {

26

// Constructors

27

public SecureStoreClient(ClientConfig config);

28

public SecureStoreClient(ClientConfig config, RESTClient restClient);

29

30

// Secure key management methods

31

public void createKey(SecureKeyId secureKeyId, SecureKeyCreateRequest keyCreateRequest);

32

public String getData(SecureKeyId secureKeyId);

33

public SecureStoreMetadata getKeyMetadata(SecureKeyId secureKeyId);

34

public void deleteKey(SecureKeyId secureKeyId);

35

public List<SecureStoreMetadata> listKeys(NamespaceId namespaceId);

36

}

37

```

38

39

## MetadataClient

40

41

```java { .api }

42

public class MetadataClient {

43

// Constructors

44

public MetadataClient(ClientConfig config);

45

public MetadataClient(ClientConfig config, RESTClient restClient);

46

47

// Inherits metadata CRUD and search operations from AbstractMetadataClient

48

// Common operations include:

49

// - addTags(MetadataEntity entity, Set<String> tags)

50

// - getTags(MetadataEntity entity)

51

// - addProperties(MetadataEntity entity, Map<String, String> properties)

52

// - getProperties(MetadataEntity entity)

53

// - removeMetadata(MetadataEntity entity)

54

// - searchMetadata(NamespaceId namespace, String query)

55

}

56

```

57

58

## AuthorizationClient

59

60

```java { .api }

61

public class AuthorizationClient {

62

// Constructors

63

public AuthorizationClient(ClientConfig config);

64

public AuthorizationClient(ClientConfig config, RESTClient restClient);

65

66

// Authorization operations (specific methods depend on implementation)

67

// Common operations include:

68

// - grant permissions

69

// - revoke permissions

70

// - list permissions

71

// - check access

72

}

73

```

74

75

## Security Types and Configuration

76

77

```java { .api }

78

public class SecureKeyId {

79

public static SecureKeyId of(NamespaceId namespace, String name);

80

public NamespaceId getNamespace();

81

public String getName();

82

}

83

84

public class SecureKeyCreateRequest {

85

public SecureKeyCreateRequest(String description, String data, Map<String, String> properties);

86

public String getDescription();

87

public String getData();

88

public Map<String, String> getProperties();

89

}

90

91

public class SecureStoreMetadata {

92

public String getName();

93

public String getDescription();

94

public long getCreationTimeMs();

95

public Map<String, String> getProperties();

96

}

97

98

public class NamespaceMeta {

99

public NamespaceMeta(String name, String description, Map<String, String> config);

100

public String getName();

101

public String getDescription();

102

public Map<String, String> getConfig();

103

}

104

```

105

106

## Namespace Management

107

108

### Namespace Operations

109

110

```java

111

// List all namespaces

112

List<NamespaceMeta> namespaces = namespaceClient.list();

113

System.out.println("Available namespaces (" + namespaces.size() + "):");

114

115

for (NamespaceMeta namespace : namespaces) {

116

System.out.println("- " + namespace.getName());

117

System.out.println(" Description: " + namespace.getDescription());

118

System.out.println(" Config: " + namespace.getConfig());

119

}

120

121

// Get specific namespace

122

NamespaceId namespaceId = NamespaceId.of("production");

123

try {

124

NamespaceMeta namespaceMeta = namespaceClient.get(namespaceId);

125

System.out.println("Namespace: " + namespaceMeta.getName());

126

System.out.println("Description: " + namespaceMeta.getDescription());

127

System.out.println("Configuration: " + namespaceMeta.getConfig());

128

} catch (NamespaceNotFoundException e) {

129

System.err.println("Namespace not found: " + namespaceId.getId());

130

}

131

```

132

133

### Namespace Creation

134

135

```java

136

// Create development namespace

137

Map<String, String> devConfig = Map.of(

138

"scheduler.queue.name", "dev-queue",

139

"explore.as.principal", "dev-user",

140

"security.impersonation.principal", "dev-service-account",

141

"data.retention.days", "7"

142

);

143

144

NamespaceMeta devNamespace = new NamespaceMeta(

145

"development",

146

"Development environment for testing applications",

147

devConfig

148

);

149

150

try {

151

namespaceClient.create(devNamespace);

152

System.out.println("Created development namespace");

153

} catch (NamespaceAlreadyExistsException e) {

154

System.out.println("Development namespace already exists");

155

}

156

157

// Create production namespace with strict configuration

158

Map<String, String> prodConfig = Map.of(

159

"scheduler.queue.name", "production-queue",

160

"security.authorization.enabled", "true",

161

"security.authentication.required", "true",

162

"data.retention.days", "365",

163

"backup.enabled", "true",

164

"monitoring.enabled", "true"

165

);

166

167

NamespaceMeta prodNamespace = new NamespaceMeta(

168

"production",

169

"Production environment with enhanced security",

170

prodConfig

171

);

172

173

namespaceClient.create(prodNamespace);

174

System.out.println("Created production namespace");

175

```

176

177

### Namespace Configuration Updates

178

179

```java

180

// Update namespace properties

181

Map<String, String> updatedProperties = Map.of(

182

"data.retention.days", "14", // Extended retention

183

"backup.frequency", "daily",

184

"monitoring.alerts.enabled", "true"

185

);

186

187

namespaceClient.updateProperties(namespaceId, updatedProperties);

188

System.out.println("Updated namespace properties");

189

190

// Get updated configuration

191

NamespaceMeta updated = namespaceClient.get(namespaceId);

192

System.out.println("Updated config: " + updated.getConfig());

193

```

194

195

## Secure Key Management

196

197

### Creating Secure Keys

198

199

```java

200

// Create secure key for database password

201

SecureKeyId dbPasswordKey = SecureKeyId.of(namespaceId, "db-password");

202

Map<String, String> keyProperties = Map.of(

203

"type", "database-credential",

204

"database", "user-profiles",

205

"environment", "production"

206

);

207

208

SecureKeyCreateRequest passwordRequest = new SecureKeyCreateRequest(

209

"Database password for user profiles service",

210

"super-secure-password-123", // In practice, this should come from secure input

211

keyProperties

212

);

213

214

secureStoreClient.createKey(dbPasswordKey, passwordRequest);

215

System.out.println("Created secure key: " + dbPasswordKey.getName());

216

217

// Create API key

218

SecureKeyId apiKey = SecureKeyId.of(namespaceId, "external-api-key");

219

SecureKeyCreateRequest apiRequest = new SecureKeyCreateRequest(

220

"API key for external service integration",

221

"api-key-xyz789",

222

Map.of(

223

"type", "api-credential",

224

"service", "external-analytics",

225

"expires", "2024-12-31"

226

)

227

);

228

229

secureStoreClient.createKey(apiKey, apiRequest);

230

231

// Create encryption key

232

SecureKeyId encryptionKey = SecureKeyId.of(namespaceId, "data-encryption-key");

233

SecureKeyCreateRequest encryptionRequest = new SecureKeyCreateRequest(

234

"AES encryption key for sensitive data",

235

generateSecureKey(), // Generate cryptographically secure key

236

Map.of(

237

"type", "encryption-key",

238

"algorithm", "AES-256",

239

"purpose", "data-encryption"

240

)

241

);

242

243

secureStoreClient.createKey(encryptionKey, encryptionRequest);

244

```

245

246

### Retrieving and Managing Secure Keys

247

248

```java

249

// List all secure keys in namespace

250

List<SecureStoreMetadata> keys = secureStoreClient.listKeys(namespaceId);

251

System.out.println("Secure keys in namespace " + namespaceId.getId() + ":");

252

253

for (SecureStoreMetadata keyMeta : keys) {

254

System.out.println("- " + keyMeta.getName());

255

System.out.println(" Description: " + keyMeta.getDescription());

256

System.out.println(" Created: " + new Date(keyMeta.getCreationTimeMs()));

257

System.out.println(" Properties: " + keyMeta.getProperties());

258

}

259

260

// Get key metadata (without revealing the actual key)

261

SecureStoreMetadata metadata = secureStoreClient.getKeyMetadata(dbPasswordKey);

262

System.out.println("Key metadata: " + metadata.getName());

263

System.out.println("Description: " + metadata.getDescription());

264

265

// Retrieve key data (use with caution!)

266

try {

267

String keyData = secureStoreClient.getData(dbPasswordKey);

268

// Use the key data immediately and don't store it

269

connectToDatabase(keyData);

270

// Clear the key from memory

271

keyData = null;

272

} catch (UnauthorizedException e) {

273

System.err.println("Not authorized to access key: " + dbPasswordKey.getName());

274

}

275

276

// Delete expired or unused key

277

secureStoreClient.deleteKey(apiKey);

278

System.out.println("Deleted secure key: " + apiKey.getName());

279

```

280

281

### Secure Key Rotation

282

283

```java

284

// Key rotation example

285

public void rotateSecureKey(SecureKeyId keyId, String newKeyData) {

286

try {

287

// Get current key metadata

288

SecureStoreMetadata currentMeta = secureStoreClient.getKeyMetadata(keyId);

289

290

// Create new key request with updated timestamp

291

Map<String, String> updatedProperties = new HashMap<>(currentMeta.getProperties());

292

updatedProperties.put("rotated.timestamp", String.valueOf(System.currentTimeMillis()));

293

updatedProperties.put("rotation.count",

294

String.valueOf(Integer.parseInt(updatedProperties.getOrDefault("rotation.count", "0")) + 1));

295

296

SecureKeyCreateRequest rotatedRequest = new SecureKeyCreateRequest(

297

currentMeta.getDescription() + " (rotated)",

298

newKeyData,

299

updatedProperties

300

);

301

302

// Delete old key and create new one

303

secureStoreClient.deleteKey(keyId);

304

secureStoreClient.createKey(keyId, rotatedRequest);

305

306

System.out.println("Successfully rotated key: " + keyId.getName());

307

308

// Notify applications that key has been rotated

309

notifyApplicationsOfKeyRotation(keyId);

310

311

} catch (Exception e) {

312

System.err.println("Key rotation failed for " + keyId.getName() + ": " + e.getMessage());

313

throw new RuntimeException("Key rotation failed", e);

314

}

315

}

316

317

private String generateSecureKey() {

318

// Generate cryptographically secure key (example implementation)

319

SecureRandom random = new SecureRandom();

320

byte[] keyBytes = new byte[32]; // 256-bit key

321

random.nextBytes(keyBytes);

322

return Base64.getEncoder().encodeToString(keyBytes);

323

}

324

325

private void notifyApplicationsOfKeyRotation(SecureKeyId keyId) {

326

// Implement application notification logic

327

System.out.println("Notifying applications of key rotation: " + keyId.getName());

328

}

329

330

private void connectToDatabase(String password) {

331

// Database connection logic using the secure password

332

System.out.println("Connecting to database with retrieved password");

333

}

334

```

335

336

## Metadata Management

337

338

### Adding and Managing Metadata

339

340

```java

341

// Add tags to application

342

ApplicationId appId = ApplicationId.of(namespaceId, "user-analytics", "1.0.0");

343

MetadataEntity appEntity = MetadataEntity.ofApplication(appId);

344

345

Set<String> tags = Set.of(

346

"analytics",

347

"user-data",

348

"production",

349

"gdpr-compliant",

350

"high-priority"

351

);

352

353

metadataClient.addTags(appEntity, tags);

354

System.out.println("Added tags to application: " + appId.getApplication());

355

356

// Add properties to application

357

Map<String, String> properties = Map.of(

358

"owner", "data-team@company.com",

359

"cost-center", "engineering",

360

"sla", "99.9%",

361

"data-classification", "internal",

362

"backup-required", "true",

363

"monitoring-tier", "critical"

364

);

365

366

metadataClient.addProperties(appEntity, properties);

367

System.out.println("Added properties to application");

368

369

// Add metadata to dataset

370

DatasetId datasetId = DatasetId.of(namespaceId, "user-profiles");

371

MetadataEntity datasetEntity = MetadataEntity.ofDataset(datasetId);

372

373

metadataClient.addTags(datasetEntity, Set.of("pii", "user-data", "encrypted"));

374

metadataClient.addProperties(datasetEntity, Map.of(

375

"schema-version", "2.1",

376

"retention-policy", "7-years",

377

"encryption-enabled", "true",

378

"access-control", "restricted"

379

));

380

```

381

382

### Retrieving Metadata

383

384

```java

385

// Get tags for application

386

Set<String> appTags = metadataClient.getTags(appEntity);

387

System.out.println("Application tags: " + appTags);

388

389

// Get properties for application

390

Map<String, String> appProperties = metadataClient.getProperties(appEntity);

391

System.out.println("Application properties: " + appProperties);

392

393

// Get all metadata for dataset

394

Set<String> datasetTags = metadataClient.getTags(datasetEntity);

395

Map<String, String> datasetProperties = metadataClient.getProperties(datasetEntity);

396

397

System.out.println("Dataset " + datasetId.getDataset() + ":");

398

System.out.println(" Tags: " + datasetTags);

399

System.out.println(" Properties: " + datasetProperties);

400

```

401

402

### Metadata Search

403

404

```java

405

// Search by tags

406

String tagQuery = "tags:analytics AND tags:production";

407

List<MetadataSearchResult> tagResults = metadataClient.searchMetadata(namespaceId, tagQuery);

408

409

System.out.println("Found " + tagResults.size() + " entities with analytics and production tags:");

410

for (MetadataSearchResult result : tagResults) {

411

System.out.println("- " + result.getEntityId() + " (type: " + result.getEntityType() + ")");

412

}

413

414

// Search by properties

415

String propertyQuery = "properties.owner:data-team*";

416

List<MetadataSearchResult> ownerResults = metadataClient.searchMetadata(namespaceId, propertyQuery);

417

418

// Complex search query

419

String complexQuery = "(tags:user-data OR tags:analytics) AND properties.sla:99.9% AND type:application";

420

List<MetadataSearchResult> complexResults = metadataClient.searchMetadata(namespaceId, complexQuery);

421

422

System.out.println("Complex search results: " + complexResults.size() + " entities");

423

```

424

425

## Data Operations and Utilities

426

427

### LineageClient

428

429

```java { .api }

430

public class LineageClient {

431

// Constructors

432

public LineageClient(ClientConfig config);

433

public LineageClient(ClientConfig config, RESTClient restClient);

434

435

// Data lineage operations

436

// Common operations include:

437

// - getLineage(DatasetId dataset, long startTime, long endTime, int levels)

438

// - getFieldLineage(DatasetId dataset, String field, long startTime, long endTime)

439

}

440

```

441

442

### PreferencesClient

443

444

```java { .api }

445

public class PreferencesClient {

446

// Constructors

447

public PreferencesClient(ClientConfig config);

448

public PreferencesClient(ClientConfig config, RESTClient restClient);

449

450

// Preferences management operations

451

// Common operations include:

452

// - setPreferences(NamespaceId namespace, Map<String, String> preferences)

453

// - getPreferences(NamespaceId namespace)

454

// - setApplicationPreferences(ApplicationId application, Map<String, String> preferences)

455

// - getProgramPreferences(ProgramId program)

456

}

457

```

458

459

### DatasetModuleClient and DatasetTypeClient

460

461

```java { .api }

462

public class DatasetModuleClient {

463

// Constructors

464

public DatasetModuleClient(ClientConfig config);

465

public DatasetModuleClient(ClientConfig config, RESTClient restClient);

466

467

// Dataset module management operations

468

// Common operations include:

469

// - deploy(NamespaceId namespace, String moduleName, String className, File jarFile)

470

// - list(NamespaceId namespace)

471

// - get(DatasetModuleId moduleId)

472

// - delete(DatasetModuleId moduleId)

473

}

474

475

public class DatasetTypeClient {

476

// Constructors

477

public DatasetTypeClient(ClientConfig config);

478

public DatasetTypeClient(ClientConfig config, RESTClient restClient);

479

480

// Dataset type operations

481

// Common operations include:

482

// - list(NamespaceId namespace)

483

// - get(DatasetTypeId typeId)

484

}

485

```

486

487

### WorkflowClient

488

489

```java { .api }

490

public class WorkflowClient {

491

// Constructors

492

public WorkflowClient(ClientConfig config);

493

public WorkflowClient(ClientConfig config, RESTClient restClient);

494

495

// Workflow management operations

496

// Common operations include:

497

// - getWorkflowToken(ProgramRunId workflowRun)

498

// - getWorkflowStatus(ProgramId workflow)

499

// - getNodeStates(ProgramRunId workflowRun)

500

}

501

```

502

503

### MetaClient

504

505

```java { .api }

506

public class MetaClient {

507

// Constructors

508

public MetaClient(ClientConfig config);

509

public MetaClient(ClientConfig config, RESTClient restClient);

510

511

// Meta information operations

512

// Common operations include:

513

// - getVersion()

514

// - ping()

515

// - getConfiguration()

516

}

517

```

518

519

## Advanced Administrative Operations

520

521

### Comprehensive Environment Setup

522

523

```java

524

public class EnvironmentSetup {

525

private final NamespaceClient namespaceClient;

526

private final SecureStoreClient secureStoreClient;

527

private final MetadataClient metadataClient;

528

529

public EnvironmentSetup(NamespaceClient namespaceClient,

530

SecureStoreClient secureStoreClient,

531

MetadataClient metadataClient) {

532

this.namespaceClient = namespaceClient;

533

this.secureStoreClient = secureStoreClient;

534

this.metadataClient = metadataClient;

535

}

536

537

public void setupProductionEnvironment() {

538

try {

539

// Create production namespace

540

createProductionNamespace();

541

542

// Set up secure keys

543

setupSecureKeys();

544

545

// Configure metadata policies

546

setupMetadataPolicies();

547

548

System.out.println("Production environment setup completed");

549

550

} catch (Exception e) {

551

System.err.println("Environment setup failed: " + e.getMessage());

552

throw new RuntimeException("Failed to setup production environment", e);

553

}

554

}

555

556

private void createProductionNamespace() {

557

Map<String, String> prodConfig = Map.of(

558

"security.authorization.enabled", "true",

559

"security.authentication.required", "true",

560

"audit.enabled", "true",

561

"data.retention.days", "2555", // 7 years

562

"backup.enabled", "true",

563

"monitoring.enabled", "true",

564

"scheduler.queue.name", "production"

565

);

566

567

NamespaceMeta prodNamespace = new NamespaceMeta(

568

"production",

569

"Production environment with enterprise security",

570

prodConfig

571

);

572

573

try {

574

namespaceClient.create(prodNamespace);

575

System.out.println("Created production namespace");

576

} catch (NamespaceAlreadyExistsException e) {

577

System.out.println("Production namespace already exists");

578

}

579

}

580

581

private void setupSecureKeys() {

582

NamespaceId prodNamespace = NamespaceId.of("production");

583

584

// Database credentials

585

createSecureKey(prodNamespace, "db-primary-password",

586

"Primary database password", getSecureInput("database password"));

587

588

// API keys

589

createSecureKey(prodNamespace, "external-api-key",

590

"External service API key", getSecureInput("API key"));

591

592

// Encryption keys

593

createSecureKey(prodNamespace, "data-encryption-key",

594

"Data encryption key", generateEncryptionKey());

595

596

System.out.println("Secure keys configured");

597

}

598

599

private void setupMetadataPolicies() {

600

// Add metadata tags for governance

601

NamespaceId prodNamespace = NamespaceId.of("production");

602

MetadataEntity namespaceEntity = MetadataEntity.ofNamespace(prodNamespace);

603

604

metadataClient.addTags(namespaceEntity, Set.of(

605

"production", "enterprise", "audited", "secure"

606

));

607

608

metadataClient.addProperties(namespaceEntity, Map.of(

609

"environment", "production",

610

"compliance", "sox-gdpr",

611

"backup-policy", "daily",

612

"retention-policy", "7-years",

613

"access-policy", "restricted"

614

));

615

616

System.out.println("Metadata policies configured");

617

}

618

619

private void createSecureKey(NamespaceId namespace, String keyName, String description, String keyData) {

620

SecureKeyId keyId = SecureKeyId.of(namespace, keyName);

621

SecureKeyCreateRequest request = new SecureKeyCreateRequest(description, keyData, Map.of(

622

"created.by", "admin",

623

"created.timestamp", String.valueOf(System.currentTimeMillis()),

624

"environment", "production"

625

));

626

627

try {

628

secureStoreClient.createKey(keyId, request);

629

System.out.println("Created secure key: " + keyName);

630

} catch (Exception e) {

631

System.err.println("Failed to create key " + keyName + ": " + e.getMessage());

632

}

633

}

634

635

private String getSecureInput(String prompt) {

636

// Implement secure input mechanism (e.g., read from environment, vault, etc.)

637

return System.getenv("SECURE_" + prompt.toUpperCase().replace(" ", "_"));

638

}

639

640

private String generateEncryptionKey() {

641

SecureRandom random = new SecureRandom();

642

byte[] key = new byte[32]; // 256-bit key

643

random.nextBytes(key);

644

return Base64.getEncoder().encodeToString(key);

645

}

646

}

647

```

648

649

## Error Handling

650

651

Security and administrative operations may throw these exceptions:

652

653

- **NamespaceNotFoundException**: Namespace does not exist

654

- **NamespaceAlreadyExistsException**: Namespace already exists during creation

655

- **SecureKeyNotFoundException**: Secure key does not exist

656

- **SecureKeyAlreadyExistsException**: Secure key already exists during creation

657

- **UnauthorizedException**: Insufficient permissions for security operations

658

- **UnauthenticatedException**: Authentication required

659

660

```java

661

try {

662

NamespaceMeta namespace = namespaceClient.get(namespaceId);

663

System.out.println("Namespace found: " + namespace.getName());

664

} catch (NamespaceNotFoundException e) {

665

System.err.println("Namespace not found: " + namespaceId.getId());

666

} catch (UnauthorizedException e) {

667

System.err.println("No permission to access namespace: " + e.getMessage());

668

} catch (IOException e) {

669

System.err.println("Network error: " + e.getMessage());

670

}

671

```

672

673

## Best Practices

674

675

1. **Security First**: Always use secure key storage for sensitive data

676

2. **Least Privilege**: Grant minimum necessary permissions

677

3. **Metadata Governance**: Implement consistent metadata tagging strategies

678

4. **Environment Separation**: Use separate namespaces for different environments

679

5. **Key Rotation**: Regularly rotate secure keys and credentials

680

6. **Audit Trail**: Enable auditing for all security-related operations

681

682

```java

683

// Good: Comprehensive security setup with proper error handling

684

public class SecurityManager {

685

private final SecureStoreClient secureStoreClient;

686

private final MetadataClient metadataClient;

687

688

public SecurityManager(SecureStoreClient secureStoreClient, MetadataClient metadataClient) {

689

this.secureStoreClient = secureStoreClient;

690

this.metadataClient = metadataClient;

691

}

692

693

public void setupApplicationSecurity(ApplicationId appId, Map<String, String> credentials) {

694

try {

695

// Create secure keys for application

696

for (Map.Entry<String, String> entry : credentials.entrySet()) {

697

createApplicationSecureKey(appId, entry.getKey(), entry.getValue());

698

}

699

700

// Add security metadata

701

addSecurityMetadata(appId);

702

703

System.out.println("Security setup completed for application: " + appId.getApplication());

704

705

} catch (Exception e) {

706

System.err.println("Security setup failed for " + appId.getApplication() + ": " + e.getMessage());

707

throw new SecurityException("Application security setup failed", e);

708

}

709

}

710

711

private void createApplicationSecureKey(ApplicationId appId, String keyName, String keyValue) {

712

SecureKeyId keyId = SecureKeyId.of(appId.getNamespace(),

713

appId.getApplication() + "-" + keyName);

714

715

SecureKeyCreateRequest request = new SecureKeyCreateRequest(

716

"Secure key for " + appId.getApplication() + " - " + keyName,

717

keyValue,

718

Map.of(

719

"application", appId.getApplication(),

720

"key-type", keyName,

721

"created.timestamp", String.valueOf(System.currentTimeMillis())

722

)

723

);

724

725

try {

726

secureStoreClient.createKey(keyId, request);

727

System.out.println("Created secure key: " + keyId.getName());

728

} catch (SecureKeyAlreadyExistsException e) {

729

System.out.println("Secure key already exists, skipping: " + keyId.getName());

730

}

731

}

732

733

private void addSecurityMetadata(ApplicationId appId) {

734

MetadataEntity entity = MetadataEntity.ofApplication(appId);

735

736

metadataClient.addTags(entity, Set.of("secure", "credentials-managed"));

737

metadataClient.addProperties(entity, Map.of(

738

"security.credentials", "managed",

739

"security.review.required", "true",

740

"security.last.review", String.valueOf(System.currentTimeMillis())

741

));

742

}

743

}

744

```