1
1
package com .marklogic .appdeployer .command .security ;
2
2
3
- import com .fasterxml .jackson .databind .JsonNode ;
4
- import com .fasterxml .jackson .databind .node .ArrayNode ;
5
3
import com .marklogic .appdeployer .command .AbstractResourceCommand ;
6
4
import com .marklogic .appdeployer .command .CommandContext ;
7
5
import com .marklogic .appdeployer .command .SortOrderConstants ;
8
- import com .marklogic .mgmt .PayloadParser ;
9
- import com .marklogic .mgmt .resource .ResourceManager ;
6
+ import com .marklogic .mgmt .api .API ;
10
7
import com .marklogic .mgmt .api .security .Role ;
8
+ import com .marklogic .mgmt .mapper .DefaultResourceMapper ;
9
+ import com .marklogic .mgmt .mapper .ResourceMapper ;
10
+ import com .marklogic .mgmt .resource .ResourceManager ;
11
11
import com .marklogic .mgmt .resource .security .RoleManager ;
12
- import com .marklogic .rest .util .Fragment ;
13
12
14
13
import java .io .File ;
15
- import java .util .*;
16
14
17
15
public class DeployRolesCommand extends AbstractResourceCommand {
18
16
19
- private int maxSortAttempts = 100 ;
17
+ // Used internally
18
+ private boolean removeRolesAndPermissionsDuringDeployment = false ;
19
+ private ResourceMapper resourceMapper ;
20
20
21
21
public DeployRolesCommand () {
22
22
setExecuteSortOrder (SortOrderConstants .DEPLOY_ROLES );
23
23
setUndoSortOrder (SortOrderConstants .DELETE_ROLES );
24
24
}
25
25
26
26
/**
27
- * Overriding this so we can list the files based on role dependencies.
27
+ * The set of roles is processed twice. The first time, the roles are saved without any permissions or dependent roles.
28
+ * This is to avoid issues where the roles depend on each other or on themselves. The second time, the roles are
29
+ * saved with permissions and dependent roles, which is guaranteed to work now that the roles have all been created.
28
30
*
29
- * @param dir
30
- * @return
31
+ * @param context
31
32
*/
32
33
@ Override
33
- protected File [] listFilesInDirectory (File dir , CommandContext context ) {
34
- File [] files = super .listFilesInDirectory (dir );
35
-
36
- if (context .getAppConfig ().isSortRolesByDependencies () && files != null && files .length > 0 ) {
37
- if (logger .isInfoEnabled ()) {
38
- logger .info ("Sorting role files by role dependencies" );
39
- }
40
- List <RoleFile > roleFiles = sortFilesBasedOnRoleDependencies (files , context );
41
- files = new File [files .length ];
42
- for (int i = 0 ; i < roleFiles .size (); i ++) {
43
- files [i ] = roleFiles .get (i ).file ;
44
- }
34
+ public void execute (CommandContext context ) {
35
+ removeRolesAndPermissionsDuringDeployment = true ;
36
+ if (logger .isInfoEnabled ()) {
37
+ logger .info ("Deploying roles without any permissions or dependent roles" );
45
38
}
46
-
47
- return files ;
39
+ super .execute (context );
40
+ if (logger .isInfoEnabled ()) {
41
+ logger .info ("Deploying roles with permissions and dependent roles" );
42
+ }
43
+ removeRolesAndPermissionsDuringDeployment = false ;
44
+ super .execute (context );
48
45
}
49
46
50
- protected List <RoleFile > sortFilesBasedOnRoleDependencies (File [] files , CommandContext context ) {
51
- List <RoleFile > roleFiles = new ArrayList <>();
52
- if (files == null || files .length < 1 ) {
53
- return roleFiles ;
54
- }
55
- PayloadParser parser = new PayloadParser ();
56
- for (File f : files ) {
57
- RoleFile rf = new RoleFile (f );
58
- String payload = copyFileToString (f , context );
59
- if (parser .isJsonPayload (payload )) {
60
- JsonNode json = parser .parseJson (payload );
61
- rf .role .setRoleName (json .get ("role-name" ).asText ());
62
- if (json .has ("role" )) {
63
- ArrayNode roles = (ArrayNode ) json .get ("role" );
64
- Iterator <JsonNode > iter = roles .elements ();
65
- while (iter .hasNext ()) {
66
- rf .role .getRole ().add (iter .next ().asText ());
67
- }
68
- }
69
- } else {
70
- Fragment frag = new Fragment (payload );
71
- rf .role .setRoleName (frag .getElementValue ("/node()/m:role-name" ));
72
- rf .role .setRole (frag .getElementValues ("/node()/m:roles/m:role" ));
47
+ @ Override
48
+ protected String adjustPayloadBeforeSavingResource (ResourceManager mgr , CommandContext context , File f , String payload ) {
49
+ if (removeRolesAndPermissionsDuringDeployment ) {
50
+ if (resourceMapper == null ) {
51
+ API api = new API (context .getManageClient (), context .getAdminManager ());
52
+ resourceMapper = new DefaultResourceMapper (api );
73
53
}
74
- roleFiles .add (rf );
54
+ Role role = resourceMapper .readResource (payload , Role .class );
55
+ role .clearPermissionsAndRoles ();
56
+ return role .getJson ();
75
57
}
76
-
77
- return keepSortingRoleFilesUntilOrderDoesntChange (roleFiles );
78
- }
79
-
80
- /**
81
- * Some sets of role files require multiple sorts until the order no longer changes. The maxSortAttempts class
82
- * attribute controls how many times this command will try to sort the roles.
83
- *
84
- * @param roleFiles
85
- * @return
86
- */
87
- protected List <RoleFile > keepSortingRoleFilesUntilOrderDoesntChange (List <RoleFile > roleFiles ) {
88
- List <RoleFile > previousRoleFiles ;
89
- int counter = 0 ;
90
- do {
91
- previousRoleFiles = roleFiles ;
92
- roleFiles = new ArrayList <>();
93
- roleFiles .addAll (previousRoleFiles );
94
- roleFiles = sortRoleFiles (roleFiles );
95
- counter ++;
96
- } while (!previousRoleFiles .equals (roleFiles ) && counter < maxSortAttempts );
97
- return roleFiles ;
98
- }
99
-
100
- protected List <RoleFile > sortRoleFiles (List <RoleFile > roleFiles ) {
101
- RoleFileComparator comparator = new RoleFileComparator (roleFiles );
102
- Collections .sort (roleFiles , comparator );
103
- return roleFiles ;
58
+ return payload ;
104
59
}
105
60
106
61
protected File [] getResourceDirs (CommandContext context ) {
@@ -111,104 +66,5 @@ protected File[] getResourceDirs(CommandContext context) {
111
66
protected ResourceManager getResourceManager (CommandContext context ) {
112
67
return new RoleManager (context .getManageClient ());
113
68
}
114
-
115
- public void setMaxSortAttempts (int maxSortAttempts ) {
116
- this .maxSortAttempts = maxSortAttempts ;
117
- }
118
- }
119
-
120
- /**
121
- * Simple data structure for associating a File that defines a role, and the parsed Role that is used for
122
- * sorting the role files.
123
- */
124
- class RoleFile {
125
-
126
- File file ;
127
- Role role ;
128
-
129
- public RoleFile (File file ) {
130
- this .file = file ;
131
- this .role = new Role ();
132
- this .role .setRole (new ArrayList <String >());
133
- }
134
-
135
- @ Override
136
- public int hashCode () {
137
- return role .getRoleName ().hashCode ();
138
- }
139
-
140
- @ Override
141
- public boolean equals (Object obj ) {
142
- return role .getRoleName ().equals (((RoleFile )obj ).role .getRoleName ());
143
- }
144
69
}
145
70
146
- /**
147
- * This comparator is designed to handle a scenario where two roles are next to each other, but they don't have any
148
- * dependencies in common, nor does one depend on the other. In this scenario, we need to know which role has a
149
- * dependency on a role furthest to the end of the list of roles. In order to know that, we first build up a data
150
- * structure that tracks the highest position of a dependency in the list of roles for each role.
151
- */
152
- class RoleFileComparator implements Comparator <RoleFile > {
153
-
154
- private Map <String , Integer > highestDependencyPositionMap ;
155
-
156
- public RoleFileComparator (List <RoleFile > roleFiles ) {
157
- Map <String , Integer > rolePositions = new HashMap <>();
158
- for (int i = 0 ; i < roleFiles .size (); i ++) {
159
- rolePositions .put (roleFiles .get (i ).role .getRoleName (), i );
160
- }
161
-
162
- highestDependencyPositionMap = new HashMap <>();
163
- for (RoleFile rf : roleFiles ) {
164
- String roleName = rf .role .getRoleName ();
165
- int highest = -1 ;
166
- for (String role : rf .role .getRole ()) {
167
- if (rolePositions .containsKey (role )) {
168
- int pos = rolePositions .get (role );
169
- if (pos > highest ) {
170
- highest = pos ;
171
- }
172
- }
173
- }
174
- highestDependencyPositionMap .put (roleName , highest );
175
- }
176
- }
177
-
178
- @ Override
179
- public int compare (RoleFile o1 , RoleFile o2 ) {
180
- if (o1 .role .getRole ().isEmpty () && o2 .role .getRole ().isEmpty ()) {
181
- return 0 ;
182
- }
183
- if (o1 .role .getRole ().isEmpty ()) {
184
- return -1 ;
185
- }
186
- if (o2 .role .getRole ().isEmpty ()) {
187
- return 1 ;
188
- }
189
- if (o2 .role .getRole ().contains (o1 .role .getRoleName ())) {
190
- return -1 ;
191
- }
192
- if (o1 .role .getRole ().contains (o2 .role .getRoleName ())) {
193
- return 1 ;
194
- }
195
-
196
- /**
197
- * If the roles aren't dependent on each other, then we want to base this on which role has a dependency further
198
- * to the right.
199
- */
200
- int o1Pos = highestDependencyPositionMap .get (o1 .role .getRoleName ());
201
- int o2Pos = highestDependencyPositionMap .get (o2 .role .getRoleName ());
202
- if (o1Pos > o2Pos ) {
203
- return 1 ;
204
- }
205
- if (o2Pos > o1Pos ) {
206
- return -1 ;
207
- }
208
-
209
- /**
210
- * This would be for two roles that depend on the same other role.
211
- */
212
- return 0 ;
213
- }
214
- }
0 commit comments