Teams support.
Teams simplify the management of user-repository access permissions. Teams have a list of restricted repositories. Users are also added to teams and that grants them access to those repositories.
Federation and RPC support are still in-progress.
7 files added
18 files modified
| | |
| | | - Gitweb inspired web UI
|
| | | - Administrators may create, edit, rename, or delete repositories through the web UI or RPC interface
|
| | | - Administrators may create, edit, rename, or delete users through the web UI or RPC interface
|
| | | - Administrators may create, edit, rename, or delete teams through the web UI or RPC interface
|
| | | - Repository Owners may edit repositories through the web UI
|
| | | - Git-notes display support
|
| | | - Branch metrics (uses Google Charts)
|
| | |
| | | #### Repository Owner
|
| | | The *Repository Owner* has the special permission of being able to edit a repository through the web UI. The Repository Owner is not permitted to rename the repository, delete the repository, or reassign ownership to another user.
|
| | |
|
| | | ### Administering Users (Gitblit v0.8.0+)
|
| | | All users are stored in the `users.conf` file or in the file you specified in `gitblit.properties`.<br/>
|
| | | ### Teams
|
| | |
|
| | | Since v0.8.0, Gitblit supports *teams* for the original `users.properties` user service and the current default user service `users.conf`. Teams have assigned users and assigned repositories. A user can be a member of multiple teams and a repository may belong to multiple teams. This allows the administrator to quickly add a user to a team without having to keep track of all the appropriate repositories. |
| | |
|
| | | ### Administering Users (users.conf, Gitblit v0.8.0+)
|
| | | All users are stored in the `users.conf` file or in the file you specified in `gitblit.properties`. Your file extension must be *.conf* in order to use this user service.
|
| | |
|
| | | The `users.conf` file uses a Git-style configuration format:
|
| | |
|
| | | [user "admin"]
|
| | |
| | | role = "#notfederated"
|
| | | repository = repo1.git
|
| | | repository = repo2.git
|
| | | |
| | | [user "hannibal"]
|
| | | password = bossman
|
| | |
|
| | | [user "faceman"]
|
| | | password = vanity
|
| | |
|
| | | [user "murdock"]
|
| | | password = crazy |
| | | |
| | | [user "babaracus"]
|
| | | password = grrrr
|
| | | |
| | | [team "ateam"]
|
| | | user = hannibal
|
| | | user = faceman
|
| | | user = murdock
|
| | | user = babaracus
|
| | | repository = topsecret.git
|
| | |
|
| | | The `users.conf` file allows flexibility for adding new fields to a UserModel object that the original `users.properties` file does not afford without imposing the complexity of relying on an embedded SQL database.
|
| | |
|
| | | ### Administering Users (Gitblit v0.5.0 - v0.7.0)
|
| | | All users are stored in the `users.properties` file or in the file you specified in `gitblit.properties`.<br/>
|
| | | ### Administering Users (users.properties, Gitblit v0.5.0 - v0.7.0)
|
| | | All users are stored in the `users.properties` file or in the file you specified in `gitblit.properties`. Your file extension must be *.properties* in order to use this user service.
|
| | |
|
| | | The format of `users.properties` follows Jetty's convention for HashRealms:
|
| | |
|
| | | username,password,role1,role2,role3...
|
| | | @teamname,!username1,!username2,!username3,repository1,repository2,repository3...
|
| | |
|
| | | #### Usernames
|
| | | Usernames must be unique and are case-insensitive.
|
| | |
| | |
|
| | | You may use your own custom *com.gitblit.IUserService* implementation by specifying its fully qualified classname in the *realm.userService* setting.
|
| | |
|
| | | Your user service class must be on Gitblit's classpath and must have a public default constructor. |
| | |
|
| | | %BEGINCODE%
|
| | | public interface IUserService {
|
| | |
|
| | | /**
|
| | | * Setup the user service.
|
| | | * |
| | | * @param settings
|
| | | * @since 0.7.0
|
| | | */
|
| | | @Override
|
| | | public void setup(IStoredSettings settings) {
|
| | | }
|
| | | |
| | | /**
|
| | | * Does the user service support cookie authentication?
|
| | | * |
| | | * @return true or false
|
| | | */
|
| | | boolean supportsCookies();
|
| | |
|
| | | /**
|
| | | * Returns the cookie value for the specified user.
|
| | | * |
| | | * @param model
|
| | | * @return cookie value
|
| | | */
|
| | | char[] getCookie(UserModel model);
|
| | |
|
| | | /**
|
| | | * Authenticate a user based on their cookie.
|
| | | * |
| | | * @param cookie
|
| | | * @return a user object or null
|
| | | */
|
| | | UserModel authenticate(char[] cookie);
|
| | |
|
| | | /**
|
| | | * Authenticate a user based on a username and password.
|
| | | * |
| | | * @param username
|
| | | * @param password
|
| | | * @return a user object or null
|
| | | */
|
| | | UserModel authenticate(String username, char[] password);
|
| | |
|
| | | /**
|
| | | * Retrieve the user object for the specified username.
|
| | | * |
| | | * @param username
|
| | | * @return a user object or null
|
| | | */
|
| | | UserModel getUserModel(String username);
|
| | |
|
| | | /**
|
| | | * Updates/writes a complete user object.
|
| | | * |
| | | * @param model
|
| | | * @return true if update is successful
|
| | | */
|
| | | boolean updateUserModel(UserModel model);
|
| | |
|
| | | /**
|
| | | * Adds/updates a user object keyed by username. This method allows for
|
| | | * renaming a user.
|
| | | * |
| | | * @param username
|
| | | * the old username
|
| | | * @param model
|
| | | * the user object to use for username
|
| | | * @return true if update is successful
|
| | | */
|
| | | boolean updateUserModel(String username, UserModel model);
|
| | |
|
| | | /**
|
| | | * Deletes the user object from the user service.
|
| | | * |
| | | * @param model
|
| | | * @return true if successful
|
| | | */
|
| | | boolean deleteUserModel(UserModel model);
|
| | |
|
| | | /**
|
| | | * Delete the user object with the specified username
|
| | | * |
| | | * @param username
|
| | | * @return true if successful
|
| | | */
|
| | | boolean deleteUser(String username);
|
| | |
|
| | | /**
|
| | | * Returns the list of all users available to the login service.
|
| | | * |
| | | * @return list of all usernames
|
| | | */
|
| | | List<String> getAllUsernames();
|
| | |
|
| | | /**
|
| | | * Returns the list of all users who are allowed to bypass the access
|
| | | * restriction placed on the specified repository.
|
| | | * |
| | | * @param role
|
| | | * the repository name
|
| | | * @return list of all usernames that can bypass the access restriction
|
| | | */
|
| | | List<String> getUsernamesForRepositoryRole(String role);
|
| | |
|
| | | /**
|
| | | * Sets the list of all uses who are allowed to bypass the access
|
| | | * restriction placed on the specified repository.
|
| | | * |
| | | * @param role
|
| | | * the repository name
|
| | | * @param usernames
|
| | | * @return true if successful
|
| | | */
|
| | | boolean setUsernamesForRepositoryRole(String role, List<String> usernames);
|
| | |
|
| | | /**
|
| | | * Renames a repository role.
|
| | | * |
| | | * @param oldRole
|
| | | * @param newRole
|
| | | * @return true if successful
|
| | | */
|
| | | boolean renameRepositoryRole(String oldRole, String newRole);
|
| | |
|
| | | /**
|
| | | * Removes a repository role from all users.
|
| | | * |
| | | * @param role
|
| | | * @return true if successful
|
| | | */
|
| | | boolean deleteRepositoryRole(String role);
|
| | |
|
| | | /**
|
| | | * @See java.lang.Object.toString();
|
| | | * @return string representation of the login service
|
| | | */
|
| | | String toString();
|
| | | }
|
| | | %ENDCODE%
|
| | | Your user service class must be on Gitblit's classpath and must have a public default constructor. |
| | | Please see the following interface definition [com.gitblit.IUserService](https://github.com/gitblit/gitblit/blob/master/src/com/gitblit/IUserService.java).
|
| | |
|
| | | ## Client Setup and Configuration
|
| | | ### Https with Self-Signed Certificates
|
| | |
| | | - added: new default user service implementation: com.gitblit.ConfigUserService (users.conf)
|
| | | This user service implementation allows for serialization and deserialization of more sophisticated Gitblit User objects and will open the door for more advanced Gitblit features. For upgrading installations, a `users.conf` file will automatically be created for you from your existing `users.properties` file on your first launch of Gitblit. You will have to manually set *realm.userService=users.conf* to switch to the new user service. The original `users.properties` file and it's corresponding implementation are deprecated.
|
| | | **New:** *realm.userService = users.conf*
|
| | | - added: Teams for specifying user-repository access
|
| | | - added: Gitblit Express bundle to get started running Gitblit on RedHat's OpenShift cloud
|
| | | - added: optional Gravatar integration
|
| | | **New:** *web.allowGravatar = true*
|
| | |
| | | import org.slf4j.Logger;
|
| | | import org.slf4j.LoggerFactory;
|
| | |
|
| | | import com.gitblit.models.TeamModel;
|
| | | import com.gitblit.models.UserModel;
|
| | | import com.gitblit.utils.DeepCopier;
|
| | | import com.gitblit.utils.StringUtils;
|
| | |
|
| | | /**
|
| | |
| | | */
|
| | | 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 REPOSITORY = "repository";
|
| | |
|
| | | private static final String ROLE = "role";
|
| | |
|
| | | private final File realmFile;
|
| | |
|
| | | private final Logger logger = LoggerFactory.getLogger(ConfigUserService.class);
|
| | |
| | |
|
| | | private final Map<String, UserModel> cookies = new ConcurrentHashMap<String, UserModel>();
|
| | |
|
| | | private final String userSection = "user";
|
| | |
|
| | | private final String passwordField = "password";
|
| | |
|
| | | private final String repositoryField = "repository";
|
| | |
|
| | | private final String roleField = "role";
|
| | | private final Map<String, TeamModel> teams = new ConcurrentHashMap<String, TeamModel>();
|
| | |
|
| | | private volatile long lastModified;
|
| | |
|
| | |
| | | * Setup the user service.
|
| | | *
|
| | | * @param settings
|
| | | * @since 0.6.1
|
| | | * @since 0.7.0
|
| | | */
|
| | | @Override
|
| | | public void setup(IStoredSettings settings) {
|
| | |
| | | public 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;
|
| | | }
|
| | |
|
| | |
| | | public boolean updateUserModel(String username, UserModel model) {
|
| | | try {
|
| | | read();
|
| | | users.remove(username.toLowerCase());
|
| | | UserModel oldUser = users.remove(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) {
|
| | | for (TeamModel team : model.teams) {
|
| | | TeamModel t = teams.get(team.name.toLowerCase());
|
| | | if (t == null) {
|
| | | // new team
|
| | | team.addUser(username);
|
| | | teams.put(team.name.toLowerCase(), team);
|
| | | } else {
|
| | | // do not clobber existing team definition
|
| | | // maybe because this is a federated user
|
| | | t.removeUser(username);
|
| | | t.addUser(model.username);
|
| | | }
|
| | | }
|
| | |
|
| | | // check for implicit team removal
|
| | | if (oldUser != null) {
|
| | | for (TeamModel team : oldUser.teams) {
|
| | | if (!model.isTeamMember(team.name)) {
|
| | | team.removeUser(username);
|
| | | }
|
| | | }
|
| | | }
|
| | | }
|
| | | write();
|
| | | return true;
|
| | | } catch (Throwable t) {
|
| | |
| | | try {
|
| | | // Read realm file
|
| | | read();
|
| | | users.remove(username.toLowerCase());
|
| | | UserModel model = users.remove(username.toLowerCase());
|
| | | // remove user from team
|
| | | for (TeamModel team : model.teams) {
|
| | | TeamModel t = teams.get(team.name);
|
| | | if (t == null) {
|
| | | // new team
|
| | | team.removeUser(username);
|
| | | teams.put(team.name.toLowerCase(), team);
|
| | | } else {
|
| | | // existing team
|
| | | t.removeUser(username);
|
| | | }
|
| | | }
|
| | | write();
|
| | | return true;
|
| | | } catch (Throwable t) {
|
| | | logger.error(MessageFormat.format("Failed to delete user {0}!", username), t);
|
| | | }
|
| | | return false;
|
| | | }
|
| | |
|
| | | /**
|
| | | * Returns the list of all teams available to the login service.
|
| | | * |
| | | * @return list of all teams
|
| | | * @since 0.8.0
|
| | | */
|
| | | @Override
|
| | | public List<String> getAllTeamNames() {
|
| | | read();
|
| | | List<String> list = new ArrayList<String>(teams.keySet());
|
| | | return list;
|
| | | }
|
| | | |
| | | /**
|
| | | * Returns the list of all users who are allowed to bypass the access
|
| | | * restriction placed on the specified repository.
|
| | | * |
| | | * @param role
|
| | | * the repository name
|
| | | * @return list of all usernames that can bypass the access restriction
|
| | | */
|
| | | @Override
|
| | | public List<String> getTeamnamesForRepositoryRole(String role) {
|
| | | List<String> list = new ArrayList<String>();
|
| | | try {
|
| | | read();
|
| | | for (Map.Entry<String, TeamModel> entry : teams.entrySet()) {
|
| | | TeamModel model = entry.getValue();
|
| | | if (model.hasRepository(role)) {
|
| | | list.add(model.name);
|
| | | }
|
| | | }
|
| | | } catch (Throwable t) {
|
| | | logger.error(MessageFormat.format("Failed to get teamnames for role {0}!", role), t);
|
| | | }
|
| | | return list;
|
| | | }
|
| | |
|
| | | /**
|
| | | * Sets the list of all teams who are allowed to bypass the access
|
| | | * restriction placed on the specified repository.
|
| | | * |
| | | * @param role
|
| | | * the repository name
|
| | | * @param teamnames
|
| | | * @return true if successful
|
| | | */
|
| | | @Override
|
| | | public boolean setTeamnamesForRepositoryRole(String role, List<String> teamnames) {
|
| | | try {
|
| | | Set<String> specifiedTeams = new HashSet<String>();
|
| | | for (String teamname : teamnames) {
|
| | | specifiedTeams.add(teamname.toLowerCase());
|
| | | }
|
| | |
|
| | | read();
|
| | |
|
| | | // identify teams which require add or remove role
|
| | | for (TeamModel team : teams.values()) {
|
| | | // team has role, check against revised team list
|
| | | if (specifiedTeams.contains(team.name.toLowerCase())) {
|
| | | team.addRepository(role);
|
| | | } else {
|
| | | // remove role from team
|
| | | team.removeRepository(role);
|
| | | }
|
| | | }
|
| | |
|
| | | // persist changes
|
| | | write();
|
| | | return true;
|
| | | } catch (Throwable t) {
|
| | | logger.error(MessageFormat.format("Failed to set teams for role {0}!", role), t);
|
| | | }
|
| | | return false;
|
| | | }
|
| | |
|
| | | /**
|
| | | * Retrieve the team object for the specified team name.
|
| | | * |
| | | * @param teamname
|
| | | * @return a team object or null
|
| | | * @since 0.8.0
|
| | | */
|
| | | @Override
|
| | | public TeamModel getTeamModel(String teamname) {
|
| | | read();
|
| | | TeamModel model = teams.get(teamname.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 team object.
|
| | | * |
| | | * @param model
|
| | | * @return true if update is successful
|
| | | * @since 0.8.0
|
| | | */
|
| | | @Override
|
| | | public boolean updateTeamModel(TeamModel model) {
|
| | | return updateTeamModel(model.name, model);
|
| | | }
|
| | |
|
| | | /**
|
| | | * Updates/writes and replaces a complete team object keyed by teamname.
|
| | | * This method allows for renaming a team.
|
| | | * |
| | | * @param teamname
|
| | | * the old teamname
|
| | | * @param model
|
| | | * the team object to use for teamname
|
| | | * @return true if update is successful
|
| | | * @since 0.8.0
|
| | | */
|
| | | @Override
|
| | | public boolean updateTeamModel(String teamname, TeamModel model) {
|
| | | try {
|
| | | read();
|
| | | teams.remove(teamname.toLowerCase());
|
| | | teams.put(model.name.toLowerCase(), model);
|
| | | write();
|
| | | return true;
|
| | | } catch (Throwable t) {
|
| | | logger.error(MessageFormat.format("Failed to update team model {0}!", model.name), t);
|
| | | }
|
| | | return false;
|
| | | }
|
| | |
|
| | | /**
|
| | | * Deletes the team object from the user service.
|
| | | * |
| | | * @param model
|
| | | * @return true if successful
|
| | | * @since 0.8.0
|
| | | */
|
| | | @Override
|
| | | public boolean deleteTeamModel(TeamModel model) {
|
| | | return deleteTeam(model.name);
|
| | | }
|
| | |
|
| | | /**
|
| | | * Delete the team object with the specified teamname
|
| | | * |
| | | * @param teamname
|
| | | * @return true if successful
|
| | | * @since 0.8.0
|
| | | */
|
| | | @Override
|
| | | public boolean deleteTeam(String teamname) {
|
| | | try {
|
| | | // Read realm file
|
| | | read();
|
| | | teams.remove(teamname.toLowerCase());
|
| | | write();
|
| | | return true;
|
| | | } catch (Throwable t) {
|
| | | logger.error(MessageFormat.format("Failed to delete team {0}!", teamname), t);
|
| | | }
|
| | | return false;
|
| | | }
|
| | |
| | | }
|
| | | }
|
| | |
|
| | | // identify teams which require role rename
|
| | | for (TeamModel model : teams.values()) {
|
| | | if (model.hasRepository(oldRole)) {
|
| | | model.removeRepository(oldRole);
|
| | | model.addRepository(newRole);
|
| | | }
|
| | | }
|
| | | // persist changes
|
| | | write();
|
| | | return true;
|
| | |
| | | user.removeRepository(role);
|
| | | }
|
| | |
|
| | | // identify teams which require role rename
|
| | | for (TeamModel team : teams.values()) {
|
| | | team.removeRepository(role);
|
| | | }
|
| | |
|
| | | // persist changes
|
| | | write();
|
| | | return true;
|
| | |
| | | File realmFileCopy = new File(realmFile.getAbsolutePath() + ".tmp");
|
| | |
|
| | | StoredConfig config = new FileBasedConfig(realmFileCopy, FS.detect());
|
| | |
|
| | | // write users
|
| | | for (UserModel model : users.values()) {
|
| | | config.setString(userSection, model.username, passwordField, model.password);
|
| | | config.setString(USER, model.username, PASSWORD, model.password);
|
| | |
|
| | | // user roles
|
| | | List<String> roles = new ArrayList<String>();
|
| | |
| | | if (model.excludeFromFederation) {
|
| | | roles.add(Constants.NOT_FEDERATED_ROLE);
|
| | | }
|
| | | config.setStringList(userSection, model.username, roleField, roles);
|
| | | config.setStringList(USER, model.username, ROLE, roles);
|
| | |
|
| | | // repository memberships
|
| | | config.setStringList(userSection, model.username, repositoryField,
|
| | | new ArrayList<String>(model.repositories));
|
| | | // null check on "final" repositories because JSON-sourced UserModel
|
| | | // can have a null repositories object
|
| | | if (model.repositories != null) {
|
| | | config.setStringList(USER, model.username, REPOSITORY, new ArrayList<String>(
|
| | | model.repositories));
|
| | | }
|
| | | }
|
| | |
|
| | | // write teams
|
| | | for (TeamModel model : teams.values()) {
|
| | | // null check on "final" repositories because JSON-sourced TeamModel
|
| | | // can have a null repositories object
|
| | | if (model.repositories != null) {
|
| | | config.setStringList(TEAM, model.name, REPOSITORY, new ArrayList<String>(
|
| | | model.repositories));
|
| | | }
|
| | |
|
| | | // null check on "final" users because JSON-sourced TeamModel
|
| | | // can have a null users object
|
| | | if (model.users != null) {
|
| | | config.setStringList(TEAM, model.name, USER, new ArrayList<String>(model.users));
|
| | | }
|
| | | }
|
| | |
|
| | | config.save();
|
| | |
|
| | | // If the write is successful, delete the current file and rename
|
| | |
| | | lastModified = realmFile.lastModified();
|
| | | users.clear();
|
| | | cookies.clear();
|
| | | teams.clear();
|
| | |
|
| | | try {
|
| | | StoredConfig config = new FileBasedConfig(realmFile, FS.detect());
|
| | | config.load();
|
| | | Set<String> usernames = config.getSubsections(userSection);
|
| | | Set<String> usernames = config.getSubsections(USER);
|
| | | for (String username : usernames) {
|
| | | UserModel user = new UserModel(username);
|
| | | user.password = config.getString(userSection, username, passwordField);
|
| | | user.password = config.getString(USER, username, PASSWORD);
|
| | |
|
| | | // user roles
|
| | | Set<String> roles = new HashSet<String>(Arrays.asList(config.getStringList(
|
| | | userSection, username, roleField)));
|
| | | USER, username, ROLE)));
|
| | | user.canAdmin = roles.contains(Constants.ADMIN_ROLE);
|
| | | user.excludeFromFederation = roles.contains(Constants.NOT_FEDERATED_ROLE);
|
| | |
|
| | | // repository memberships
|
| | | Set<String> repositories = new HashSet<String>(Arrays.asList(config
|
| | | .getStringList(userSection, username, repositoryField)));
|
| | | .getStringList(USER, username, REPOSITORY)));
|
| | | for (String repository : repositories) {
|
| | | user.addRepository(repository);
|
| | | }
|
| | |
| | | users.put(username, user);
|
| | | cookies.put(StringUtils.getSHA1(username + user.password), user);
|
| | | }
|
| | |
|
| | | // load the teams
|
| | | Set<String> teamnames = config.getSubsections(TEAM);
|
| | | for (String teamname : teamnames) {
|
| | | TeamModel team = new TeamModel(teamname);
|
| | | team.addRepositories(Arrays.asList(config.getStringList(TEAM, teamname,
|
| | | REPOSITORY)));
|
| | | team.addUsers(Arrays.asList(config.getStringList(TEAM, teamname, USER)));
|
| | |
|
| | | 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);
|
| | | }
|
| | |
| | | import org.slf4j.Logger;
|
| | | import org.slf4j.LoggerFactory;
|
| | |
|
| | | import com.gitblit.models.TeamModel;
|
| | | import com.gitblit.models.UserModel;
|
| | | import com.gitblit.utils.DeepCopier;
|
| | | import com.gitblit.utils.StringUtils;
|
| | |
|
| | | /**
|
| | |
| | |
|
| | | private final Map<String, String> cookies = new ConcurrentHashMap<String, String>();
|
| | |
|
| | | private final Map<String, TeamModel> teams = new ConcurrentHashMap<String, TeamModel>();
|
| | |
|
| | | public FileUserService(File realmFile) {
|
| | | super(realmFile.getAbsolutePath());
|
| | | }
|
| | |
| | | * Setup the user service.
|
| | | *
|
| | | * @param settings
|
| | | * @since 0.6.1
|
| | | * @since 0.7.0
|
| | | */
|
| | | @Override
|
| | | public void setup(IStoredSettings settings) {
|
| | |
| | | model.addRepository(role);
|
| | | }
|
| | | }
|
| | | // set the teams for the user
|
| | | for (TeamModel team : teams.values()) {
|
| | | if (team.hasUser(username)) {
|
| | | model.teams.add(DeepCopier.copy(team));
|
| | | }
|
| | | }
|
| | | return model;
|
| | | }
|
| | |
|
| | |
| | | public boolean updateUserModel(String username, UserModel model) {
|
| | | try {
|
| | | Properties allUsers = read();
|
| | | UserModel oldUser = getUserModel(username);
|
| | | ArrayList<String> roles = new ArrayList<String>(model.repositories);
|
| | |
|
| | | // Permissions
|
| | |
| | | sb.setLength(sb.length() - 1);
|
| | | allUsers.remove(username);
|
| | | allUsers.put(model.username, sb.toString());
|
| | |
|
| | | // null check on "final" teams because JSON-sourced UserModel
|
| | | // can have a null teams object
|
| | | if (model.teams != null) {
|
| | | // update team cache
|
| | | for (TeamModel team : model.teams) {
|
| | | TeamModel t = getTeamModel(team.name);
|
| | | if (t == null) {
|
| | | // new team
|
| | | t = team;
|
| | | }
|
| | | t.removeUser(username);
|
| | | t.addUser(model.username);
|
| | | updateTeamCache(allUsers, t.name, t);
|
| | | }
|
| | |
|
| | | // check for implicit team removal
|
| | | if (oldUser != null) {
|
| | | for (TeamModel team : oldUser.teams) {
|
| | | if (!model.isTeamMember(team.name)) {
|
| | | team.removeUser(username);
|
| | | updateTeamCache(allUsers, team.name, team);
|
| | | }
|
| | | }
|
| | | }
|
| | | }
|
| | |
|
| | | write(allUsers);
|
| | | return true;
|
| | |
| | | try {
|
| | | // Read realm file
|
| | | Properties allUsers = read();
|
| | | UserModel user = getUserModel(username);
|
| | | allUsers.remove(username);
|
| | | for (TeamModel team : user.teams) {
|
| | | TeamModel t = getTeamModel(team.name);
|
| | | if (t == null) {
|
| | | // new team
|
| | | t = team;
|
| | | }
|
| | | t.removeUser(username);
|
| | | updateTeamCache(allUsers, t.name, t);
|
| | | }
|
| | | write(allUsers);
|
| | | return true;
|
| | | } catch (Throwable t) {
|
| | |
| | | @Override
|
| | | public List<String> getAllUsernames() {
|
| | | Properties allUsers = read();
|
| | | List<String> list = new ArrayList<String>(allUsers.stringPropertyNames());
|
| | | List<String> list = new ArrayList<String>();
|
| | | for (String user : allUsers.stringPropertyNames()) {
|
| | | if (user.charAt(0) == '@') {
|
| | | // skip team user definitions
|
| | | continue;
|
| | | }
|
| | | list.add(user);
|
| | | }
|
| | | return list;
|
| | | }
|
| | |
|
| | |
| | | try {
|
| | | Properties allUsers = read();
|
| | | for (String username : allUsers.stringPropertyNames()) {
|
| | | if (username.charAt(0) == '@') {
|
| | | continue;
|
| | | }
|
| | | String value = allUsers.getProperty(username);
|
| | | String[] values = value.split(",");
|
| | | // skip first value (password)
|
| | |
| | | }
|
| | |
|
| | | /**
|
| | | * Sets the list of all uses who are allowed to bypass the access
|
| | | * Sets the list of all users who are allowed to bypass the access
|
| | | * restriction placed on the specified repository.
|
| | | *
|
| | | * @param role
|
| | |
| | | sb.append(',');
|
| | | sb.append(newRole);
|
| | | sb.append(',');
|
| | | |
| | |
|
| | | // skip first value (password)
|
| | | for (int i = 1; i < values.length; i++) {
|
| | | String value = values[i];
|
| | |
| | | FileWriter writer = new FileWriter(realmFileCopy);
|
| | | properties
|
| | | .store(writer,
|
| | | "# Gitblit realm file format: username=password,\\#permission,repository1,repository2...");
|
| | | " Gitblit realm file format:\n username=password,\\#permission,repository1,repository2...\n @teamname=!username1,!username2,!username3,repository1,repository2...");
|
| | | writer.close();
|
| | | // If the write is successful, delete the current file and rename
|
| | | // the temporary copy to the original filename.
|
| | |
| | | if (lastRead != lastModified()) {
|
| | | // reload hash cache
|
| | | cookies.clear();
|
| | | teams.clear();
|
| | |
|
| | | for (String username : allUsers.stringPropertyNames()) {
|
| | | String value = allUsers.getProperty(username);
|
| | | String[] roles = value.split(",");
|
| | | String password = roles[0];
|
| | | cookies.put(StringUtils.getSHA1(username + password), username);
|
| | | if (username.charAt(0) == '@') {
|
| | | // team definition
|
| | | TeamModel team = new TeamModel(username.substring(1));
|
| | | List<String> repositories = new ArrayList<String>();
|
| | | List<String> users = new ArrayList<String>();
|
| | | for (String role : roles) {
|
| | | if (role.charAt(0) == '!') {
|
| | | users.add(role.substring(1));
|
| | | } else {
|
| | | repositories.add(role);
|
| | | }
|
| | | }
|
| | | team.addRepositories(repositories);
|
| | | team.addUsers(users);
|
| | | teams.put(team.name.toLowerCase(), team);
|
| | | } else {
|
| | | // user definition
|
| | | String password = roles[0];
|
| | | cookies.put(StringUtils.getSHA1(username + password), username);
|
| | | }
|
| | | }
|
| | | }
|
| | | return allUsers;
|
| | |
| | | public String toString() {
|
| | | return getClass().getSimpleName() + "(" + propertiesFile.getAbsolutePath() + ")";
|
| | | }
|
| | |
|
| | | /**
|
| | | * Returns the list of all teams available to the login service.
|
| | | * |
| | | * @return list of all teams
|
| | | * @since 0.8.0
|
| | | */
|
| | | @Override
|
| | | public List<String> getAllTeamNames() {
|
| | | List<String> list = new ArrayList<String>(teams.keySet());
|
| | | return list;
|
| | | }
|
| | |
|
| | | /**
|
| | | * Returns the list of all teams who are allowed to bypass the access
|
| | | * restriction placed on the specified repository.
|
| | | * |
| | | * @param role
|
| | | * the repository name
|
| | | * @return list of all teamnames that can bypass the access restriction
|
| | | */
|
| | | @Override
|
| | | public List<String> getTeamnamesForRepositoryRole(String role) {
|
| | | List<String> list = new ArrayList<String>();
|
| | | try {
|
| | | Properties allUsers = read();
|
| | | for (String team : allUsers.stringPropertyNames()) {
|
| | | if (team.charAt(0) != '@') {
|
| | | // skip users
|
| | | continue;
|
| | | }
|
| | | String value = allUsers.getProperty(team);
|
| | | String[] values = value.split(",");
|
| | | for (int i = 0; i < values.length; i++) {
|
| | | String r = values[i];
|
| | | if (r.equalsIgnoreCase(role)) {
|
| | | // strip leading @
|
| | | list.add(team.substring(1));
|
| | | break;
|
| | | }
|
| | | }
|
| | | }
|
| | | } catch (Throwable t) {
|
| | | logger.error(MessageFormat.format("Failed to get teamnames for role {0}!", role), t);
|
| | | }
|
| | | return list;
|
| | | }
|
| | |
|
| | | /**
|
| | | * Sets the list of all teams who are allowed to bypass the access
|
| | | * restriction placed on the specified repository.
|
| | | * |
| | | * @param role
|
| | | * the repository name
|
| | | * @param teamnames
|
| | | * @return true if successful
|
| | | */
|
| | | @Override
|
| | | public boolean setTeamnamesForRepositoryRole(String role, List<String> teamnames) {
|
| | | try {
|
| | | Set<String> specifiedTeams = new HashSet<String>(teamnames);
|
| | | Set<String> needsAddRole = new HashSet<String>(specifiedTeams);
|
| | | Set<String> needsRemoveRole = new HashSet<String>();
|
| | |
|
| | | // identify teams which require add and remove role
|
| | | Properties allUsers = read();
|
| | | for (String team : allUsers.stringPropertyNames()) {
|
| | | if (team.charAt(0) != '@') {
|
| | | // skip users
|
| | | continue;
|
| | | }
|
| | | String name = team.substring(1);
|
| | | String value = allUsers.getProperty(team);
|
| | | String[] values = value.split(",");
|
| | | for (int i = 0; i < values.length; i++) {
|
| | | String r = values[i];
|
| | | if (r.equalsIgnoreCase(role)) {
|
| | | // team has role, check against revised team list
|
| | | if (specifiedTeams.contains(name)) {
|
| | | needsAddRole.remove(name);
|
| | | } else {
|
| | | // remove role from team
|
| | | needsRemoveRole.add(name);
|
| | | }
|
| | | break;
|
| | | }
|
| | | }
|
| | | }
|
| | |
|
| | | // add roles to teams
|
| | | for (String name : needsAddRole) {
|
| | | String team = "@" + name;
|
| | | String teamValues = allUsers.getProperty(team);
|
| | | teamValues += "," + role;
|
| | | allUsers.put(team, teamValues);
|
| | | }
|
| | |
|
| | | // remove role from team
|
| | | for (String name : needsRemoveRole) {
|
| | | String team = "@" + name;
|
| | | String[] values = allUsers.getProperty(team).split(","); |
| | | StringBuilder sb = new StringBuilder();
|
| | | for (int i = 0; i < values.length; i++) {
|
| | | String value = values[i];
|
| | | if (!value.equalsIgnoreCase(role)) {
|
| | | sb.append(value);
|
| | | sb.append(',');
|
| | | }
|
| | | }
|
| | | sb.setLength(sb.length() - 1);
|
| | |
|
| | | // update properties
|
| | | allUsers.put(team, sb.toString());
|
| | | }
|
| | |
|
| | | // persist changes
|
| | | write(allUsers);
|
| | | return true;
|
| | | } catch (Throwable t) {
|
| | | logger.error(MessageFormat.format("Failed to set teamnames for role {0}!", role), t);
|
| | | }
|
| | | return false;
|
| | | }
|
| | |
|
| | | /**
|
| | | * Retrieve the team object for the specified team name.
|
| | | * |
| | | * @param teamname
|
| | | * @return a team object or null
|
| | | * @since 0.8.0
|
| | | */
|
| | | @Override
|
| | | public TeamModel getTeamModel(String teamname) {
|
| | | read();
|
| | | TeamModel team = teams.get(teamname.toLowerCase());
|
| | | if (team != null) {
|
| | | // clone the model, otherwise all changes to this object are
|
| | | // live and unpersisted
|
| | | team = DeepCopier.copy(team);
|
| | | }
|
| | | return team;
|
| | | }
|
| | |
|
| | | /**
|
| | | * Updates/writes a complete team object.
|
| | | * |
| | | * @param model
|
| | | * @return true if update is successful
|
| | | * @since 0.8.0
|
| | | */
|
| | | @Override
|
| | | public boolean updateTeamModel(TeamModel model) {
|
| | | return updateTeamModel(model.name, model);
|
| | | }
|
| | |
|
| | | /**
|
| | | * Updates/writes and replaces a complete team object keyed by teamname.
|
| | | * This method allows for renaming a team.
|
| | | * |
| | | * @param teamname
|
| | | * the old teamname
|
| | | * @param model
|
| | | * the team object to use for teamname
|
| | | * @return true if update is successful
|
| | | * @since 0.8.0
|
| | | */
|
| | | @Override
|
| | | public boolean updateTeamModel(String teamname, TeamModel model) {
|
| | | try {
|
| | | Properties allUsers = read();
|
| | | updateTeamCache(allUsers, teamname, model);
|
| | | write(allUsers);
|
| | | return true;
|
| | | } catch (Throwable t) {
|
| | | logger.error(MessageFormat.format("Failed to update team model {0}!", model.name), t);
|
| | | }
|
| | | return false;
|
| | | }
|
| | |
|
| | | private void updateTeamCache(Properties allUsers, String teamname, TeamModel model) {
|
| | | StringBuilder sb = new StringBuilder();
|
| | | for (String repository : model.repositories) {
|
| | | sb.append(repository);
|
| | | sb.append(',');
|
| | | }
|
| | | for (String user : model.users) {
|
| | | sb.append('!');
|
| | | sb.append(user);
|
| | | sb.append(',');
|
| | | }
|
| | | // trim trailing comma
|
| | | sb.setLength(sb.length() - 1);
|
| | | allUsers.remove("@" + teamname);
|
| | | allUsers.put("@" + model.name, sb.toString());
|
| | |
|
| | | // update team cache
|
| | | teams.remove(teamname.toLowerCase());
|
| | | teams.put(model.name.toLowerCase(), model);
|
| | | }
|
| | |
|
| | | /**
|
| | | * Deletes the team object from the user service.
|
| | | * |
| | | * @param model
|
| | | * @return true if successful
|
| | | * @since 0.8.0
|
| | | */
|
| | | @Override
|
| | | public boolean deleteTeamModel(TeamModel model) {
|
| | | return deleteTeam(model.name);
|
| | | }
|
| | |
|
| | | /**
|
| | | * Delete the team object with the specified teamname
|
| | | * |
| | | * @param teamname
|
| | | * @return true if successful
|
| | | * @since 0.8.0
|
| | | */
|
| | | @Override
|
| | | public boolean deleteTeam(String teamname) {
|
| | | Properties allUsers = read();
|
| | | teams.remove(teamname.toLowerCase());
|
| | | allUsers.remove("@" + teamname);
|
| | | try {
|
| | | write(allUsers);
|
| | | return true;
|
| | | } catch (Throwable t) {
|
| | | logger.error(MessageFormat.format("Failed to delete team {0}!", teamname), t);
|
| | | }
|
| | | return false;
|
| | | }
|
| | | }
|
| | |
| | | import com.gitblit.models.ServerSettings;
|
| | | import com.gitblit.models.ServerStatus;
|
| | | import com.gitblit.models.SettingModel;
|
| | | import com.gitblit.models.TeamModel;
|
| | | import com.gitblit.models.UserModel;
|
| | | import com.gitblit.utils.ByteFormat;
|
| | | import com.gitblit.utils.FederationUtils;
|
| | |
| | | if (!userService.updateUserModel(username, user)) {
|
| | | throw new GitBlitException(isCreate ? "Failed to add user!" : "Failed to update user!");
|
| | | }
|
| | | }
|
| | |
|
| | | /**
|
| | | * Returns the list of available teams that a user or repository may be
|
| | | * assigned to.
|
| | | * |
| | | * @return the list of teams
|
| | | */
|
| | | public List<String> getAllTeamnames() {
|
| | | List<String> teams = new ArrayList<String>(userService.getAllTeamNames());
|
| | | Collections.sort(teams);
|
| | | return teams;
|
| | | }
|
| | |
|
| | | /**
|
| | | * Returns the TeamModel object for the specified name.
|
| | | * |
| | | * @param teamname
|
| | | * @return a TeamModel object or null
|
| | | */
|
| | | public TeamModel getTeamModel(String teamname) {
|
| | | return userService.getTeamModel(teamname);
|
| | | }
|
| | |
|
| | | /**
|
| | | * Returns the list of all teams who are allowed to bypass the access
|
| | | * restriction placed on the specified repository.
|
| | | * |
| | | * @see IUserService.getTeamnamesForRepositoryRole(String)
|
| | | * @param repository
|
| | | * @return list of all teamnames that can bypass the access restriction
|
| | | */
|
| | | public List<String> getRepositoryTeams(RepositoryModel repository) {
|
| | | return userService.getTeamnamesForRepositoryRole(repository.name);
|
| | | }
|
| | |
|
| | | /**
|
| | | * Sets the list of all uses who are allowed to bypass the access
|
| | | * restriction placed on the specified repository.
|
| | | * |
| | | * @see IUserService.setTeamnamesForRepositoryRole(String, List<String>)
|
| | | * @param repository
|
| | | * @param teamnames
|
| | | * @return true if successful
|
| | | */
|
| | | public boolean setRepositoryTeams(RepositoryModel repository, List<String> repositoryTeams) {
|
| | | return userService.setTeamnamesForRepositoryRole(repository.name, repositoryTeams);
|
| | | }
|
| | | /**
|
| | | * Updates the TeamModel object for the specified name.
|
| | | * |
| | | * @param teamname
|
| | | * @param team
|
| | | * @param isCreate
|
| | | */
|
| | | public void updateTeamModel(String teamname, TeamModel team, boolean isCreate) throws GitBlitException {
|
| | | if (!teamname.equalsIgnoreCase(team.name)) {
|
| | | if (userService.getTeamModel(team.name) != null) {
|
| | | throw new GitBlitException(MessageFormat.format(
|
| | | "Failed to rename ''{0}'' because ''{1}'' already exists.", teamname,
|
| | | team.name));
|
| | | }
|
| | | }
|
| | | if (!userService.updateTeamModel(teamname, team)) {
|
| | | throw new GitBlitException(isCreate ? "Failed to add team!" : "Failed to update team!");
|
| | | }
|
| | | }
|
| | | |
| | | /**
|
| | | * Delete the team object with the specified teamname
|
| | | * |
| | | * @see IUserService.deleteTeam(String)
|
| | | * @param teamname
|
| | | * @return true if successful
|
| | | */
|
| | | public boolean deleteTeam(String teamname) {
|
| | | return userService.deleteTeam(teamname);
|
| | | }
|
| | |
|
| | | /**
|
| | |
| | | case PULL_REPOSITORIES:
|
| | | return token.equals(all) || token.equals(unr) || token.equals(jur);
|
| | | case PULL_USERS:
|
| | | case PULL_TEAMS:
|
| | | return token.equals(all) || token.equals(unr);
|
| | | case PULL_SETTINGS:
|
| | | return token.equals(all);
|
| | |
| | | configService.updateUserModel(userModel);
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | // issue suggestion about switching to users.conf
|
| | | logger.warn("Please consider using \"users.conf\" instead of the deprecated \"users.properties\" file");
|
| | | } else if (realmFile.getName().toLowerCase().endsWith(".conf")) {
|
| | |
| | |
|
| | | import java.util.List;
|
| | |
|
| | | import com.gitblit.models.TeamModel;
|
| | | import com.gitblit.models.UserModel;
|
| | |
|
| | | /**
|
| | |
| | | List<String> getAllUsernames();
|
| | |
|
| | | /**
|
| | | * Returns the list of all teams available to the login service.
|
| | | * |
| | | * @return list of all teams
|
| | | * @since 0.8.0
|
| | | */ |
| | | List<String> getAllTeamNames();
|
| | | |
| | | /**
|
| | | * Returns the list of all users who are allowed to bypass the access
|
| | | * restriction placed on the specified repository.
|
| | | * |
| | | * @param role
|
| | | * the repository name
|
| | | * @return list of all usernames that can bypass the access restriction
|
| | | */ |
| | | List<String> getTeamnamesForRepositoryRole(String role);
|
| | |
|
| | | /**
|
| | | * Sets the list of all teams who are allowed to bypass the access
|
| | | * restriction placed on the specified repository.
|
| | | * |
| | | * @param role
|
| | | * the repository name
|
| | | * @param teamnames
|
| | | * @return true if successful
|
| | | */ |
| | | boolean setTeamnamesForRepositoryRole(String role, List<String> teamnames);
|
| | | |
| | | /**
|
| | | * Retrieve the team object for the specified team name.
|
| | | * |
| | | * @param teamname
|
| | | * @return a team object or null
|
| | | * @since 0.8.0
|
| | | */ |
| | | TeamModel getTeamModel(String teamname);
|
| | |
|
| | | /**
|
| | | * Updates/writes a complete team object.
|
| | | * |
| | | * @param model
|
| | | * @return true if update is successful
|
| | | * @since 0.8.0
|
| | | */ |
| | | boolean updateTeamModel(TeamModel model);
|
| | |
|
| | | /**
|
| | | * Updates/writes and replaces a complete team object keyed by teamname.
|
| | | * This method allows for renaming a team.
|
| | | * |
| | | * @param teamname
|
| | | * the old teamname
|
| | | * @param model
|
| | | * the team object to use for teamname
|
| | | * @return true if update is successful
|
| | | * @since 0.8.0
|
| | | */
|
| | | boolean updateTeamModel(String teamname, TeamModel model);
|
| | |
|
| | | /**
|
| | | * Deletes the team object from the user service.
|
| | | * |
| | | * @param model
|
| | | * @return true if successful
|
| | | * @since 0.8.0
|
| | | */
|
| | | boolean deleteTeamModel(TeamModel model);
|
| | |
|
| | | /**
|
| | | * Delete the team object with the specified teamname
|
| | | * |
| | | * @param teamname
|
| | | * @return true if successful
|
| | | * @since 0.8.0
|
| | | */ |
| | | boolean deleteTeam(String teamname);
|
| | |
|
| | | /**
|
| | | * Returns the list of all users who are allowed to bypass the access
|
| | | * restriction placed on the specified repository.
|
| | | *
|
New file |
| | |
| | | /*
|
| | | * 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.models;
|
| | |
|
| | | import java.io.Serializable;
|
| | | import java.util.Collection;
|
| | | import java.util.HashSet;
|
| | | import java.util.Set;
|
| | |
|
| | | /**
|
| | | * TeamModel is a serializable model class that represents a group of users and
|
| | | * a list of accessible repositories.
|
| | | * |
| | | * @author James Moger
|
| | | * |
| | | */
|
| | | public class TeamModel implements Serializable, Comparable<TeamModel> {
|
| | |
|
| | | private static final long serialVersionUID = 1L;
|
| | |
|
| | | // field names are reflectively mapped in EditTeam page
|
| | | public String name;
|
| | | public final Set<String> users = new HashSet<String>();
|
| | | public final Set<String> repositories = new HashSet<String>();
|
| | |
|
| | | public TeamModel(String name) {
|
| | | this.name = name;
|
| | | }
|
| | |
|
| | | public boolean hasRepository(String name) {
|
| | | return repositories.contains(name.toLowerCase());
|
| | | }
|
| | |
|
| | | public void addRepository(String name) {
|
| | | repositories.add(name.toLowerCase());
|
| | | }
|
| | | |
| | | public void addRepositories(Collection<String> names) {
|
| | | for (String name:names) {
|
| | | repositories.add(name.toLowerCase());
|
| | | }
|
| | | } |
| | |
|
| | | public void removeRepository(String name) {
|
| | | repositories.remove(name.toLowerCase());
|
| | | }
|
| | | |
| | | public boolean hasUser(String name) {
|
| | | return users.contains(name.toLowerCase());
|
| | | }
|
| | |
|
| | | public void addUser(String name) {
|
| | | users.add(name.toLowerCase());
|
| | | }
|
| | |
|
| | | public void addUsers(Collection<String> names) {
|
| | | for (String name:names) {
|
| | | users.add(name.toLowerCase());
|
| | | }
|
| | | }
|
| | |
|
| | | public void removeUser(String name) {
|
| | | users.remove(name.toLowerCase());
|
| | | }
|
| | |
|
| | | @Override
|
| | | public String toString() {
|
| | | return name;
|
| | | }
|
| | |
|
| | | @Override
|
| | | public int compareTo(TeamModel o) {
|
| | | return name.compareTo(o.name);
|
| | | }
|
| | | }
|
| | |
| | | public boolean canAdmin;
|
| | | public boolean excludeFromFederation;
|
| | | public final Set<String> repositories = new HashSet<String>();
|
| | | public final Set<TeamModel> teams = new HashSet<TeamModel>();
|
| | |
|
| | | public UserModel(String username) {
|
| | | this.username = username;
|
| | |
| | | */
|
| | | @Deprecated
|
| | | public boolean canAccessRepository(String repositoryName) {
|
| | | return canAdmin || repositories.contains(repositoryName.toLowerCase());
|
| | | return canAdmin || repositories.contains(repositoryName.toLowerCase())
|
| | | || hasTeamAccess(repositoryName);
|
| | | }
|
| | |
|
| | | public boolean canAccessRepository(RepositoryModel repository) {
|
| | | boolean isOwner = !StringUtils.isEmpty(repository.owner)
|
| | | && repository.owner.equals(username);
|
| | | return canAdmin || isOwner || repositories.contains(repository.name.toLowerCase());
|
| | | return canAdmin || isOwner || repositories.contains(repository.name.toLowerCase())
|
| | | || hasTeamAccess(repository.name);
|
| | | }
|
| | |
|
| | | public boolean hasTeamAccess(String repositoryName) {
|
| | | for (TeamModel team : teams) {
|
| | | if (team.hasRepository(repositoryName)) {
|
| | | return true;
|
| | | }
|
| | | }
|
| | | return false;
|
| | | }
|
| | |
|
| | | public boolean hasRepository(String name) {
|
| | |
| | | repositories.remove(name.toLowerCase());
|
| | | }
|
| | |
|
| | | public boolean isTeamMember(String teamname) {
|
| | | for (TeamModel team : teams) {
|
| | | if (team.name.equalsIgnoreCase(teamname)) {
|
| | | return true;
|
| | | }
|
| | | }
|
| | | return false;
|
| | | }
|
| | |
|
| | | @Override
|
| | | public String getName() {
|
| | | return username;
|
New file |
| | |
| | | /*
|
| | | * 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.utils;
|
| | |
|
| | | import java.io.ByteArrayInputStream;
|
| | | import java.io.ByteArrayOutputStream;
|
| | | import java.io.IOException;
|
| | | import java.io.InputStream;
|
| | | import java.io.ObjectInputStream;
|
| | | import java.io.ObjectOutputStream;
|
| | | import java.io.PipedInputStream;
|
| | | import java.io.PipedOutputStream;
|
| | |
|
| | | public class DeepCopier {
|
| | |
|
| | | /**
|
| | | * Produce a deep copy of the given object. Serializes the entire object to
|
| | | * a byte array in memory. Recommended for relatively small objects.
|
| | | */
|
| | | @SuppressWarnings("unchecked")
|
| | | public static <T> T copy(T original) {
|
| | | T o = null;
|
| | | try {
|
| | | ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
|
| | | ObjectOutputStream oos = new ObjectOutputStream(byteOut);
|
| | | oos.writeObject(original);
|
| | | ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());
|
| | | ObjectInputStream ois = new ObjectInputStream(byteIn);
|
| | | try {
|
| | | o = (T) ois.readObject();
|
| | | } catch (ClassNotFoundException cex) {
|
| | | // actually can not happen in this instance
|
| | | }
|
| | | } catch (IOException iox) {
|
| | | // doesn't seem likely to happen as these streams are in memory
|
| | | throw new RuntimeException(iox);
|
| | | }
|
| | | return o;
|
| | | }
|
| | |
|
| | | /**
|
| | | * This conserves heap memory!!!!! Produce a deep copy of the given object.
|
| | | * Serializes the object through a pipe between two threads. Recommended for
|
| | | * very large objects. The current thread is used for serializing the
|
| | | * original object in order to respect any synchronization the caller may
|
| | | * have around it, and a new thread is used for deserializing the copy.
|
| | | * |
| | | */
|
| | | public static <T> T copyParallel(T original) {
|
| | | try {
|
| | | PipedOutputStream outputStream = new PipedOutputStream();
|
| | | PipedInputStream inputStream = new PipedInputStream(outputStream);
|
| | | ObjectOutputStream ois = new ObjectOutputStream(outputStream);
|
| | | Receiver<T> receiver = new Receiver<T>(inputStream);
|
| | | try {
|
| | | ois.writeObject(original);
|
| | | } finally {
|
| | | ois.close();
|
| | | }
|
| | | return receiver.getResult();
|
| | | } catch (IOException iox) {
|
| | | // doesn't seem likely to happen as these streams are in memory
|
| | | throw new RuntimeException(iox);
|
| | | }
|
| | | }
|
| | |
|
| | | private static class Receiver<T> extends Thread {
|
| | |
|
| | | private final InputStream inputStream;
|
| | | private volatile T result;
|
| | | private volatile Throwable throwable;
|
| | |
|
| | | public Receiver(InputStream inputStream) {
|
| | | this.inputStream = inputStream;
|
| | | start();
|
| | | }
|
| | |
|
| | | @SuppressWarnings("unchecked")
|
| | | public void run() {
|
| | |
|
| | | try {
|
| | | ObjectInputStream ois = new ObjectInputStream(inputStream);
|
| | | try {
|
| | | result = (T) ois.readObject();
|
| | | try {
|
| | | // Some serializers may write more than they actually
|
| | | // need to deserialize the object, but if we don't
|
| | | // read it all the PipedOutputStream will choke.
|
| | | while (inputStream.read() != -1) {
|
| | | }
|
| | | } catch (IOException e) {
|
| | | // The object has been successfully deserialized, so
|
| | | // ignore problems at this point (for example, the
|
| | | // serializer may have explicitly closed the inputStream
|
| | | // itself, causing this read to fail).
|
| | | }
|
| | | } finally {
|
| | | ois.close();
|
| | | }
|
| | | } catch (Throwable t) {
|
| | | throwable = t;
|
| | | }
|
| | | }
|
| | |
|
| | | public T getResult() throws IOException {
|
| | | try {
|
| | | join();
|
| | | } catch (InterruptedException e) {
|
| | | throw new RuntimeException("Unexpected InterruptedException", e);
|
| | | }
|
| | | // join() guarantees that all shared memory is synchronized between
|
| | | // the two threads
|
| | | if (throwable != null) {
|
| | | if (throwable instanceof ClassNotFoundException) {
|
| | | // actually can not happen in this instance
|
| | | }
|
| | | throw new RuntimeException(throwable);
|
| | | }
|
| | | return result;
|
| | | }
|
| | | }
|
| | | } |
| | |
| | | gb.dailyActivity = daily activity
|
| | | gb.activeRepositories = active repositories
|
| | | gb.activeAuthors = active authors
|
| | | gb.commits = commits |
| | | gb.commits = commits
|
| | | gb.teams = teams
|
| | | gb.teamName = team name
|
| | | gb.teamMembers = team members
|
| | | gb.teamMemberships = team memberships
|
| | | gb.newTeam = new team
|
| | | gb.permittedTeams = permitted teams |
| | |
| | | return new PageParameters("user=" + username);
|
| | | }
|
| | |
|
| | | public static PageParameters newTeamnameParameter(String teamname) {
|
| | | return new PageParameters("team=" + teamname);
|
| | | }
|
| | |
|
| | | public static PageParameters newRepositoryParameter(String repositoryName) {
|
| | | return new PageParameters("r=" + repositoryName);
|
| | | }
|
| | |
| | | return params.getString("user", "");
|
| | | }
|
| | |
|
| | | public static String getTeamname(PageParameters params) {
|
| | | return params.getString("team", "");
|
| | | }
|
| | |
|
| | | public static String getToken(PageParameters params) {
|
| | | return params.getString("t", "");
|
| | | }
|
| | |
| | | <wicket:extend>
|
| | | <body onload="document.getElementById('name').focus();">
|
| | | <!-- Repository Table -->
|
| | | <form wicket:id="editForm">
|
| | | <form style="padding-top:5px;" wicket:id="editForm">
|
| | | <table class="plain">
|
| | | <tbody>
|
| | | <tr><th><wicket:message key="gb.name"></wicket:message></th><td class="edit"><input class="span6" type="text" wicket:id="name" id="name" size="40" tabindex="1" /> <i><wicket:message key="gb.nameDescription"></wicket:message></i></td></tr>
|
| | |
| | | <tr><td colspan="2"><hr></hr></td></tr>
|
| | | <tr><th><wicket:message key="gb.accessRestriction"></wicket:message></th><td class="edit"><select class="span6" wicket:id="accessRestriction" tabindex="12" /></td></tr>
|
| | | <tr><th style="vertical-align: top;"><wicket:message key="gb.permittedUsers"></wicket:message></th><td style="padding:2px;"><span wicket:id="users"></span></td></tr>
|
| | | <tr><th style="vertical-align: top;"><wicket:message key="gb.permittedTeams"></wicket:message></th><td style="padding:2px;"><span wicket:id="teams"></span></td></tr>
|
| | | <tr><td colspan="2"><hr></hr></td></tr>
|
| | | <tr><th><wicket:message key="gb.federationStrategy"></wicket:message></th><td class="edit"><select class="span6" wicket:id="federationStrategy" tabindex="13" /></td></tr>
|
| | | <tr><th style="vertical-align: top;"><wicket:message key="gb.federationSets"></wicket:message></th><td style="padding:2px;"><span wicket:id="federationSets"></span></td></tr>
|
| | |
| | |
|
| | | List<String> federationSets = new ArrayList<String>();
|
| | | List<String> repositoryUsers = new ArrayList<String>();
|
| | | List<String> repositoryTeams = new ArrayList<String>();
|
| | | if (isCreate) {
|
| | | super.setupPage(getString("gb.newRepository"), "");
|
| | | } else {
|
| | | super.setupPage(getString("gb.edit"), repositoryModel.name);
|
| | | if (repositoryModel.accessRestriction.exceeds(AccessRestrictionType.NONE)) {
|
| | | repositoryUsers.addAll(GitBlit.self().getRepositoryUsers(repositoryModel));
|
| | | repositoryTeams.addAll(GitBlit.self().getRepositoryTeams(repositoryModel));
|
| | | Collections.sort(repositoryUsers);
|
| | | }
|
| | | federationSets.addAll(repositoryModel.federationSets);
|
| | |
| | | // users palette
|
| | | final Palette<String> usersPalette = new Palette<String>("users", new ListModel<String>(
|
| | | repositoryUsers), new CollectionModel<String>(GitBlit.self().getAllUsernames()),
|
| | | new ChoiceRenderer<String>("", ""), 10, false);
|
| | |
|
| | | // teams palette
|
| | | final Palette<String> teamsPalette = new Palette<String>("teams", new ListModel<String>(
|
| | | repositoryTeams), new CollectionModel<String>(GitBlit.self().getAllTeamnames()),
|
| | | new ChoiceRenderer<String>("", ""), 10, false);
|
| | |
|
| | | // federation sets palette
|
| | |
| | | // save the repository
|
| | | GitBlit.self().updateRepositoryModel(oldName, repositoryModel, isCreate);
|
| | |
|
| | | // save the repository access list
|
| | | // repository access
|
| | | if (repositoryModel.accessRestriction.exceeds(AccessRestrictionType.NONE)) {
|
| | | // save the user access list
|
| | | Iterator<String> users = usersPalette.getSelectedChoices();
|
| | | List<String> repositoryUsers = new ArrayList<String>();
|
| | | while (users.hasNext()) {
|
| | |
| | | repositoryUsers.add(repositoryModel.owner);
|
| | | }
|
| | | GitBlit.self().setRepositoryUsers(repositoryModel, repositoryUsers);
|
| | | |
| | | // save the team access list
|
| | | Iterator<String> teams = teamsPalette.getSelectedChoices();
|
| | | List<String> repositoryTeams = new ArrayList<String>();
|
| | | while (teams.hasNext()) {
|
| | | repositoryTeams.add(teams.next());
|
| | | }
|
| | | GitBlit.self().setRepositoryTeams(repositoryModel, repositoryTeams);
|
| | | }
|
| | | } catch (GitBlitException e) {
|
| | | error(e.getMessage());
|
| | |
| | | form.add(new CheckBox("skipSizeCalculation"));
|
| | | form.add(new CheckBox("skipSummaryMetrics"));
|
| | | form.add(usersPalette);
|
| | | form.add(teamsPalette);
|
| | | form.add(federationSetsPalette);
|
| | |
|
| | | form.add(new Button("save"));
|
New file |
| | |
| | | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
| | | <html xmlns="http://www.w3.org/1999/xhtml" |
| | | xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd" |
| | | xml:lang="en" |
| | | lang="en"> |
| | |
|
| | | <wicket:extend>
|
| | | <body onload="document.getElementById('name').focus();">
|
| | | <!-- User Table -->
|
| | | <form style="padding-top:5px;" wicket:id="editForm">
|
| | | <table class="plain">
|
| | | <tbody>
|
| | | <tr><th><wicket:message key="gb.teamName"></wicket:message></th><td class="edit"><input type="text" wicket:id="name" id="name" size="30" tabindex="1" /></td></tr>
|
| | | <tr><td colspan="2"><hr></hr></td></tr>
|
| | | <tr><th style="vertical-align: top;"><wicket:message key="gb.teamMembers"></wicket:message></th><td style="padding:2px;"><span wicket:id="users"></span></td></tr>
|
| | | <tr><td colspan="2"><hr></hr></td></tr>
|
| | | <tr><th style="vertical-align: top;"><wicket:message key="gb.restrictedRepositories"></wicket:message></th><td style="padding:2px;"><span wicket:id="repositories"></span></td></tr>
|
| | | <tr><th></th><td class="editButton"><input class="btn" type="submit" value="Cancel" wicket:message="value:gb.cancel" wicket:id="cancel" tabindex="3" /> <input class="btn primary" type="submit" value="Save" wicket:message="value:gb.save" wicket:id="save" tabindex="4" /></td></tr>
|
| | | </tbody>
|
| | | </table>
|
| | | </form> |
| | | </body>
|
| | | </wicket:extend>
|
| | | </html> |
New file |
| | |
| | | /*
|
| | | * 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.wicket.pages;
|
| | |
|
| | | import java.text.MessageFormat;
|
| | | import java.util.ArrayList;
|
| | | import java.util.Collections;
|
| | | import java.util.Iterator;
|
| | | import java.util.List;
|
| | |
|
| | | import org.apache.wicket.PageParameters;
|
| | | import org.apache.wicket.extensions.markup.html.form.palette.Palette;
|
| | | import org.apache.wicket.markup.html.form.Button;
|
| | | import org.apache.wicket.markup.html.form.ChoiceRenderer;
|
| | | import org.apache.wicket.markup.html.form.Form;
|
| | | import org.apache.wicket.markup.html.form.TextField;
|
| | | import org.apache.wicket.model.CompoundPropertyModel;
|
| | | import org.apache.wicket.model.util.CollectionModel;
|
| | | import org.apache.wicket.model.util.ListModel;
|
| | |
|
| | | import com.gitblit.Constants.AccessRestrictionType;
|
| | | import com.gitblit.GitBlit;
|
| | | import com.gitblit.GitBlitException;
|
| | | import com.gitblit.models.RepositoryModel;
|
| | | import com.gitblit.models.TeamModel;
|
| | | import com.gitblit.utils.StringUtils;
|
| | | import com.gitblit.wicket.RequiresAdminRole;
|
| | | import com.gitblit.wicket.WicketUtils;
|
| | |
|
| | | @RequiresAdminRole
|
| | | public class EditTeamPage extends RootSubPage {
|
| | |
|
| | | private final boolean isCreate;
|
| | |
|
| | | public EditTeamPage() {
|
| | | // create constructor
|
| | | super();
|
| | | isCreate = true;
|
| | | setupPage(new TeamModel(""));
|
| | | }
|
| | |
|
| | | public EditTeamPage(PageParameters params) {
|
| | | // edit constructor
|
| | | super(params);
|
| | | isCreate = false;
|
| | | String name = WicketUtils.getTeamname(params);
|
| | | TeamModel model = GitBlit.self().getTeamModel(name);
|
| | | setupPage(model);
|
| | | }
|
| | |
|
| | | protected void setupPage(final TeamModel teamModel) {
|
| | | if (isCreate) {
|
| | | super.setupPage(getString("gb.newTeam"), "");
|
| | | } else {
|
| | | super.setupPage(getString("gb.edit"), teamModel.name);
|
| | | }
|
| | | |
| | | CompoundPropertyModel<TeamModel> model = new CompoundPropertyModel<TeamModel>(teamModel);
|
| | |
|
| | | List<String> repos = new ArrayList<String>();
|
| | | for (String repo : GitBlit.self().getRepositoryList()) {
|
| | | RepositoryModel repositoryModel = GitBlit.self().getRepositoryModel(repo);
|
| | | if (repositoryModel.accessRestriction.exceeds(AccessRestrictionType.NONE)) {
|
| | | repos.add(repo);
|
| | | }
|
| | | }
|
| | | StringUtils.sortRepositorynames(repos);
|
| | | |
| | | List<String> teamUsers = new ArrayList<String>(teamModel.users);
|
| | | Collections.sort(teamUsers);
|
| | | |
| | | final String oldName = teamModel.name;
|
| | | final Palette<String> repositories = new Palette<String>("repositories",
|
| | | new ListModel<String>(new ArrayList<String>(teamModel.repositories)),
|
| | | new CollectionModel<String>(repos), new ChoiceRenderer<String>("", ""), 10, false);
|
| | | final Palette<String> users = new Palette<String>("users", new ListModel<String>(
|
| | | new ArrayList<String>(teamUsers)), new CollectionModel<String>(GitBlit.self()
|
| | | .getAllUsernames()), new ChoiceRenderer<String>("", ""), 10, false);
|
| | | Form<TeamModel> form = new Form<TeamModel>("editForm", model) {
|
| | |
|
| | | private static final long serialVersionUID = 1L;
|
| | |
|
| | | /*
|
| | | * (non-Javadoc)
|
| | | * |
| | | * @see org.apache.wicket.markup.html.form.Form#onSubmit()
|
| | | */
|
| | | @Override
|
| | | protected void onSubmit() {
|
| | | String teamname = teamModel.name;
|
| | | if (StringUtils.isEmpty(teamname)) {
|
| | | error("Please enter a teamname!");
|
| | | return;
|
| | | }
|
| | | if (isCreate) {
|
| | | TeamModel model = GitBlit.self().getTeamModel(teamname);
|
| | | if (model != null) {
|
| | | error(MessageFormat.format("Team name ''{0}'' is unavailable.", teamname));
|
| | | return;
|
| | | }
|
| | | }
|
| | | Iterator<String> selectedRepositories = repositories.getSelectedChoices();
|
| | | List<String> repos = new ArrayList<String>();
|
| | | while (selectedRepositories.hasNext()) {
|
| | | repos.add(selectedRepositories.next().toLowerCase());
|
| | | }
|
| | | teamModel.repositories.clear();
|
| | | teamModel.repositories.addAll(repos);
|
| | |
|
| | | Iterator<String> selectedUsers = users.getSelectedChoices();
|
| | | List<String> members = new ArrayList<String>();
|
| | | while (selectedUsers.hasNext()) {
|
| | | members.add(selectedUsers.next().toLowerCase());
|
| | | }
|
| | | teamModel.users.clear();
|
| | | teamModel.users.addAll(members);
|
| | |
|
| | | try {
|
| | | GitBlit.self().updateTeamModel(oldName, teamModel, isCreate);
|
| | | } catch (GitBlitException e) {
|
| | | error(e.getMessage());
|
| | | return;
|
| | | }
|
| | | setRedirect(false);
|
| | | if (isCreate) {
|
| | | // create another team
|
| | | info(MessageFormat.format("New team ''{0}'' successfully created.",
|
| | | teamModel.name));
|
| | | setResponsePage(EditTeamPage.class);
|
| | | } else {
|
| | | // back to users page
|
| | | setResponsePage(UsersPage.class);
|
| | | }
|
| | | }
|
| | | };
|
| | |
|
| | | // field names reflective match TeamModel fields
|
| | | form.add(new TextField<String>("name"));
|
| | | form.add(repositories);
|
| | | form.add(users);
|
| | |
|
| | | form.add(new Button("save"));
|
| | | Button cancel = new Button("cancel") {
|
| | | private static final long serialVersionUID = 1L;
|
| | |
|
| | | @Override
|
| | | public void onSubmit() {
|
| | | setResponsePage(UsersPage.class);
|
| | | }
|
| | | };
|
| | | cancel.setDefaultFormProcessing(false);
|
| | | form.add(cancel);
|
| | |
|
| | | add(form);
|
| | | }
|
| | | }
|
| | |
| | | <wicket:extend>
|
| | | <body onload="document.getElementById('username').focus();">
|
| | | <!-- User Table -->
|
| | | <form wicket:id="editForm">
|
| | | <form style="padding-top:5px;" wicket:id="editForm">
|
| | | <table class="plain">
|
| | | <tbody>
|
| | | <tr><th><wicket:message key="gb.username"></wicket:message></th><td class="edit"><input type="text" wicket:id="username" id="username" size="30" tabindex="1" /></td></tr>
|
| | |
| | | <tr><th><wicket:message key="gb.confirmPassword"></wicket:message></th><td class="edit"><input type="password" wicket:id="confirmPassword" size="30" tabindex="3" /></td></tr>
|
| | | <tr><th><wicket:message key="gb.canAdmin"></wicket:message></th><td class="edit"><input type="checkbox" wicket:id="canAdmin" tabindex="6" /> <i><wicket:message key="gb.canAdminDescription"></wicket:message></i></td></tr>
|
| | | <tr><th><wicket:message key="gb.excludeFromFederation"></wicket:message></th><td class="edit"><input type="checkbox" wicket:id="excludeFromFederation" tabindex="7" /> <i><wicket:message key="gb.excludeFromFederationDescription"></wicket:message></i></td></tr>
|
| | | <tr><td colspan="2"><hr></hr></td></tr>
|
| | | <tr><th style="vertical-align: top;"><wicket:message key="gb.teamMemberships"></wicket:message></th><td style="padding:2px;"><span wicket:id="teams"></span></td></tr>
|
| | | <tr><td colspan="2"><hr></hr></td></tr>
|
| | | <tr><th style="vertical-align: top;"><wicket:message key="gb.restrictedRepositories"></wicket:message></th><td style="padding:2px;"><span wicket:id="repositories"></span></td></tr>
|
| | | <tr><th></th><td class="editButton"><input class="btn" type="submit" value="Cancel" wicket:message="value:gb.cancel" wicket:id="cancel" tabindex="8" /> <input class="btn primary" type="submit" value="Save" wicket:message="value:gb.save" wicket:id="save" tabindex="9" /></td></tr>
|
| | | </tbody>
|
| | |
| | |
|
| | | import java.text.MessageFormat;
|
| | | import java.util.ArrayList;
|
| | | import java.util.Collections;
|
| | | import java.util.Iterator;
|
| | | import java.util.List;
|
| | |
|
| | |
| | | import com.gitblit.GitBlitException;
|
| | | import com.gitblit.Keys;
|
| | | import com.gitblit.models.RepositoryModel;
|
| | | import com.gitblit.models.TeamModel;
|
| | | import com.gitblit.models.UserModel;
|
| | | import com.gitblit.utils.StringUtils;
|
| | | import com.gitblit.wicket.RequiresAdminRole;
|
| | |
| | | repos.add(repo);
|
| | | }
|
| | | }
|
| | | List<String> userTeams = new ArrayList<String>();
|
| | | for (TeamModel team : userModel.teams) {
|
| | | userTeams.add(team.name);
|
| | | }
|
| | | Collections.sort(userTeams);
|
| | | |
| | | final String oldName = userModel.username;
|
| | | final Palette<String> repositories = new Palette<String>("repositories",
|
| | | new ListModel<String>(new ArrayList<String>(userModel.repositories)),
|
| | | new CollectionModel<String>(repos), new ChoiceRenderer<String>("", ""), 10, false);
|
| | | final Palette<String> teams = new Palette<String>("teams", new ListModel<String>(
|
| | | new ArrayList<String>(userTeams)), new CollectionModel<String>(GitBlit.self()
|
| | | .getAllTeamnames()), new ChoiceRenderer<String>("", ""), 10, false);
|
| | | Form<UserModel> form = new Form<UserModel>("editForm", model) {
|
| | |
|
| | | private static final long serialVersionUID = 1L;
|
| | |
| | | return;
|
| | | }
|
| | | }
|
| | | boolean rename = !StringUtils.isEmpty(oldName) && !oldName.equalsIgnoreCase(username);
|
| | | boolean rename = !StringUtils.isEmpty(oldName)
|
| | | && !oldName.equalsIgnoreCase(username);
|
| | | if (!userModel.password.equals(confirmPassword.getObject())) {
|
| | | error("Passwords do not match!");
|
| | | return;
|
| | |
| | | }
|
| | | userModel.repositories.clear();
|
| | | userModel.repositories.addAll(repos);
|
| | |
|
| | | Iterator<String> selectedTeams = teams.getSelectedChoices();
|
| | | userModel.teams.clear();
|
| | | while (selectedTeams.hasNext()) {
|
| | | TeamModel team = GitBlit.self().getTeamModel(selectedTeams.next());
|
| | | if (team == null) {
|
| | | continue;
|
| | | }
|
| | | userModel.teams.add(team);
|
| | | }
|
| | |
|
| | | try {
|
| | | GitBlit.self().updateUserModel(oldName, userModel, isCreate);
|
| | | } catch (GitBlitException e) {
|
| | |
| | | form.add(new CheckBox("canAdmin"));
|
| | | form.add(new CheckBox("excludeFromFederation"));
|
| | | form.add(repositories);
|
| | | form.add(teams);
|
| | |
|
| | | form.add(new Button("save"));
|
| | | Button cancel = new Button("cancel") {
|
| | |
| | | lang="en">
|
| | | <body>
|
| | | <wicket:extend>
|
| | | <div wicket:id="teamsPanel">[teams panel]</div>
|
| | |
|
| | | <div wicket:id="usersPanel">[users panel]</div>
|
| | | </wicket:extend>
|
| | | </body>
|
| | |
| | | package com.gitblit.wicket.pages;
|
| | |
|
| | | import com.gitblit.wicket.RequiresAdminRole;
|
| | | import com.gitblit.wicket.panels.TeamsPanel;
|
| | | import com.gitblit.wicket.panels.UsersPanel;
|
| | |
|
| | | @RequiresAdminRole
|
| | |
| | | super();
|
| | | setupPage("", "");
|
| | |
|
| | | add(new TeamsPanel("teamsPanel", showAdmin).setVisible(showAdmin));
|
| | | |
| | | add(new UsersPanel("usersPanel", showAdmin).setVisible(showAdmin));
|
| | | }
|
| | | }
|
New file |
| | |
| | | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
| | | <html xmlns="http://www.w3.org/1999/xhtml" |
| | | xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd" |
| | | xml:lang="en" |
| | | lang="en"> |
| | |
|
| | | <body>
|
| | | <wicket:panel>
|
| | |
|
| | | <div wicket:id="adminPanel">[admin links]</div>
|
| | | |
| | | <table class="repositories">
|
| | | <tr>
|
| | | <th class="left">
|
| | | <img style="vertical-align: middle; border: 1px solid #888; background-color: white;" src="users_16x16.png"/>
|
| | | <wicket:message key="gb.teams">[teams]</wicket:message>
|
| | | </th>
|
| | | <th class="right"></th>
|
| | | </tr>
|
| | | <tbody> |
| | | <tr wicket:id="teamRow">
|
| | | <td class="left" ><div class="list" wicket:id="teamname">[teamname]</div></td>
|
| | | <td class="rightAlign"><span wicket:id="teamLinks"></span></td> |
| | | </tr>
|
| | | </tbody>
|
| | | </table>
|
| | | |
| | | <wicket:fragment wicket:id="adminLinks">
|
| | | <!-- page nav links --> |
| | | <div class="admin_nav">
|
| | | <img style="vertical-align: middle;" src="add_16x16.png"/>
|
| | | <a wicket:id="newTeam">
|
| | | <wicket:message key="gb.newTeam"></wicket:message>
|
| | | </a>
|
| | | </div> |
| | | </wicket:fragment>
|
| | | |
| | | <wicket:fragment wicket:id="teamAdminLinks">
|
| | | <span class="link"><a wicket:id="editTeam"><wicket:message key="gb.edit">[edit]</wicket:message></a> | <a wicket:id="deleteTeam"><wicket:message key="gb.delete">[delete]</wicket:message></a></span>
|
| | | </wicket:fragment>
|
| | | |
| | | </wicket:panel>
|
| | | </body>
|
| | | </html> |
New file |
| | |
| | | /*
|
| | | * 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.wicket.panels;
|
| | |
|
| | | import java.text.MessageFormat;
|
| | | import java.util.List;
|
| | |
|
| | | import org.apache.wicket.markup.html.link.BookmarkablePageLink;
|
| | | import org.apache.wicket.markup.html.link.Link;
|
| | | import org.apache.wicket.markup.html.panel.Fragment;
|
| | | import org.apache.wicket.markup.repeater.Item;
|
| | | import org.apache.wicket.markup.repeater.data.DataView;
|
| | | import org.apache.wicket.markup.repeater.data.ListDataProvider;
|
| | |
|
| | | import com.gitblit.GitBlit;
|
| | | import com.gitblit.wicket.WicketUtils;
|
| | | import com.gitblit.wicket.pages.EditTeamPage;
|
| | |
|
| | | public class TeamsPanel extends BasePanel {
|
| | |
|
| | | private static final long serialVersionUID = 1L;
|
| | |
|
| | | public TeamsPanel(String wicketId, final boolean showAdmin) {
|
| | | super(wicketId);
|
| | |
|
| | | Fragment adminLinks = new Fragment("adminPanel", "adminLinks", this);
|
| | | adminLinks.add(new BookmarkablePageLink<Void>("newTeam", EditTeamPage.class));
|
| | | add(adminLinks.setVisible(showAdmin));
|
| | |
|
| | | final List<String> teamnames = GitBlit.self().getAllTeamnames();
|
| | | DataView<String> teamsView = new DataView<String>("teamRow", new ListDataProvider<String>(
|
| | | teamnames)) {
|
| | | private static final long serialVersionUID = 1L;
|
| | | private int counter;
|
| | |
|
| | | @Override
|
| | | protected void onBeforeRender() {
|
| | | super.onBeforeRender();
|
| | | counter = 0;
|
| | | }
|
| | |
|
| | | public void populateItem(final Item<String> item) {
|
| | | final String entry = item.getModelObject();
|
| | | LinkPanel editLink = new LinkPanel("teamname", "list", entry, EditTeamPage.class,
|
| | | WicketUtils.newTeamnameParameter(entry));
|
| | | WicketUtils.setHtmlTooltip(editLink, getString("gb.edit") + " " + entry);
|
| | | item.add(editLink);
|
| | | Fragment teamLinks = new Fragment("teamLinks", "teamAdminLinks", this);
|
| | | teamLinks.add(new BookmarkablePageLink<Void>("editTeam", EditTeamPage.class,
|
| | | WicketUtils.newTeamnameParameter(entry)));
|
| | | Link<Void> deleteLink = new Link<Void>("deleteTeam") {
|
| | |
|
| | | private static final long serialVersionUID = 1L;
|
| | |
|
| | | @Override
|
| | | public void onClick() {
|
| | | if (GitBlit.self().deleteTeam(entry)) {
|
| | | teamnames.remove(entry);
|
| | | info(MessageFormat.format("Team ''{0}'' deleted.", entry));
|
| | | } else {
|
| | | error(MessageFormat.format("Failed to delete team ''{0}''!", entry));
|
| | | }
|
| | | }
|
| | | };
|
| | | deleteLink.add(new JavascriptEventConfirmation("onclick", MessageFormat.format(
|
| | | "Delete team \"{0}\"?", entry)));
|
| | | teamLinks.add(deleteLink);
|
| | | item.add(teamLinks);
|
| | |
|
| | | WicketUtils.setAlternatingBackground(item, counter);
|
| | | counter++;
|
| | | }
|
| | | };
|
| | | add(teamsView.setVisible(showAdmin));
|
| | | }
|
| | | }
|
| | |
| | | <tr>
|
| | | <th class="left">
|
| | | <img style="vertical-align: middle; border: 1px solid #888; background-color: white;" src="user_16x16.png"/>
|
| | | <wicket:message key="gb.username">[username]</wicket:message>
|
| | | <wicket:message key="gb.users">[users]</wicket:message>
|
| | | </th>
|
| | | <th class="right"></th>
|
| | | </tr>
|
| | |
| | | file.delete();
|
| | | IUserService service = new FileUserService(file);
|
| | | testUsers(service);
|
| | | testTeams(service);
|
| | | file.delete();
|
| | | }
|
| | |
|
| | |
| | | file.delete();
|
| | | IUserService service = new ConfigUserService(file);
|
| | | testUsers(service);
|
| | | testTeams(service);
|
| | | file.delete();
|
| | | }
|
| | |
|
| | |
| | | testUser = service.getUserModel("test");
|
| | | assertTrue(testUser.hasRepository("newrepo1"));
|
| | | }
|
| | |
|
| | | protected void testTeams(IUserService service) {
|
| | |
|
| | | // confirm we have no teams
|
| | | assertEquals(0, service.getAllTeamNames().size());
|
| | |
|
| | | // remove newrepo1 from test user
|
| | | // now test user has no repositories
|
| | | UserModel user = service.getUserModel("test");
|
| | | user.repositories.clear();
|
| | | service.updateUserModel(user);
|
| | | user = service.getUserModel("test");
|
| | | assertEquals(0, user.repositories.size());
|
| | | assertFalse(user.canAccessRepository("newrepo1"));
|
| | | assertFalse(user.canAccessRepository("NEWREPO1"));
|
| | |
|
| | | // create test team and add test user and newrepo1
|
| | | TeamModel team = new TeamModel("testteam");
|
| | | team.addUser("test");
|
| | | team.addRepository("newrepo1");
|
| | | service.updateTeamModel(team);
|
| | |
|
| | | // confirm 1 user and 1 repo
|
| | | team = service.getTeamModel("testteam");
|
| | | assertEquals(1, team.repositories.size());
|
| | | assertEquals(1, team.users.size());
|
| | |
|
| | | // confirm team membership
|
| | | user = service.getUserModel("test");
|
| | | assertEquals(0, user.repositories.size());
|
| | | assertEquals(1, user.teams.size());
|
| | |
|
| | | // confirm team access
|
| | | assertTrue(team.hasRepository("newrepo1"));
|
| | | assertTrue(user.hasTeamAccess("newrepo1"));
|
| | | assertTrue(team.hasRepository("NEWREPO1"));
|
| | | assertTrue(user.hasTeamAccess("NEWREPO1"));
|
| | |
|
| | | // rename the team and add new repository
|
| | | team.addRepository("newrepo2");
|
| | | team.name = "testteam2";
|
| | | service.updateTeamModel("testteam", team);
|
| | |
|
| | | team = service.getTeamModel("testteam2");
|
| | | user = service.getUserModel("test");
|
| | |
|
| | | // confirm user and team can access newrepo2
|
| | | assertEquals(2, team.repositories.size());
|
| | | assertTrue(team.hasRepository("newrepo2"));
|
| | | assertTrue(user.hasTeamAccess("newrepo2"));
|
| | | assertTrue(team.hasRepository("NEWREPO2"));
|
| | | assertTrue(user.hasTeamAccess("NEWREPO2"));
|
| | |
|
| | | // delete testteam2
|
| | | service.deleteTeam("testteam2");
|
| | | team = service.getTeamModel("testteam2");
|
| | | user = service.getUserModel("test");
|
| | |
|
| | | // confirm team does not exist and user can not access newrepo1 and 2
|
| | | assertEquals(null, team);
|
| | | assertFalse(user.canAccessRepository("newrepo1"));
|
| | | assertFalse(user.canAccessRepository("newrepo2"));
|
| | |
|
| | | // create new team and add it to user
|
| | | // this tests the inverse team creation/team addition
|
| | | team = new TeamModel("testteam");
|
| | | team.addRepository("NEWREPO1");
|
| | | team.addRepository("NEWREPO2");
|
| | | user.teams.add(team);
|
| | | service.updateUserModel(user);
|
| | |
|
| | | // confirm the inverted team addition
|
| | | user = service.getUserModel("test");
|
| | | team = service.getTeamModel("testteam");
|
| | | assertTrue(user.hasTeamAccess("newrepo1"));
|
| | | assertTrue(user.hasTeamAccess("newrepo2"));
|
| | | assertTrue(team.hasUser("test"));
|
| | |
|
| | | // drop testteam from user and add nextteam to user
|
| | | team = new TeamModel("nextteam");
|
| | | team.addRepository("NEWREPO1");
|
| | | team.addRepository("NEWREPO2");
|
| | | user.teams.clear();
|
| | | user.teams.add(team);
|
| | | service.updateUserModel(user);
|
| | |
|
| | | // confirm implicit drop
|
| | | user = service.getUserModel("test");
|
| | | team = service.getTeamModel("testteam");
|
| | | assertTrue(user.hasTeamAccess("newrepo1"));
|
| | | assertTrue(user.hasTeamAccess("newrepo2"));
|
| | | assertFalse(team.hasUser("test"));
|
| | | team = service.getTeamModel("nextteam");
|
| | | assertTrue(team.hasUser("test"));
|
| | |
|
| | | // delete the user and confirm team no longer has user
|
| | | service.deleteUser("test");
|
| | | team = service.getTeamModel("testteam");
|
| | | assertFalse(team.hasUser("test"));
|
| | |
|
| | | // delete both teams
|
| | | service.deleteTeam("testteam");
|
| | | service.deleteTeam("nextteam");
|
| | | assertEquals(0, service.getAllTeamNames().size());
|
| | | }
|
| | | } |