View Javadoc
1   /*
2    * Copyright (C) 2019 Nail Samatov <sanail@yandex.ru>
3    * and other copyright owners as documented in the project's IP log.
4    *
5    * This program and the accompanying materials are made available
6    * under the terms of the Eclipse Distribution License v1.0 which
7    * accompanies this distribution, is reproduced below, and is
8    * available at http://www.eclipse.org/org/documents/edl-v10.php
9    *
10   * All rights reserved.
11   *
12   * Redistribution and use in source and binary forms, with or
13   * without modification, are permitted provided that the following
14   * conditions are met:
15   *
16   * - Redistributions of source code must retain the above copyright
17   *   notice, this list of conditions and the following disclaimer.
18   *
19   * - Redistributions in binary form must reproduce the above
20   *   copyright notice, this list of conditions and the following
21   *   disclaimer in the documentation and/or other materials provided
22   *   with the distribution.
23   *
24   * - Neither the name of the Eclipse Foundation, Inc. nor the
25   *   names of its contributors may be used to endorse or promote
26   *   products derived from this software without specific prior
27   *   written permission.
28   *
29   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
30   * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
31   * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
32   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
34   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
36   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
38   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
40   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
41   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42   */
43  package org.eclipse.jgit.api;
44  
45  import static org.junit.Assert.assertEquals;
46  import static org.junit.Assert.assertNotNull;
47  import static org.junit.Assert.assertTrue;
48  
49  import java.io.File;
50  import java.io.FilePermission;
51  import java.io.IOException;
52  import java.lang.reflect.ReflectPermission;
53  import java.nio.file.Files;
54  import java.security.Permission;
55  import java.security.SecurityPermission;
56  import java.util.ArrayList;
57  import java.util.List;
58  import java.util.PropertyPermission;
59  import java.util.logging.LoggingPermission;
60  
61  import javax.security.auth.AuthPermission;
62  
63  import org.eclipse.jgit.api.errors.GitAPIException;
64  import org.eclipse.jgit.junit.JGitTestUtil;
65  import org.eclipse.jgit.junit.MockSystemReader;
66  import org.eclipse.jgit.junit.SeparateClassloaderTestRunner;
67  import org.eclipse.jgit.revwalk.RevCommit;
68  import org.eclipse.jgit.treewalk.TreeWalk;
69  import org.eclipse.jgit.util.FileUtils;
70  import org.eclipse.jgit.util.SystemReader;
71  import org.junit.After;
72  import org.junit.Before;
73  import org.junit.Test;
74  import org.junit.runner.RunWith;
75  
76  /**
77   * <p>
78   * Tests if jgit works if SecurityManager is enabled.
79   * </p>
80   *
81   * <p>
82   * Note: JGit's classes shouldn't be used before SecurityManager is configured.
83   * If you use some JGit's class before SecurityManager is replaced then part of
84   * the code can be invoked outside of our custom SecurityManager and this test
85   * becomes useless.
86   * </p>
87   *
88   * <p>
89   * For example the class {@link org.eclipse.jgit.util.FS} is used widely in jgit
90   * sources. It contains DETECTED static field. At the first usage of the class
91   * FS the field DETECTED is initialized and during initialization many system
92   * operations that SecurityManager can forbid are invoked.
93   * </p>
94   *
95   * <p>
96   * For this reason this test doesn't extend LocalDiskRepositoryTestCase (it uses
97   * JGit's classes in setUp() method) and other JGit's utility classes. It's done
98   * to affect SecurityManager as less as possible.
99   * </p>
100  *
101  * <p>
102  * We use SeparateClassloaderTestRunner to isolate FS.DETECTED field
103  * initialization between different tests run.
104  * </p>
105  */
106 @RunWith(SeparateClassloaderTestRunner.class)
107 public class SecurityManagerTest {
108 	private File root;
109 
110 	private SecurityManager originalSecurityManager;
111 
112 	private List<Permission> permissions = new ArrayList<>();
113 
114 	@Before
115 	public void setUp() throws Exception {
116 		// Create working directory
117 		SystemReader.setInstance(new MockSystemReader());
118 		root = Files.createTempDirectory("jgit-security").toFile();
119 
120 		// Add system permissions
121 		permissions.add(new RuntimePermission("*"));
122 		permissions.add(new SecurityPermission("*"));
123 		permissions.add(new AuthPermission("*"));
124 		permissions.add(new ReflectPermission("*"));
125 		permissions.add(new PropertyPermission("*", "read,write"));
126 		permissions.add(new LoggingPermission("control", null));
127 
128 		permissions.add(new FilePermission(
129 				System.getProperty("java.home") + "/-", "read"));
130 
131 		String tempDir = System.getProperty("java.io.tmpdir");
132 		permissions.add(new FilePermission(tempDir, "read,write,delete"));
133 		permissions
134 				.add(new FilePermission(tempDir + "/-", "read,write,delete"));
135 
136 		// Add permissions to dependent jar files.
137 		String classPath = System.getProperty("java.class.path");
138 		if (classPath != null) {
139 			for (String path : classPath.split(File.pathSeparator)) {
140 				permissions.add(new FilePermission(path, "read"));
141 			}
142 		}
143 		// Add permissions to jgit class files.
144 		String jgitSourcesRoot = new File(System.getProperty("user.dir"))
145 				.getParent();
146 		permissions.add(new FilePermission(jgitSourcesRoot + "/-", "read"));
147 
148 		// Add permissions to working dir for jgit. Our git repositories will be
149 		// initialized and cloned here.
150 		permissions.add(new FilePermission(root.getPath() + "/-",
151 				"read,write,delete,execute"));
152 
153 		// Replace Security Manager
154 		originalSecurityManager = System.getSecurityManager();
155 		System.setSecurityManager(new SecurityManager() {
156 
157 			@Override
158 			public void checkPermission(Permission requested) {
159 				for (Permission permission : permissions) {
160 					if (permission.implies(requested)) {
161 						return;
162 					}
163 				}
164 
165 				super.checkPermission(requested);
166 			}
167 		});
168 	}
169 
170 	@After
171 	public void tearDown() throws Exception {
172 		System.setSecurityManager(originalSecurityManager);
173 
174 		// Note: don't use this method before security manager is replaced in
175 		// setUp() method. The method uses FS.DETECTED internally and can affect
176 		// the test.
177 		FileUtils.delete(root, FileUtils.RECURSIVE | FileUtils.RETRY);
178 	}
179 
180 	@Test
181 	public void testInitAndClone() throws IOException, GitAPIException {
182 		File remote = new File(root, "remote");
183 		File local = new File(root, "local");
184 
185 		try (Git git = Git.init().setDirectory(remote).call()) {
186 			JGitTestUtil.write(new File(remote, "hello.txt"), "Hello world!");
187 			git.add().addFilepattern(".").call();
188 			git.commit().setMessage("Initial commit").call();
189 		}
190 
191 		try (Git git = Git.cloneRepository().setURI(remote.toURI().toString())
192 				.setDirectory(local).call()) {
193 			assertTrue(new File(local, ".git").exists());
194 
195 			JGitTestUtil.write(new File(local, "hi.txt"), "Hi!");
196 			git.add().addFilepattern(".").call();
197 			RevCommit commit1 = git.commit().setMessage("Commit on local repo")
198 					.call();
199 			assertEquals("Commit on local repo", commit1.getFullMessage());
200 			assertNotNull(TreeWalk.forPath(git.getRepository(), "hello.txt",
201 					commit1.getTree()));
202 		}
203 
204 	}
205 
206 }