/**
* Yobi, Project Hosting SW
*
* Copyright 2013 NAVER Corp.
* http://yobi.io
*
* @Author Keesun Baik
*
* 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 controllers;
import models.*;
import models.enumeration.RoleType;
import play.data.Form;
import play.db.ebean.Transactional;
import play.mvc.Controller;
import play.mvc.Result;
import play.mvc.With;
import play.i18n.Messages;
import playRepository.GitRepository;
import utils.AccessControl;
import utils.ErrorViews;
import utils.FileUtil;
import utils.ValidationResult;
import views.html.project.create;
import views.html.project.importing;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.jgit.api.errors.*;
import org.eclipse.jgit.internal.JGitText;
import actions.AnonymousCheckAction;
import java.io.IOException;
import java.io.File;
import java.util.List;
import static play.data.Form.form;
public class ImportApp extends Controller {
@With(AnonymousCheckAction.class)
public static Result importForm() {
Form<Project> projectForm = form(Project.class).bindFromRequest("owner");
projectForm.discardErrors();
List<OrganizationUser> orgUserList = OrganizationUser.findByAdmin(UserApp.currentUser().id);
return ok(importing.render("title.newProject", projectForm, orgUserList));
}
@Transactional
public static Result newProject() throws GitAPIException, IOException {
if( !AccessControl.isGlobalResourceCreatable(UserApp.currentUser()) ){
return forbidden("'" + UserApp.currentUser().name + "' has no permission");
}
Form<Project> filledNewProjectForm = form(Project.class).bindFromRequest();
String owner = filledNewProjectForm.field("owner").value();
Organization organization = Organization.findByName(owner);
User user = User.findByLoginId(owner);
ValidationResult result = validateForm(filledNewProjectForm, organization, user);
if (result.hasError()) {
return result.getResult();
}
String gitUrl = filledNewProjectForm.data().get("url");
Project project = filledNewProjectForm.get();
if (Organization.isNameExist(owner)) {
project.organization = organization;
}
String authId = filledNewProjectForm.field("authId").value();
String authPw = filledNewProjectForm.field("authPw").value();
boolean hasNoCredentials = StringUtils.isEmpty(authId) && StringUtils.isEmpty(authPw);
try {
if(hasNoCredentials){
GitRepository.cloneRepository(gitUrl, project);
} else {
GitRepository.cloneRepository(gitUrl, project, authId, authPw);
}
Long projectId = Project.create(project);
saveProjectMenuSetting(project);
if (User.isLoginIdExist(owner)) {
ProjectUser.assignRole(UserApp.currentUser().id, projectId, RoleType.MANAGER);
}
} catch (InvalidRemoteException e) {
// It is not an url.
filledNewProjectForm.reject("url", "project.import.error.wrong.url");
} catch (JGitInternalException e) {
// The url seems that does not locate a git repository.
filledNewProjectForm.reject("url", "project.import.error.wrong.url");
} catch (TransportException e) {
addDetailedTransportErrorMessage(filledNewProjectForm, e, hasNoCredentials);
}
if (!filledNewProjectForm.errors().isEmpty()) {
List<OrganizationUser> orgUserList = OrganizationUser.findByAdmin(UserApp.currentUser().id);
FileUtil.rm_rf(new File(GitRepository.getGitDirectory(project)));
return badRequest(importing.render("title.newProject", filledNewProjectForm, orgUserList));
} else {
return redirect(routes.ProjectApp.project(project.owner, project.name));
}
}
private static void saveProjectMenuSetting(Project project) {
Form<ProjectMenuSetting> filledUpdatedProjectMenuSettingForm = form(ProjectMenuSetting.class).bindFromRequest();
ProjectMenuSetting updatedProjectMenuSetting = filledUpdatedProjectMenuSettingForm.get();
project.refresh();
updatedProjectMenuSetting.project = project;
if (project.menuSetting == null) {
updatedProjectMenuSetting.save();
} else {
updatedProjectMenuSetting.id = project.menuSetting.id;
updatedProjectMenuSetting.update();
}
}
/**
* Add assorted error messages from TransportException like Unauthorized(401), Forbidden(403)
* or other transport error with HTTP response code to the given form.
*
* Referenced TransportHttp.java of jGit which throws TransportException while connect to remote repository.
*
* @see https://github.com/eclipse/jgit/blob/4cb0bd8/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java#L491
* @param filledNewProjectForm
* @param e
* @param hasNoCredentials
* @return
*/
private static void addDetailedTransportErrorMessage(Form<Project> filledNewProjectForm, TransportException e, boolean hasNoCredentials){
String errorMessage = e.getMessage();
// HttpConnection.HTTP_UNAUTHORIZED : 401
if(errorMessage.contains(JGitText.get().notAuthorized)){
if(hasNoCredentials){
filledNewProjectForm.reject("repoAuth", "required");
filledNewProjectForm.reject("url", "project.import.error.transport.unauthorized");
} else {
filledNewProjectForm.reject("authId", "project.import.error.transport.failedToAuth");
}
} else if(errorMessage.contains(java.text.MessageFormat.format(JGitText.get().serviceNotPermitted, ""))){
// HttpConnection.HTTP_FORBIDDEN : 403
filledNewProjectForm.reject("url", "project.import.error.transport.forbidden");
} else {
// and for other errors
String statusCode = errorMessage.split(" ")[1]; // 0 = URL, 1 = ResponseCode, 2 = ResponseMessage
filledNewProjectForm.reject("url", Messages.get("project.import.error.transport", statusCode));
}
}
private static ValidationResult validateForm(Form<Project> newProjectForm, Organization organization, User user) {
boolean hasError = false;
Result result = null;
List<OrganizationUser> orgUserList = OrganizationUser.findByAdmin(UserApp.currentUser().id);
String owner = newProjectForm.field("owner").value();
String name = newProjectForm.field("name").value();
boolean ownerIsUser = User.isLoginIdExist(owner);
boolean ownerIsOrganization = Organization.isNameExist(owner);
if (!ownerIsUser && !ownerIsOrganization) {
newProjectForm.reject("owner", "project.owner.invalidate");
hasError = true;
result = badRequest(create.render("title.newProject", newProjectForm, orgUserList));
}
if (ownerIsUser && UserApp.currentUser().id != user.id) {
newProjectForm.reject("owner", "project.owner.invalidate");
hasError = true;
result = badRequest(create.render("title.newProject", newProjectForm, orgUserList));
}
if (ownerIsOrganization && !OrganizationUser.isAdmin(organization.id, UserApp.currentUser().id)) {
hasError = true;
result = forbidden(ErrorViews.Forbidden.render("'" + UserApp.currentUser().name + "' has no permission"));
}
if (Project.exists(owner, name)) {
newProjectForm.reject("name", "project.name.duplicate");
hasError = true;
result = badRequest(importing.render("title.newProject", newProjectForm, orgUserList));
}
String gitUrl = StringUtils.trim(newProjectForm.data().get("url"));
if (StringUtils.isBlank(gitUrl)) {
newProjectForm.reject("url", "project.import.error.empty.url");
hasError = true;
result = badRequest(importing.render("title.newProject", newProjectForm, orgUserList));
}
if (newProjectForm.hasErrors()) {
newProjectForm.reject("name", "project.name.alert");
hasError = true;
result = badRequest(importing.render("title.newProject", newProjectForm, orgUserList));
}
return new ValidationResult(result, hasError);
}
}