1414import java .io .IOException ;
1515import java .nio .file .Path ;
1616import java .util .List ;
17- import java .util .stream .Collectors ;
1817
1918import com .fasterxml .jackson .databind .JsonNode ;
2019import com .fasterxml .jackson .databind .node .ObjectNode ;
3231import org .opensearch .rest .RestController ;
3332import org .opensearch .rest .RestRequest ;
3433import org .opensearch .rest .RestRequest .Method ;
35- import org .opensearch .security .DefaultObjectMapper ;
3634import org .opensearch .security .auditlog .AuditLog ;
3735import org .opensearch .security .configuration .AdminDNs ;
3836import org .opensearch .security .configuration .ConfigurationRepository ;
3937import org .opensearch .security .dlic .rest .validation .AbstractConfigurationValidator ;
4038import org .opensearch .security .dlic .rest .validation .InternalUsersValidator ;
4139import org .opensearch .security .privileges .PrivilegesEvaluator ;
42- import org .opensearch .security .securityconf .Hashed ;
4340import org .opensearch .security .securityconf .impl .CType ;
4441import org .opensearch .security .securityconf .impl .SecurityDynamicConfiguration ;
4542import org .opensearch .security .ssl .transport .PrincipalExtractor ;
4643import org .opensearch .security .support .SecurityJsonNode ;
44+ import org .opensearch .security .user .UserService ;
45+ import org .opensearch .security .user .UserServiceException ;
4746import org .opensearch .threadpool .ThreadPool ;
4847
4948import static org .opensearch .security .dlic .rest .support .Utils .addRoutesPrefix ;
@@ -69,13 +68,16 @@ public class InternalUsersApiAction extends PatchableResourceApiAction {
6968 new Route (Method .PATCH , "/internalusers/{name}" )
7069 ));
7170
71+ UserService userService ;
72+
7273 @ Inject
7374 public InternalUsersApiAction (final Settings settings , final Path configPath , final RestController controller ,
7475 final Client client , final AdminDNs adminDNs , final ConfigurationRepository cl ,
7576 final ClusterService cs , final PrincipalExtractor principalExtractor , final PrivilegesEvaluator evaluator ,
76- ThreadPool threadPool , AuditLog auditLog ) {
77+ ThreadPool threadPool , UserService userService , AuditLog auditLog ) {
7778 super (settings , configPath , controller , client , adminDNs , cl , cs , principalExtractor , evaluator , threadPool ,
7879 auditLog );
80+ this .userService = userService ;
7981 }
8082
8183 @ Override
@@ -100,22 +102,7 @@ protected void handlePut(RestChannel channel, final RestRequest request, final C
100102
101103 final String username = request .param ("name" );
102104
103- if (username == null || username .length () == 0 ) {
104- badRequestResponse (channel , "No " + getResourceName () + " specified." );
105- return ;
106- }
107-
108- final List <String > foundRestrictedContents = RESTRICTED_FROM_USERNAME .stream ().filter (username ::contains ).collect (Collectors .toList ());
109- if (!foundRestrictedContents .isEmpty ()) {
110- final String restrictedContents = foundRestrictedContents .stream ().map (s -> "'" + s + "'" ).collect (Collectors .joining ("," ));
111- badRequestResponse (channel , "Username has restricted characters " + restrictedContents + " that are not permitted." );
112- return ;
113- }
114-
115- // TODO it might be sensible to consolidate this with the overridden method in
116- // order to minimize duplicated logic
117-
118- final SecurityDynamicConfiguration <?> internalUsersConfiguration = load (getConfigName (), false );
105+ SecurityDynamicConfiguration <?> internalUsersConfiguration = load (getConfigName (), false );
119106
120107 if (!isWriteable (channel , internalUsersConfiguration , username )) {
121108 return ;
@@ -128,50 +115,35 @@ protected void handlePut(RestChannel channel, final RestRequest request, final C
128115 final List <String > securityRoles = securityJsonNode .get ("opendistro_security_roles" ).asList ();
129116 if (securityRoles != null ) {
130117 for (final String role : securityRoles ) {
131- if (!isValidRolesMapping (channel , role )) return ;
118+ if (!isValidRolesMapping (channel , role )) {
119+ return ;
120+ }
132121 }
133122 }
134123
135- // if password is set, it takes precedence over hash
136- final String plainTextPassword = securityJsonNode .get ("password" ).asString ();
137- final String origHash = securityJsonNode .get ("hash" ).asString ();
138- if (plainTextPassword != null && plainTextPassword .length () > 0 ) {
139- contentAsNode .remove ("password" );
140- contentAsNode .put ("hash" , hash (plainTextPassword .toCharArray ()));
141- } else if (origHash != null && origHash .length () > 0 ) {
142- contentAsNode .remove ("password" );
143- } else if (plainTextPassword != null && plainTextPassword .isEmpty () && origHash == null ) {
144- contentAsNode .remove ("password" );
145- }
146-
147124 final boolean userExisted = internalUsersConfiguration .exists (username );
148125
149126 // when updating an existing user password hash can be blank, which means no
150127 // changes
151128
152- // sanity checks, hash is mandatory for newly created users
153- if (!userExisted && securityJsonNode .get ("hash" ).asString () == null ) {
154- badRequestResponse (channel , "Please specify either 'hash' or 'password' when creating a new internal user." );
129+ try {
130+ if (request .hasParam ("owner" )) {
131+ ((ObjectNode ) content ).put ("owner" , request .param ("owner" ));
132+ }
133+ if (request .hasParam ("isEnabled" )) {
134+ ((ObjectNode ) content ).put ("isEnabled" , request .param ("isEnabled" ));
135+ }
136+ ((ObjectNode ) content ).put ("name" , username );
137+ internalUsersConfiguration = userService .createOrUpdateAccount ((ObjectNode ) content );
138+ }
139+ catch (UserServiceException ex ) {
140+ badRequestResponse (channel , ex .getMessage ());
155141 return ;
156142 }
157-
158- // for existing users, hash is optional
159- if (userExisted && securityJsonNode .get ("hash" ).asString () == null ) {
160- // sanity check, this should usually not happen
161- final String hash = ((Hashed ) internalUsersConfiguration .getCEntry (username )).getHash ();
162- if (hash == null || hash .length () == 0 ) {
163- internalErrorResponse (channel ,
164- "Existing user " + username + " has no password, and no new password or hash was specified." );
165- return ;
166- }
167- contentAsNode .put ("hash" , hash );
143+ catch (IOException ex ) {
144+ throw new IOException (ex );
168145 }
169146
170- internalUsersConfiguration .remove (username );
171-
172- // checks complete, create or update the user
173- internalUsersConfiguration .putCObject (username , DefaultObjectMapper .readTree (contentAsNode , internalUsersConfiguration .getImplementingClass ()));
174-
175147 saveAndUpdateConfigs (this .securityIndexName ,client , CType .INTERNALUSERS , internalUsersConfiguration , new OnSucessActionListener <IndexResponse >(channel ) {
176148
177149 @ Override
0 commit comments