/* * Copyright 2011 gitblit.com. * * 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.gitblit; import java.io.File; import java.io.IOException; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import org.eclipse.jgit.lib.StoredConfig; import org.eclipse.jgit.storage.file.FileBasedConfig; import org.eclipse.jgit.util.FS; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.gitblit.Constants.AccessPermission; import com.gitblit.Constants.AccountType; import com.gitblit.Constants.Role; import com.gitblit.Constants.Transport; import com.gitblit.manager.IRuntimeManager; import com.gitblit.models.TeamModel; import com.gitblit.models.UserModel; import com.gitblit.models.UserRepositoryPreferences; import com.gitblit.utils.ArrayUtils; import com.gitblit.utils.DeepCopier; import com.gitblit.utils.StringUtils; /** * ConfigUserService is Gitblit's default user service implementation since * version 0.8.0. * * Users and their repository memberships are stored in a git-style config file * which is cached and dynamically reloaded when modified. This file is * plain-text, human-readable, and may be edited with a text editor. * * Additionally, this format allows for expansion of the user model without * bringing in the complexity of a database. * * @author James Moger * */ public class ConfigUserService implements IUserService { private static final String TEAM = "team"; private static final String USER = "user"; private static final String PASSWORD = "password"; private static final String DISPLAYNAME = "displayName"; private static final String EMAILADDRESS = "emailAddress"; private static final String ORGANIZATIONALUNIT = "organizationalUnit"; private static final String ORGANIZATION = "organization"; private static final String LOCALITY = "locality"; private static final String STATEPROVINCE = "stateProvince"; private static final String COUNTRYCODE = "countryCode"; private static final String COOKIE = "cookie"; private static final String REPOSITORY = "repository"; private static final String ROLE = "role"; private static final String MAILINGLIST = "mailingList"; private static final String PRERECEIVE = "preReceiveScript"; private static final String POSTRECEIVE = "postReceiveScript"; private static final String STARRED = "starred"; private static final String LOCALE = "locale"; private static final String EMAILONMYTICKETCHANGES = "emailMeOnMyTicketChanges"; private static final String TRANSPORT = "transport"; private static final String ACCOUNTTYPE = "accountType"; private static final String DISABLED = "disabled"; private final File realmFile; private final Logger logger = LoggerFactory.getLogger(ConfigUserService.class); private final Map users = new ConcurrentHashMap(); private final Map cookies = new ConcurrentHashMap(); private final Map teams = new ConcurrentHashMap(); private volatile long lastModified; private volatile boolean forceReload; public ConfigUserService(File realmFile) { this.realmFile = realmFile; } /** * Setup the user service. * * @param runtimeManager * @since 1.4.0 */ @Override public void setup(IRuntimeManager runtimeManager) { } /** * Returns the cookie value for the specified user. * * @param model * @return cookie value */ @Override public synchronized String getCookie(UserModel model) { if (!StringUtils.isEmpty(model.cookie)) { return model.cookie; } UserModel storedModel = getUserModel(model.username); if (storedModel == null) { return null; } return storedModel.cookie; } /** * Gets the user object for the specified cookie. * * @param cookie * @return a user object or null */ @Override public synchronized UserModel getUserModel(char[] cookie) { String hash = new String(cookie); if (StringUtils.isEmpty(hash)) { return null; } read(); UserModel model = null; if (cookies.containsKey(hash)) { model = cookies.get(hash); } if (model != null) { // clone the model, otherwise all changes to this object are // live and unpersisted model = DeepCopier.copy(model); } return model; } /** * Retrieve the user object for the specified username. * * @param username * @return a user object or null */ @Override public synchronized UserModel getUserModel(String username) { read(); UserModel model = users.get(username.toLowerCase()); if (model != null) { // clone the model, otherwise all changes to this object are // live and unpersisted model = DeepCopier.copy(model); } return model; } /** * Updates/writes a complete user object. * * @param model * @return true if update is successful */ @Override public synchronized boolean updateUserModel(UserModel model) { return updateUserModel(model.username, model); } /** * Updates/writes all specified user objects. * * @param models a list of user models * @return true if update is successful * @since 1.2.0 */ @Override public synchronized boolean updateUserModels(Collection models) { try { read(); for (UserModel model : models) { UserModel originalUser = users.remove(model.username.toLowerCase()); users.put(model.username.toLowerCase(), model); // null check on "final" teams because JSON-sourced UserModel // can have a null teams object if (model.teams != null) { Set userTeams = new HashSet(); for (TeamModel team : model.teams) { TeamModel t = teams.get(team.name.toLowerCase()); if (t == null) {
/*
 * jQuery UI Effects Transfer @VERSION
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Effects/Transfer
 *
 * Depends:
 *	jquery.effects.core.js
 */
(function( $, undefined ) {

$.effects.effect.transfer = function( o, done ) {
	var elem = $( this ),
		target = $( o.to ),
		targetFixed = target.css( "position" ) === "fixed",
		body = $("body"),
		fixTop = targetFixed ? body.scrollTop() : 0,
		fixLeft = targetFixed ? body.scrollLeft() : 0,
		endPosition = target.offset(),
		animation = {
			top: endPosition.top - fixTop ,
			left: endPosition.left - fixLeft ,
			height: target.innerHeight(),
			width: target.innerWidth()
		},
		startPosition = elem.offset(),
		transfer = $( '<div class="ui-effects-transfer"></div>' )
			.appendTo( document.body )
			.addClass( o.className )
			.css({
				top: startPosition.top - fixTop ,
				left: startPosition.left - fixLeft ,
				height: elem.innerHeight(),
				width: elem.innerWidth(),
				position: targetFixed ? "fixed" : "absolute"
			})
			.animate( animation, o.duration, o.easing, function() {
				transfer.remove();
				done();
			});
};

})(jQuery);
onfig.setStringList(USER, model.username, ROLE, roles); // discrete repository permissions if (model.permissions != null && !model.canAdmin) { List permissions = new ArrayList(); for (Map.Entry entry : model.permissions.entrySet()) { if (entry.getValue().exceeds(AccessPermission.NONE)) { permissions.add(entry.getValue().asRole(entry.getKey())); } } config.setStringList(USER, model.username, REPOSITORY, permissions); } // user preferences if (model.getPreferences() != null) { List starred = model.getPreferences().getStarredRepositories(); if (starred.size() > 0) { config.setStringList(USER, model.username, STARRED, starred); } } } // write teams for (TeamModel model : teams.values()) { // team roles List roles = new ArrayList(); if (model.canAdmin) { roles.add(Role.ADMIN.getRole()); } if (model.canFork) { roles.add(Role.FORK.getRole()); } if (model.canCreate) { roles.add(Role.CREATE.getRole()); } if (roles.size() == 0) { // we do this to ensure that team record is written. // Otherwise, StoredConfig might optimizes that record away. roles.add(Role.NONE.getRole()); } config.setStringList(TEAM, model.name, ROLE, roles); if (model.accountType != null) { config.setString(TEAM, model.name, ACCOUNTTYPE, model.accountType.name()); } if (!model.canAdmin) { // write team permission for non-admin teams if (model.permissions == null) { // null check on "final" repositories because JSON-sourced TeamModel // can have a null repositories object if (!ArrayUtils.isEmpty(model.repositories)) { config.setStringList(TEAM, model.name, REPOSITORY, new ArrayList( model.repositories)); } } else { // discrete repository permissions List permissions = new ArrayList(); for (Map.Entry entry : model.permissions.entrySet()) { if (entry.getValue().exceeds(AccessPermission.NONE)) { // code:repository (e.g. RW+:~james/myrepo.git permissions.add(entry.getValue().asRole(entry.getKey())); } } config.setStringList(TEAM, model.name, REPOSITORY, permissions); } } // null check on "final" users because JSON-sourced TeamModel // can have a null users object if (!ArrayUtils.isEmpty(model.users)) { config.setStringList(TEAM, model.name, USER, new ArrayList(model.users)); } // null check on "final" mailing lists because JSON-sourced // TeamModel can have a null users object if (!ArrayUtils.isEmpty(model.mailingLists)) { config.setStringList(TEAM, model.name, MAILINGLIST, new ArrayList( model.mailingLists)); } // null check on "final" preReceiveScripts because JSON-sourced // TeamModel can have a null preReceiveScripts object if (!ArrayUtils.isEmpty(model.preReceiveScripts)) { config.setStringList(TEAM, model.name, PRERECEIVE, model.preReceiveScripts); } // null check on "final" postReceiveScripts because JSON-sourced // TeamModel can have a null postReceiveScripts object if (!ArrayUtils.isEmpty(model.postReceiveScripts)) { config.setStringList(TEAM, model.name, POSTRECEIVE, model.postReceiveScripts); } } config.save(); // manually set the forceReload flag because not all JVMs support real // millisecond resolution of lastModified. (issue-55) forceReload = true; // If the write is successful, delete the current file and rename // the temporary copy to the original filename. if (realmFileCopy.exists() && realmFileCopy.length() > 0) { if (realmFile.exists()) { if (!realmFile.delete()) { throw new IOException(MessageFormat.format("Failed to delete {0}!", realmFile.getAbsolutePath())); } } if (!realmFileCopy.renameTo(realmFile)) { throw new IOException(MessageFormat.format("Failed to rename {0} to {1}!", realmFileCopy.getAbsolutePath(), realmFile.getAbsolutePath())); } } else { throw new IOException(MessageFormat.format("Failed to save {0}!", realmFileCopy.getAbsolutePath())); } } /** * Reads the realm file and rebuilds the in-memory lookup tables. */ protected synchronized void read() { if (realmFile.exists() && (forceReload || (realmFile.lastModified() != lastModified))) { forceReload = false; lastModified = realmFile.lastModified(); users.clear(); cookies.clear(); teams.clear(); try { StoredConfig config = new FileBasedConfig(realmFile, FS.detect()); config.load(); Set usernames = config.getSubsections(USER); for (String username : usernames) { UserModel user = new UserModel(username.toLowerCase()); user.password = config.getString(USER, username, PASSWORD); user.displayName = config.getString(USER, username, DISPLAYNAME); user.emailAddress = config.getString(USER, username, EMAILADDRESS); user.accountType = AccountType.fromString(config.getString(USER, username, ACCOUNTTYPE)); if (Constants.EXTERNAL_ACCOUNT.equals(user.password) && user.accountType.isLocal()) { user.accountType = AccountType.EXTERNAL; } user.disabled = config.getBoolean(USER, username, DISABLED, false); user.organizationalUnit = config.getString(USER, username, ORGANIZATIONALUNIT); user.organization = config.getString(USER, username, ORGANIZATION); user.locality = config.getString(USER, username, LOCALITY); user.stateProvince = config.getString(USER, username, STATEPROVINCE); user.countryCode = config.getString(USER, username, COUNTRYCODE); user.cookie = config.getString(USER, username, COOKIE); if (StringUtils.isEmpty(user.cookie) && !StringUtils.isEmpty(user.password)) { user.cookie = StringUtils.getSHA1(user.username + user.password); } // preferences user.getPreferences().setLocale(config.getString(USER, username, LOCALE)); user.getPreferences().setEmailMeOnMyTicketChanges(config.getBoolean(USER, username, EMAILONMYTICKETCHANGES, true)); user.getPreferences().setTransport(Transport.fromString(config.getString(USER, username, TRANSPORT))); // user roles Set roles = new HashSet(Arrays.asList(config.getStringList( USER, username, ROLE))); user.canAdmin = roles.contains(Role.ADMIN.getRole()); user.canFork = roles.contains(Role.FORK.getRole()); user.canCreate = roles.contains(Role.CREATE.getRole()); user.excludeFromFederation = roles.contains(Role.NOT_FEDERATED.getRole()); // repository memberships if (!user.canAdmin) { // non-admin, read permissions Set repositories = new HashSet(Arrays.asList(config .getStringList(USER, username, REPOSITORY))); for (String repository : repositories) { user.addRepositoryPermission(repository); } } // starred repositories Set starred = new HashSet(Arrays.asList(config .getStringList(USER, username, STARRED))); for (String repository : starred) { UserRepositoryPreferences prefs = user.getPreferences().getRepositoryPreferences(repository); prefs.starred = true; } // update cache users.put(user.username, user); if (!StringUtils.isEmpty(user.cookie)) { cookies.put(user.cookie, user); } } // load the teams Set teamnames = config.getSubsections(TEAM); for (String teamname : teamnames) { TeamModel team = new TeamModel(teamname); Set roles = new HashSet(Arrays.asList(config.getStringList( TEAM, teamname, ROLE))); team.canAdmin = roles.contains(Role.ADMIN.getRole()); team.canFork = roles.contains(Role.FORK.getRole()); team.canCreate = roles.contains(Role.CREATE.getRole()); team.accountType = AccountType.fromString(config.getString(TEAM, teamname, ACCOUNTTYPE)); if (!team.canAdmin) { // non-admin team, read permissions team.addRepositoryPermissions(Arrays.asList(config.getStringList(TEAM, teamname, REPOSITORY))); } team.addUsers(Arrays.asList(config.getStringList(TEAM, teamname, USER))); team.addMailingLists(Arrays.asList(config.getStringList(TEAM, teamname, MAILINGLIST))); team.preReceiveScripts.addAll(Arrays.asList(config.getStringList(TEAM, teamname, PRERECEIVE))); team.postReceiveScripts.addAll(Arrays.asList(config.getStringList(TEAM, teamname, POSTRECEIVE))); teams.put(team.name.toLowerCase(), team); // set the teams on the users for (String user : team.users) { UserModel model = users.get(user); if (model != null) { model.teams.add(team); } } } } catch (Exception e) { logger.error(MessageFormat.format("Failed to read {0}", realmFile), e); } } } protected long lastModified() { return lastModified; } @Override public String toString() { return getClass().getSimpleName() + "(" + realmFile.getAbsolutePath() + ")"; } }