Support creation of accounts via REST
PUT on '/accounts/<username>' creates a new account. The code from the 'create-account' SSH command was moved to the REST layer. Change-Id: Ic8fd9daf9fb64f7918b7f7eb461f8e8e957099a9 Signed-off-by: Edwin Kempin <edwin.kempin@sap.com>
This commit is contained in:
@@ -35,6 +35,50 @@ Returns an account as an link:#account-info[AccountInfo] entity.
|
|||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
|
||||||
|
[[create-account]]
|
||||||
|
Create Account
|
||||||
|
~~~~~~~~~~~~~~
|
||||||
|
[verse]
|
||||||
|
'PUT /accounts/link:#username[\{username\}]'
|
||||||
|
|
||||||
|
Creates a new account.
|
||||||
|
|
||||||
|
In the request body additional data for the account can be provided as
|
||||||
|
link:#account-input[AccountInput].
|
||||||
|
|
||||||
|
.Request
|
||||||
|
----
|
||||||
|
PUT /accounts/john HTTP/1.0
|
||||||
|
Content-Type: application/json;charset=UTF-8
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "John Doe",
|
||||||
|
"email": "john.doe@example.com",
|
||||||
|
"ssh_key": "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA0T...YImydZAw==",
|
||||||
|
"http_password": "19D9aIn7zePb",
|
||||||
|
"groups": [
|
||||||
|
"MyProject-Owners"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
As response a detailed link:#account-info[AccountInfo] entity is
|
||||||
|
returned that describes the created account.
|
||||||
|
|
||||||
|
.Response
|
||||||
|
----
|
||||||
|
HTTP/1.1 201 Created
|
||||||
|
Content-Disposition: attachment
|
||||||
|
Content-Type: application/json;charset=UTF-8
|
||||||
|
|
||||||
|
)]}'
|
||||||
|
{
|
||||||
|
"_account_id": 1000195,
|
||||||
|
"name": "John Doe",
|
||||||
|
"email": "john.doe@example.com"
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
[[list-account-capabilities]]
|
[[list-account-capabilities]]
|
||||||
List Account Capabilities
|
List Account Capabilities
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
@@ -391,6 +435,11 @@ This can be:
|
|||||||
Identifier of a global capability. Valid values are all field names of
|
Identifier of a global capability. Valid values are all field names of
|
||||||
the link:#capability-info[CapabilityInfo] entity.
|
the link:#capability-info[CapabilityInfo] entity.
|
||||||
|
|
||||||
|
[[username]]
|
||||||
|
\{username\}
|
||||||
|
~~~~~~~~~~~~
|
||||||
|
The user name.
|
||||||
|
|
||||||
|
|
||||||
[[json-entities]]
|
[[json-entities]]
|
||||||
JSON Entities
|
JSON Entities
|
||||||
@@ -412,6 +461,26 @@ The email address the user prefers to be contacted through. +
|
|||||||
Only set if detailed account information is requested.
|
Only set if detailed account information is requested.
|
||||||
|===========================
|
|===========================
|
||||||
|
|
||||||
|
[[account-input]]
|
||||||
|
AccountInput
|
||||||
|
~~~~~~~~~~~~
|
||||||
|
The `AccountInput` entity contains information for the creation of
|
||||||
|
a new account.
|
||||||
|
|
||||||
|
[options="header",width="50%",cols="1,^2,4"]
|
||||||
|
|============================
|
||||||
|
|Field Name ||Description
|
||||||
|
|`username` |optional|
|
||||||
|
The user name. If provided, must match the user name from the URL.
|
||||||
|
|`name` |optional|The full name of the user.
|
||||||
|
|`email` |optional|The email address of the user.
|
||||||
|
|`ssh_key` |optional|The public SSH key of the user.
|
||||||
|
|`http_password`|optional|The HTTP password of the user.
|
||||||
|
|`groups` |optional|
|
||||||
|
A list of link:rest-api-groups.html#group-id[group IDs] that identify
|
||||||
|
the groups to which the user should be added.
|
||||||
|
|============================
|
||||||
|
|
||||||
[[capability-info]]
|
[[capability-info]]
|
||||||
CapabilityInfo
|
CapabilityInfo
|
||||||
~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ package com.google.gerrit.server.account;
|
|||||||
|
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.gerrit.extensions.registration.DynamicMap;
|
import com.google.gerrit.extensions.registration.DynamicMap;
|
||||||
|
import com.google.gerrit.extensions.restapi.AcceptsCreate;
|
||||||
import com.google.gerrit.extensions.restapi.AuthException;
|
import com.google.gerrit.extensions.restapi.AuthException;
|
||||||
import com.google.gerrit.extensions.restapi.IdString;
|
import com.google.gerrit.extensions.restapi.IdString;
|
||||||
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
||||||
@@ -34,24 +35,28 @@ import com.google.inject.Provider;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
public class AccountsCollection implements
|
public class AccountsCollection implements
|
||||||
RestCollection<TopLevelResource, AccountResource> {
|
RestCollection<TopLevelResource, AccountResource>,
|
||||||
|
AcceptsCreate<TopLevelResource>{
|
||||||
private final Provider<CurrentUser> self;
|
private final Provider<CurrentUser> self;
|
||||||
private final AccountResolver resolver;
|
private final AccountResolver resolver;
|
||||||
private final AccountControl.Factory accountControlFactory;
|
private final AccountControl.Factory accountControlFactory;
|
||||||
private final IdentifiedUser.GenericFactory userFactory;
|
private final IdentifiedUser.GenericFactory userFactory;
|
||||||
private final DynamicMap<RestView<AccountResource>> views;
|
private final DynamicMap<RestView<AccountResource>> views;
|
||||||
|
private final CreateAccount.Factory createAccountFactory;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
AccountsCollection(Provider<CurrentUser> self,
|
AccountsCollection(Provider<CurrentUser> self,
|
||||||
AccountResolver resolver,
|
AccountResolver resolver,
|
||||||
AccountControl.Factory accountControlFactory,
|
AccountControl.Factory accountControlFactory,
|
||||||
IdentifiedUser.GenericFactory userFactory,
|
IdentifiedUser.GenericFactory userFactory,
|
||||||
DynamicMap<RestView<AccountResource>> views) {
|
DynamicMap<RestView<AccountResource>> views,
|
||||||
|
CreateAccount.Factory createAccountFactory) {
|
||||||
this.self = self;
|
this.self = self;
|
||||||
this.resolver = resolver;
|
this.resolver = resolver;
|
||||||
this.accountControlFactory = accountControlFactory;
|
this.accountControlFactory = accountControlFactory;
|
||||||
this.userFactory = userFactory;
|
this.userFactory = userFactory;
|
||||||
this.views = views;
|
this.views = views;
|
||||||
|
this.createAccountFactory = createAccountFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -116,4 +121,10 @@ public class AccountsCollection implements
|
|||||||
public DynamicMap<RestView<AccountResource>> views() {
|
public DynamicMap<RestView<AccountResource>> views() {
|
||||||
return views;
|
return views;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public CreateAccount create(TopLevelResource parent, IdString username) {
|
||||||
|
return createAccountFactory.create(username.get());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,200 @@
|
|||||||
|
// Copyright (C) 2013 The Android Open Source Project
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package com.google.gerrit.server.account;
|
||||||
|
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
import com.google.gerrit.common.data.GlobalCapability;
|
||||||
|
import com.google.gerrit.common.data.GroupDescriptions;
|
||||||
|
import com.google.gerrit.common.errors.InvalidSshKeyException;
|
||||||
|
import com.google.gerrit.extensions.annotations.RequiresCapability;
|
||||||
|
import com.google.gerrit.extensions.restapi.BadRequestException;
|
||||||
|
import com.google.gerrit.extensions.restapi.DefaultInput;
|
||||||
|
import com.google.gerrit.extensions.restapi.ResourceConflictException;
|
||||||
|
import com.google.gerrit.extensions.restapi.Response;
|
||||||
|
import com.google.gerrit.extensions.restapi.RestModifyView;
|
||||||
|
import com.google.gerrit.extensions.restapi.TopLevelResource;
|
||||||
|
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
|
||||||
|
import com.google.gerrit.reviewdb.client.Account;
|
||||||
|
import com.google.gerrit.reviewdb.client.AccountExternalId;
|
||||||
|
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||||
|
import com.google.gerrit.reviewdb.client.AccountGroupMember;
|
||||||
|
import com.google.gerrit.reviewdb.client.AccountGroupMemberAudit;
|
||||||
|
import com.google.gerrit.reviewdb.client.AccountSshKey;
|
||||||
|
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||||
|
import com.google.gerrit.server.IdentifiedUser;
|
||||||
|
import com.google.gerrit.server.account.CreateAccount.Input;
|
||||||
|
import com.google.gerrit.server.group.GroupsCollection;
|
||||||
|
import com.google.gerrit.server.ssh.SshKeyCache;
|
||||||
|
import com.google.gwtorm.server.OrmDuplicateKeyException;
|
||||||
|
import com.google.gwtorm.server.OrmException;
|
||||||
|
import com.google.inject.Inject;
|
||||||
|
import com.google.inject.assistedinject.Assisted;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@RequiresCapability(GlobalCapability.CREATE_ACCOUNT)
|
||||||
|
public class CreateAccount implements RestModifyView<TopLevelResource, Input> {
|
||||||
|
public static class Input {
|
||||||
|
@DefaultInput
|
||||||
|
public String username;
|
||||||
|
public String name;
|
||||||
|
public String email;
|
||||||
|
public String sshKey;
|
||||||
|
public String httpPassword;
|
||||||
|
public List<String> groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static interface Factory {
|
||||||
|
CreateAccount create(String username);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final ReviewDb db;
|
||||||
|
private final IdentifiedUser currentUser;
|
||||||
|
private final GroupsCollection groupsCollection;
|
||||||
|
private final SshKeyCache sshKeyCache;
|
||||||
|
private final AccountCache accountCache;
|
||||||
|
private final AccountByEmailCache byEmailCache;
|
||||||
|
private final String username;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
CreateAccount(ReviewDb db, IdentifiedUser currentUser,
|
||||||
|
GroupsCollection groupsCollection, SshKeyCache sshKeyCache,
|
||||||
|
AccountCache accountCache, AccountByEmailCache byEmailCache,
|
||||||
|
@Assisted String username) {
|
||||||
|
this.db = db;
|
||||||
|
this.currentUser = currentUser;
|
||||||
|
this.groupsCollection = groupsCollection;
|
||||||
|
this.sshKeyCache = sshKeyCache;
|
||||||
|
this.accountCache = accountCache;
|
||||||
|
this.byEmailCache = byEmailCache;
|
||||||
|
this.username = username;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object apply(TopLevelResource rsrc, Input input)
|
||||||
|
throws BadRequestException, ResourceConflictException,
|
||||||
|
UnprocessableEntityException, OrmException {
|
||||||
|
if (input == null) {
|
||||||
|
input = new Input();
|
||||||
|
}
|
||||||
|
if (input.username != null && !username.equals(input.username)) {
|
||||||
|
throw new BadRequestException("username must match URL");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!username.matches(Account.USER_NAME_PATTERN)) {
|
||||||
|
throw new BadRequestException("Username '" + username + "'"
|
||||||
|
+ " must contain only letters, numbers, _, - or .");
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<AccountGroup.Id> groups = parseGroups(input.groups);
|
||||||
|
|
||||||
|
Account.Id id = new Account.Id(db.nextAccountId());
|
||||||
|
AccountSshKey key = createSshKey(id, input.sshKey);
|
||||||
|
|
||||||
|
AccountExternalId extUser =
|
||||||
|
new AccountExternalId(id, new AccountExternalId.Key(
|
||||||
|
AccountExternalId.SCHEME_USERNAME, username));
|
||||||
|
|
||||||
|
if (input.httpPassword != null) {
|
||||||
|
extUser.setPassword(input.httpPassword);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (db.accountExternalIds().get(extUser.getKey()) != null) {
|
||||||
|
throw new ResourceConflictException(
|
||||||
|
"username '" + username + "' already exists");
|
||||||
|
}
|
||||||
|
if (input.email != null
|
||||||
|
&& db.accountExternalIds().get(getEmailKey(input.email)) != null) {
|
||||||
|
throw new UnprocessableEntityException(
|
||||||
|
"email '" + input.email + "' already exists");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
db.accountExternalIds().insert(Collections.singleton(extUser));
|
||||||
|
} catch (OrmDuplicateKeyException duplicateKey) {
|
||||||
|
throw new ResourceConflictException(
|
||||||
|
"username '" + username + "' already exists");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.email != null) {
|
||||||
|
AccountExternalId extMailto =
|
||||||
|
new AccountExternalId(id, getEmailKey(input.email));
|
||||||
|
extMailto.setEmailAddress(input.email);
|
||||||
|
try {
|
||||||
|
db.accountExternalIds().insert(Collections.singleton(extMailto));
|
||||||
|
} catch (OrmDuplicateKeyException duplicateKey) {
|
||||||
|
try {
|
||||||
|
db.accountExternalIds().delete(Collections.singleton(extUser));
|
||||||
|
} catch (OrmException cleanupError) {
|
||||||
|
}
|
||||||
|
throw new UnprocessableEntityException(
|
||||||
|
"email '" + input.email + "' already exists");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Account a = new Account(id);
|
||||||
|
a.setFullName(input.name);
|
||||||
|
a.setPreferredEmail(input.email);
|
||||||
|
db.accounts().insert(Collections.singleton(a));
|
||||||
|
|
||||||
|
if (key != null) {
|
||||||
|
db.accountSshKeys().insert(Collections.singleton(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (AccountGroup.Id groupId : groups) {
|
||||||
|
AccountGroupMember m =
|
||||||
|
new AccountGroupMember(new AccountGroupMember.Key(id, groupId));
|
||||||
|
db.accountGroupMembersAudit().insert(Collections.singleton(
|
||||||
|
new AccountGroupMemberAudit(m, currentUser.getAccountId())));
|
||||||
|
db.accountGroupMembers().insert(Collections.singleton(m));
|
||||||
|
}
|
||||||
|
|
||||||
|
sshKeyCache.evict(username);
|
||||||
|
accountCache.evictByUsername(username);
|
||||||
|
byEmailCache.evict(input.email);
|
||||||
|
|
||||||
|
return Response.created(AccountInfo.parse(a, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<AccountGroup.Id> parseGroups(List<String> groups)
|
||||||
|
throws UnprocessableEntityException {
|
||||||
|
Set<AccountGroup.Id> groupIds = Sets.newHashSet();
|
||||||
|
if (groups != null) {
|
||||||
|
for (String g : groups) {
|
||||||
|
groupIds.add(GroupDescriptions.toAccountGroup(
|
||||||
|
groupsCollection.parseInternal(g)).getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return groupIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
private AccountSshKey createSshKey(Account.Id id, String sshKey)
|
||||||
|
throws BadRequestException {
|
||||||
|
if (sshKey == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return sshKeyCache.create(new AccountSshKey.Id(id, 1), sshKey.trim());
|
||||||
|
} catch (InvalidSshKeyException e) {
|
||||||
|
throw new BadRequestException(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private AccountExternalId.Key getEmailKey(String email) {
|
||||||
|
return new AccountExternalId.Key(AccountExternalId.SCHEME_MAILTO, email);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,6 +19,7 @@ import static com.google.gerrit.server.account.AccountResource.CAPABILITY_KIND;
|
|||||||
|
|
||||||
import com.google.gerrit.extensions.registration.DynamicMap;
|
import com.google.gerrit.extensions.registration.DynamicMap;
|
||||||
import com.google.gerrit.extensions.restapi.RestApiModule;
|
import com.google.gerrit.extensions.restapi.RestApiModule;
|
||||||
|
import com.google.inject.assistedinject.FactoryModuleBuilder;
|
||||||
|
|
||||||
public class Module extends RestApiModule {
|
public class Module extends RestApiModule {
|
||||||
@Override
|
@Override
|
||||||
@@ -29,6 +30,7 @@ public class Module extends RestApiModule {
|
|||||||
DynamicMap.mapOf(binder(), ACCOUNT_KIND);
|
DynamicMap.mapOf(binder(), ACCOUNT_KIND);
|
||||||
DynamicMap.mapOf(binder(), CAPABILITY_KIND);
|
DynamicMap.mapOf(binder(), CAPABILITY_KIND);
|
||||||
|
|
||||||
|
put(ACCOUNT_KIND).to(PutAccount.class);
|
||||||
get(ACCOUNT_KIND).to(GetAccount.class);
|
get(ACCOUNT_KIND).to(GetAccount.class);
|
||||||
get(ACCOUNT_KIND, "avatar").to(GetAvatar.class);
|
get(ACCOUNT_KIND, "avatar").to(GetAvatar.class);
|
||||||
get(ACCOUNT_KIND, "avatar.change.url").to(GetAvatarChangeUrl.class);
|
get(ACCOUNT_KIND, "avatar.change.url").to(GetAvatarChangeUrl.class);
|
||||||
@@ -37,5 +39,7 @@ public class Module extends RestApiModule {
|
|||||||
get(ACCOUNT_KIND, "preferences.diff").to(GetDiffPreferences.class);
|
get(ACCOUNT_KIND, "preferences.diff").to(GetDiffPreferences.class);
|
||||||
put(ACCOUNT_KIND, "preferences.diff").to(SetDiffPreferences.class);
|
put(ACCOUNT_KIND, "preferences.diff").to(SetDiffPreferences.class);
|
||||||
get(CAPABILITY_KIND).to(GetCapabilities.CheckOne.class);
|
get(CAPABILITY_KIND).to(GetCapabilities.CheckOne.class);
|
||||||
|
|
||||||
|
install(new FactoryModuleBuilder().build(CreateAccount.Factory.class));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
// Copyright (C) 2013 The Android Open Source Project
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package com.google.gerrit.server.account;
|
||||||
|
|
||||||
|
import com.google.gerrit.extensions.restapi.ResourceConflictException;
|
||||||
|
import com.google.gerrit.extensions.restapi.RestModifyView;
|
||||||
|
import com.google.gerrit.server.account.CreateAccount.Input;
|
||||||
|
|
||||||
|
public class PutAccount implements RestModifyView<AccountResource, Input> {
|
||||||
|
@Override
|
||||||
|
public Object apply(AccountResource resource, Input input)
|
||||||
|
throws ResourceConflictException {
|
||||||
|
throw new ResourceConflictException("Account \"" + resource.getUser().getNameEmail()
|
||||||
|
+ "\" already exists");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,23 +14,16 @@
|
|||||||
|
|
||||||
package com.google.gerrit.sshd.commands;
|
package com.google.gerrit.sshd.commands;
|
||||||
|
|
||||||
|
import com.google.common.base.Function;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
import com.google.gerrit.common.data.GlobalCapability;
|
import com.google.gerrit.common.data.GlobalCapability;
|
||||||
import com.google.gerrit.common.errors.InvalidSshKeyException;
|
|
||||||
import com.google.gerrit.extensions.annotations.RequiresCapability;
|
import com.google.gerrit.extensions.annotations.RequiresCapability;
|
||||||
import com.google.gerrit.reviewdb.client.Account;
|
import com.google.gerrit.extensions.restapi.RestApiException;
|
||||||
import com.google.gerrit.reviewdb.client.AccountExternalId;
|
import com.google.gerrit.extensions.restapi.TopLevelResource;
|
||||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||||
import com.google.gerrit.reviewdb.client.AccountGroupMember;
|
import com.google.gerrit.server.account.CreateAccount;
|
||||||
import com.google.gerrit.reviewdb.client.AccountGroupMemberAudit;
|
|
||||||
import com.google.gerrit.reviewdb.client.AccountSshKey;
|
|
||||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
|
||||||
import com.google.gerrit.server.IdentifiedUser;
|
|
||||||
import com.google.gerrit.server.account.AccountByEmailCache;
|
|
||||||
import com.google.gerrit.server.account.AccountCache;
|
|
||||||
import com.google.gerrit.server.ssh.SshKeyCache;
|
|
||||||
import com.google.gerrit.sshd.CommandMetaData;
|
import com.google.gerrit.sshd.CommandMetaData;
|
||||||
import com.google.gerrit.sshd.SshCommand;
|
import com.google.gerrit.sshd.SshCommand;
|
||||||
import com.google.gwtorm.server.OrmDuplicateKeyException;
|
|
||||||
import com.google.gwtorm.server.OrmException;
|
import com.google.gwtorm.server.OrmException;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
|
|
||||||
@@ -42,8 +35,6 @@ import java.io.IOException;
|
|||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/** Create a new user account. **/
|
/** Create a new user account. **/
|
||||||
@@ -69,94 +60,29 @@ final class CreateAccountCommand extends SshCommand {
|
|||||||
private String username;
|
private String username;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private IdentifiedUser currentUser;
|
private CreateAccount.Factory createAccountFactory;
|
||||||
|
|
||||||
@Inject
|
|
||||||
private ReviewDb db;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
private SshKeyCache sshKeyCache;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
private AccountCache accountCache;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
private AccountByEmailCache byEmailCache;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void run() throws OrmException, IOException,
|
protected void run() throws OrmException, IOException, UnloggedFailure {
|
||||||
InvalidSshKeyException, UnloggedFailure {
|
CreateAccount.Input input = new CreateAccount.Input();
|
||||||
if (!username.matches(Account.USER_NAME_PATTERN)) {
|
input.username = username;
|
||||||
throw die("Username '" + username + "'"
|
input.email = email;
|
||||||
+ " must contain only letters, numbers, _, - or .");
|
input.name = fullName;
|
||||||
}
|
input.sshKey = readSshKey();
|
||||||
|
input.httpPassword = httpPassword;
|
||||||
final Account.Id id = new Account.Id(db.nextAccountId());
|
input.groups = Lists.transform(groups, new Function<AccountGroup.Id, String>() {
|
||||||
final AccountSshKey key = readSshKey(id);
|
@Override
|
||||||
|
public String apply(AccountGroup.Id id) {
|
||||||
AccountExternalId extUser =
|
return id.toString();
|
||||||
new AccountExternalId(id, new AccountExternalId.Key(
|
}});
|
||||||
AccountExternalId.SCHEME_USERNAME, username));
|
|
||||||
|
|
||||||
if (httpPassword != null) {
|
|
||||||
extUser.setPassword(httpPassword);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (db.accountExternalIds().get(extUser.getKey()) != null) {
|
|
||||||
throw die("username '" + username + "' already exists");
|
|
||||||
}
|
|
||||||
if (email != null && db.accountExternalIds().get(getEmailKey()) != null) {
|
|
||||||
throw die("email '" + email + "' already exists");
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
db.accountExternalIds().insert(Collections.singleton(extUser));
|
createAccountFactory.create(username).apply(TopLevelResource.INSTANCE, input);
|
||||||
} catch (OrmDuplicateKeyException duplicateKey) {
|
} catch (RestApiException e) {
|
||||||
throw die("username '" + username + "' already exists");
|
throw die(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (email != null) {
|
|
||||||
AccountExternalId extMailto = new AccountExternalId(id, getEmailKey());
|
|
||||||
extMailto.setEmailAddress(email);
|
|
||||||
try {
|
|
||||||
db.accountExternalIds().insert(Collections.singleton(extMailto));
|
|
||||||
} catch (OrmDuplicateKeyException duplicateKey) {
|
|
||||||
try {
|
|
||||||
db.accountExternalIds().delete(Collections.singleton(extUser));
|
|
||||||
} catch (OrmException cleanupError) {
|
|
||||||
}
|
|
||||||
throw die("email '" + email + "' already exists");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Account a = new Account(id);
|
|
||||||
a.setFullName(fullName);
|
|
||||||
a.setPreferredEmail(email);
|
|
||||||
db.accounts().insert(Collections.singleton(a));
|
|
||||||
|
|
||||||
if (key != null) {
|
|
||||||
db.accountSshKeys().insert(Collections.singleton(key));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (AccountGroup.Id groupId : new HashSet<AccountGroup.Id>(groups)) {
|
|
||||||
AccountGroupMember m =
|
|
||||||
new AccountGroupMember(new AccountGroupMember.Key(id, groupId));
|
|
||||||
db.accountGroupMembersAudit().insert(Collections.singleton( //
|
|
||||||
new AccountGroupMemberAudit(m, currentUser.getAccountId())));
|
|
||||||
db.accountGroupMembers().insert(Collections.singleton(m));
|
|
||||||
}
|
|
||||||
|
|
||||||
sshKeyCache.evict(username);
|
|
||||||
accountCache.evictByUsername(username);
|
|
||||||
byEmailCache.evict(email);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private AccountExternalId.Key getEmailKey() {
|
private String readSshKey() throws UnsupportedEncodingException, IOException {
|
||||||
return new AccountExternalId.Key(AccountExternalId.SCHEME_MAILTO, email);
|
|
||||||
}
|
|
||||||
|
|
||||||
private AccountSshKey readSshKey(final Account.Id id)
|
|
||||||
throws UnsupportedEncodingException, IOException, InvalidSshKeyException {
|
|
||||||
if (sshKey == null) {
|
if (sshKey == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -169,6 +95,6 @@ final class CreateAccountCommand extends SshCommand {
|
|||||||
sshKey += line + "\n";
|
sshKey += line + "\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return sshKeyCache.create(new AccountSshKey.Id(id, 1), sshKey.trim());
|
return sshKey;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user