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
```