James Moger
2013-06-24 5abe3350a38b3cdfc28ac2839860d28b88f2306a
commit | author | age
8c9a20 1 /*
JM 2  * Copyright 2011 gitblit.com.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.gitblit;
17
18 import java.text.MessageFormat;
19
20 import com.gitblit.Constants.AccessRestrictionType;
72cb19 21 import com.gitblit.Constants.AuthorizationControl;
8c9a20 22 import com.gitblit.models.RepositoryModel;
JM 23 import com.gitblit.models.UserModel;
24 import com.gitblit.utils.StringUtils;
25
892570 26 /**
JM 27  * The GitFilter is an AccessRestrictionFilter which ensures that Git client
28  * requests for push, clone, or view restricted repositories are authenticated
29  * and authorized.
30  * 
31  * @author James Moger
32  * 
33  */
8c9a20 34 public class GitFilter extends AccessRestrictionFilter {
JM 35
756117 36     protected static final String gitReceivePack = "/git-receive-pack";
8c9a20 37
756117 38     protected static final String gitUploadPack = "/git-upload-pack";
8c9a20 39
756117 40     protected static final String[] suffixes = { gitReceivePack, gitUploadPack, "/info/refs", "/HEAD",
8c9a20 41             "/objects" };
756117 42
JM 43     /**
44      * Extract the repository name from the url.
45      * 
ffbf03 46      * @param cloneUrl
756117 47      * @return repository name
JM 48      */
49     public static String getRepositoryName(String value) {
50         String repository = value;
51         // get the repository name from the url by finding a known url suffix
52         for (String urlSuffix : suffixes) {
53             if (repository.indexOf(urlSuffix) > -1) {
54                 repository = repository.substring(0, repository.indexOf(urlSuffix));
55             }
56         }
57         return repository;
58     }
8c9a20 59
892570 60     /**
JM 61      * Extract the repository name from the url.
62      * 
63      * @param url
64      * @return repository name
65      */
8c9a20 66     @Override
JM 67     protected String extractRepositoryName(String url) {
756117 68         return GitFilter.getRepositoryName(url);
8c9a20 69     }
JM 70
892570 71     /**
JM 72      * Analyze the url and returns the action of the request. Return values are
73      * either "/git-receive-pack" or "/git-upload-pack".
74      * 
831469 75      * @param serverUrl
892570 76      * @return action of the request
JM 77      */
8c9a20 78     @Override
892570 79     protected String getUrlRequestAction(String suffix) {
8c9a20 80         if (!StringUtils.isEmpty(suffix)) {
JM 81             if (suffix.startsWith(gitReceivePack)) {
82                 return gitReceivePack;
83             } else if (suffix.startsWith(gitUploadPack)) {
84                 return gitUploadPack;
85             } else if (suffix.contains("?service=git-receive-pack")) {
86                 return gitReceivePack;
87             } else if (suffix.contains("?service=git-upload-pack")) {
88                 return gitUploadPack;
d40adc 89             } else {
JM 90                 return gitUploadPack;
8c9a20 91             }
JM 92         }
93         return null;
72cb19 94     }
JM 95     
96     /**
97      * Determine if a non-existing repository can be created using this filter.
98      *  
99      * @return true if the server allows repository creation on-push
100      */
101     @Override
102     protected boolean isCreationAllowed() {
103         return GitBlit.getBoolean(Keys.git.allowCreateOnPush, true);
8c9a20 104     }
b74031 105     
JM 106     /**
107      * Determine if the repository can receive pushes.
108      * 
109      * @param repository
110      * @param action
111      * @return true if the action may be performed
112      */
113     @Override
114     protected boolean isActionAllowed(RepositoryModel repository, String action) {
75bca8 115         // the log here has been moved into ReceiveHook to provide clients with
JM 116         // error messages
b74031 117         return true;
JM 118     }
8c9a20 119
3983a6 120     @Override
JM 121     protected boolean requiresClientCertificate() {
122         return GitBlit.getBoolean(Keys.git.requiresClientCertificate, false);
123     }
124
892570 125     /**
JM 126      * Determine if the repository requires authentication.
127      * 
128      * @param repository
3f8cd4 129      * @param action
892570 130      * @return true if authentication required
JM 131      */
8c9a20 132     @Override
3f8cd4 133     protected boolean requiresAuthentication(RepositoryModel repository, String action) {
JM 134         if (gitUploadPack.equals(action)) {
135             // send to client
136             return repository.accessRestriction.atLeast(AccessRestrictionType.CLONE);    
137         } else if (gitReceivePack.equals(action)) {
138             // receive from client
139             return repository.accessRestriction.atLeast(AccessRestrictionType.PUSH);
140         }
141         return false;
8c9a20 142     }
JM 143
892570 144     /**
JM 145      * Determine if the user can access the repository and perform the specified
146      * action.
147      * 
148      * @param repository
149      * @param user
150      * @param action
151      * @return true if user may execute the action on the repository
152      */
8c9a20 153     @Override
892570 154     protected boolean canAccess(RepositoryModel repository, UserModel user, String action) {
5450d0 155         if (!GitBlit.getBoolean(Keys.git.enableGitServlet, true)) {
JM 156             // Git Servlet disabled
157             return false;
b74031 158         }        
20714a 159         if (action.equals(gitReceivePack)) {
JM 160             // Push request
161             if (user.canPush(repository)) {
162                 return true;
163             } else {
164                 // user is unauthorized to push to this repository
165                 logger.warn(MessageFormat.format("user {0} is not authorized to push to {1}",
166                         user.username, repository));
167                 return false;
168             }
169         } else if (action.equals(gitUploadPack)) {
170             // Clone request
171             if (user.canClone(repository)) {
172                 return true;
173             } else {
174                 // user is unauthorized to clone this repository
175                 logger.warn(MessageFormat.format("user {0} is not authorized to clone {1}",
176                         user.username, repository));
177                 return false;
8c9a20 178             }
JM 179         }
180         return true;
181     }
72cb19 182     
JM 183     /**
184      * An authenticated user with the CREATE role can create a repository on
185      * push.
186      * 
187      * @param user
188      * @param repository
189      * @param action
190      * @return the repository model, if it is created, null otherwise
191      */
192     @Override
193     protected RepositoryModel createRepository(UserModel user, String repository, String action) {
194         boolean isPush = !StringUtils.isEmpty(action) && gitReceivePack.equals(action);
195         if (isPush) {
ec7ac2 196             if (user.canCreate(repository)) {
72cb19 197                 // user is pushing to a new repository
3e44b6 198                 // validate name
JM 199                 if (repository.startsWith("../")) {
200                     logger.error(MessageFormat.format("Illegal relative path in repository name! {0}", repository));
201                     return null;
202                 }
203                 if (repository.contains("/../")) {
204                     logger.error(MessageFormat.format("Illegal relative path in repository name! {0}", repository));
205                     return null;
206                 }                    
207
208                 // confirm valid characters in repository name
209                 Character c = StringUtils.findInvalidCharacter(repository);
210                 if (c != null) {
211                     logger.error(MessageFormat.format("Invalid character '{0}' in repository name {1}!", c, repository));
212                     return null;
213                 }
214
215                 // create repository
72cb19 216                 RepositoryModel model = new RepositoryModel();
JM 217                 model.name = repository;
661db6 218                 model.addOwner(user.username);
72cb19 219                 model.projectPath = StringUtils.getFirstPathElement(repository);
JM 220                 if (model.isUsersPersonalRepository(user.username)) {
221                     // personal repository, default to private for user
222                     model.authorizationControl = AuthorizationControl.NAMED;
223                     model.accessRestriction = AccessRestrictionType.VIEW;
224                 } else {
225                     // common repository, user default server settings
226                     model.authorizationControl = AuthorizationControl.fromName(GitBlit.getString(Keys.git.defaultAuthorizationControl, ""));
227                     model.accessRestriction = AccessRestrictionType.fromName(GitBlit.getString(Keys.git.defaultAccessRestriction, ""));
228                 }
229
230                 // create the repository
231                 try {
3e44b6 232                     GitBlit.self().updateRepositoryModel(model.name, model, true);
JM 233                     logger.info(MessageFormat.format("{0} created {1} ON-PUSH", user.username, model.name));
234                     return GitBlit.self().getRepositoryModel(model.name);
72cb19 235                 } catch (GitBlitException e) {
3e44b6 236                     logger.error(MessageFormat.format("{0} failed to create repository {1} ON-PUSH!", user.username, model.name), e);
72cb19 237                 }
JM 238             } else {
239                 logger.warn(MessageFormat.format("{0} is not permitted to create repository {1} ON-PUSH!", user.username, repository));
240             }
241         }
242         
243         // repository could not be created or action was not a push
244         return null;
245     }
8c9a20 246 }