View Javadoc
1   /*
2    * Copyright (C) 2019 Nail Samatov <sanail@yandex.ru> and others
3    *
4    * This program and the accompanying materials are made available under the
5    * terms of the Eclipse Distribution License v. 1.0 which is available at
6    * https://www.eclipse.org/org/documents/edl-v10.php.
7    *
8    * SPDX-License-Identifier: BSD-3-Clause
9    */
10  package org.eclipse.jgit.api;
11  
12  import static org.junit.Assert.assertEquals;
13  import static org.junit.Assert.assertNotNull;
14  import static org.junit.Assert.assertTrue;
15  
16  import java.io.File;
17  import java.io.FilePermission;
18  import java.io.IOException;
19  import java.lang.reflect.ReflectPermission;
20  import java.nio.file.Files;
21  import java.security.Permission;
22  import java.security.SecurityPermission;
23  import java.util.ArrayList;
24  import java.util.List;
25  import java.util.PropertyPermission;
26  import java.util.logging.LoggingPermission;
27  
28  import javax.security.auth.AuthPermission;
29  
30  import org.eclipse.jgit.api.errors.GitAPIException;
31  import org.eclipse.jgit.junit.JGitTestUtil;
32  import org.eclipse.jgit.junit.MockSystemReader;
33  import org.eclipse.jgit.junit.SeparateClassloaderTestRunner;
34  import org.eclipse.jgit.revwalk.RevCommit;
35  import org.eclipse.jgit.treewalk.TreeWalk;
36  import org.eclipse.jgit.util.FileUtils;
37  import org.eclipse.jgit.util.SystemReader;
38  import org.junit.After;
39  import org.junit.Before;
40  import org.junit.Test;
41  import org.junit.runner.RunWith;
42  
43  /**
44   * <p>
45   * Tests if jgit works if SecurityManager is enabled.
46   * </p>
47   *
48   * <p>
49   * Note: JGit's classes shouldn't be used before SecurityManager is configured.
50   * If you use some JGit's class before SecurityManager is replaced then part of
51   * the code can be invoked outside of our custom SecurityManager and this test
52   * becomes useless.
53   * </p>
54   *
55   * <p>
56   * For example the class {@link org.eclipse.jgit.util.FS} is used widely in jgit
57   * sources. It contains DETECTED static field. At the first usage of the class
58   * FS the field DETECTED is initialized and during initialization many system
59   * operations that SecurityManager can forbid are invoked.
60   * </p>
61   *
62   * <p>
63   * For this reason this test doesn't extend LocalDiskRepositoryTestCase (it uses
64   * JGit's classes in setUp() method) and other JGit's utility classes. It's done
65   * to affect SecurityManager as less as possible.
66   * </p>
67   *
68   * <p>
69   * We use SeparateClassloaderTestRunner to isolate FS.DETECTED field
70   * initialization between different tests run.
71   * </p>
72   */
73  @RunWith(SeparateClassloaderTestRunner.class)
74  public class SecurityManagerTest {
75  	private File root;
76  
77  	private SecurityManager originalSecurityManager;
78  
79  	private List<Permission> permissions = new ArrayList<>();
80  
81  	@Before
82  	public void setUp() throws Exception {
83  		// Create working directory
84  		SystemReader.setInstance(new MockSystemReader());
85  		root = Files.createTempDirectory("jgit-security").toFile();
86  
87  		// Add system permissions
88  		permissions.add(new RuntimePermission("*"));
89  		permissions.add(new SecurityPermission("*"));
90  		permissions.add(new AuthPermission("*"));
91  		permissions.add(new ReflectPermission("*"));
92  		permissions.add(new PropertyPermission("*", "read,write"));
93  		permissions.add(new LoggingPermission("control", null));
94  
95  		permissions.add(new FilePermission(
96  				System.getProperty("java.home") + "/-", "read"));
97  
98  		String tempDir = System.getProperty("java.io.tmpdir");
99  		permissions.add(new FilePermission(tempDir, "read,write,delete"));
100 		permissions
101 				.add(new FilePermission(tempDir + "/-", "read,write,delete"));
102 
103 		// Add permissions to dependent jar files.
104 		String classPath = System.getProperty("java.class.path");
105 		if (classPath != null) {
106 			for (String path : classPath.split(File.pathSeparator)) {
107 				permissions.add(new FilePermission(path, "read"));
108 			}
109 		}
110 		// Add permissions to jgit class files.
111 		String jgitSourcesRoot = new File(System.getProperty("user.dir"))
112 				.getParent();
113 		permissions.add(new FilePermission(jgitSourcesRoot + "/-", "read"));
114 
115 		// Add permissions to working dir for jgit. Our git repositories will be
116 		// initialized and cloned here.
117 		permissions.add(new FilePermission(root.getPath() + "/-",
118 				"read,write,delete,execute"));
119 
120 		// Replace Security Manager
121 		originalSecurityManager = System.getSecurityManager();
122 		System.setSecurityManager(new SecurityManager() {
123 
124 			@Override
125 			public void checkPermission(Permission requested) {
126 				for (Permission permission : permissions) {
127 					if (permission.implies(requested)) {
128 						return;
129 					}
130 				}
131 
132 				super.checkPermission(requested);
133 			}
134 		});
135 	}
136 
137 	@After
138 	public void tearDown() throws Exception {
139 		System.setSecurityManager(originalSecurityManager);
140 
141 		// Note: don't use this method before security manager is replaced in
142 		// setUp() method. The method uses FS.DETECTED internally and can affect
143 		// the test.
144 		FileUtils.delete(root, FileUtils.RECURSIVE | FileUtils.RETRY);
145 	}
146 
147 	@Test
148 	public void testInitAndClone() throws IOException, GitAPIException {
149 		File remote = new File(root, "remote");
150 		File local = new File(root, "local");
151 
152 		try (Git git = Git.init().setDirectory(remote).call()) {
153 			JGitTestUtil.write(new File(remote, "hello.txt"), "Hello world!");
154 			git.add().addFilepattern(".").call();
155 			git.commit().setMessage("Initial commit").call();
156 		}
157 
158 		try (Git git = Git.cloneRepository().setURI(remote.toURI().toString())
159 				.setDirectory(local).call()) {
160 			assertTrue(new File(local, ".git").exists());
161 
162 			JGitTestUtil.write(new File(local, "hi.txt"), "Hi!");
163 			git.add().addFilepattern(".").call();
164 			RevCommit commit1 = git.commit().setMessage("Commit on local repo")
165 					.call();
166 			assertEquals("Commit on local repo", commit1.getFullMessage());
167 			assertNotNull(TreeWalk.forPath(git.getRepository(), "hello.txt",
168 					commit1.getTree()));
169 		}
170 
171 	}
172 
173 }