Changeset 35298


Ignore:
Timestamp:
2021-08-16T20:17:05+12:00 (3 years ago)
Author:
anupama
Message:

Working version of hierarchical groups: now the usersDB stores expandedGroups instead of user-entered groups in the 'roles' table. The expandedGroups listing is therefore now consulted behind-the-scenes/automatically by HttpServletRequest.isUserInRole(), which is configured (in tomcat servlet configuration file greenstone3.xml) to query the roles table of the userDB. The new UserTermInfo.compactGroups() function takes care that the display value of the groups listing in the administration pages is the compacted version: it's not exactly the same as the user-entered value as the compactedGroups listing is in natural (alphabetic/ASCII) order and with duplicates removed.

Location:
main/trunk/greenstone3/src/java/org/greenstone/gsdl3
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • main/trunk/greenstone3/src/java/org/greenstone/gsdl3/service/Authentication.java

    r35288 r35298  
    163163  protected static final String COLLECTION = "collection";
    164164  protected static final String GROUPS = "groups";
     165  protected static final String COMPACTED_GROUPS = "compacted_groups";
    165166  protected static final String EXPANDED_GROUPS = "expanded_groups";
    166167  protected static final String STATUS = "status";
     
    576577        }
    577578        logger.debug("username="+username+", groups = "+groups);
     579        logger.debug("expandedGroups would be: "+UserTermInfo.expandGroups(groups));
     580        logger.debug("compactedGroups would be: "+UserTermInfo.compactGroups(groups));
     581       
    578582        if ((userInformation == null || username == null) && _userOpList.contains(op))
    579583        {
     
    788792          }
    789793         
    790           groups = null;
     794          groups = null; // doesn't matter if local groups var keeps value in compactedGroups or expandedGroups form,
     795          // as addUser() and addUserInformationToNode() will ensure use of expanded and compacted forms respectively
    791796          String status = null;
    792797          String comment = null;
     
    798803
    799804          } else {
    800             groups = retrieveDataForUser(previousUsername, GROUPS);
     805            groups = retrieveDataForUser(previousUsername, EXPANDED_GROUPS);
    801806            status = retrieveDataForUser(previousUsername, STATUS);
    802807            comment = retrieveDataForUser(previousUsername, COMMENT);
     
    872877
    873878            DerbyWrapper derbyWrapper = openDatabase();
    874             String chpa_groups = retrieveDataForUser(user_name, GROUPS);
     879            String chpa_groups = retrieveDataForUser(user_name, EXPANDED_GROUPS); // userDB now stores expandedGroups, not compactedGroups
    875880            String chpa_comment = "password_changed_by_user";
    876881            String info = derbyWrapper.modifyUserInfo(user_name, hashPassword(newPassword), chpa_groups, null, chpa_comment, null);
     
    10791084            for (int i = 0; i < userQueryResult.getSize(); i++)
    10801085            {
     1086                // can call either getExpandedGroups() or getOrigGroups() to check admin group is in there:
    10811087                if (((UserTermInfo) userInfo.get(i)).getOrigGroups() != null && ((UserTermInfo) userInfo.get(i)).getOrigGroups().matches(".*\\badministrator\\b.*"))
    10821088                {
     
    11381144    {
    11391145        newGroups = newGroups.replaceAll(" ", "");
     1146        // store only expandedGroups in DB now
     1147        newGroups = UserTermInfo.expandGroups(newGroups);
    11401148
    11411149        //Check if the user already exists
     
    12071215                    break;
    12081216                }
    1209                 else if (dataType.equals(GROUPS))
     1217                else if (dataType.equals(COMPACTED_GROUPS))
    12101218                {
    12111219                    data = ((UserTermInfo) userInfo.get(i)).getOrigGroups();
     
    12521260            Element user_node = doc.createElement(GSXML.USER_NODE_ELEM);
    12531261            String username = ((UserTermInfo) userInfo.get(i)).getUsername();
    1254             String groups = ((UserTermInfo) userInfo.get(i)).getOrigGroups();
    1255             // Retrieve original (stored, user-entered) and not expanded groups
    1256             // as getUserNodeList only used in addUserInformationToNode() which
    1257             // allows displaying and modifying user info pages
    1258             // and authenticating admin user which doesn't require expanded groups.
     1262            String compactedGroups = ((UserTermInfo) userInfo.get(i)).getOrigGroups();
     1263            // Since this is used for display in admin pages, get compacted (more similar to user-entered) groups
     1264            // and not expanded groups, as getUserNodeList only used in addUserInformationToNode() which
     1265            // allows displaying and modifying user info pages and authenticating admin user,
     1266            // none of which requires expanded groups.
    12591267            String accountstatus = ((UserTermInfo) userInfo.get(i)).getAccountStatus();
    12601268            String comment = ((UserTermInfo) userInfo.get(i)).getComment();
    12611269            String email = ((UserTermInfo) userInfo.get(i)).getEmail();
    12621270            user_node.setAttribute(USERNAME, username);
    1263             user_node.setAttribute(GROUPS, groups);
     1271            user_node.setAttribute(GROUPS, compactedGroups);
    12641272            user_node.setAttribute(STATUS, accountstatus);
    12651273            user_node.setAttribute(COMMENT, comment);
  • main/trunk/greenstone3/src/java/org/greenstone/gsdl3/util/DerbyWrapper.java

    r30240 r35298  
    3333import org.greenstone.util.GlobalProperties;
    3434
     35/**
     36 * The UserDB stores the groups in the "roles" table in expanded form, i.e. expandedGroups.
     37 * This is what is retrieved as well. Call UserTermInfo.compactGroup(expandedGroups)
     38 * to get the compacted form closer to what the user may have entered.
     39*/
    3540public class DerbyWrapper
    3641{
     
    474479    }
    475480
    476     public boolean addUser(String username, String password, String groups, String accountstatus, String comment, String email)
     481    public boolean addUser(String username, String password, String expandedGroups, String accountstatus, String comment, String email)
    477482    {
    478483        try
     
    482487            state.execute(sql_insert_user);
    483488
    484             String[] groupArray = groups.split(",");
     489            String[] groupArray = expandedGroups.split(",");
    485490            for (String g : groupArray)
    486491            {
     
    719724    }
    720725
    721     public String modifyUserInfo(String username, String new_password, String groups, String accountstatus, String comment, String email)
     726    public String modifyUserInfo(String username, String new_password, String expandedGroups, String accountstatus, String comment, String email)
    722727    {
    723728        try
     
    750755            state.execute(sql_delete_groups);
    751756
    752             String[] groupsArray = groups.split(",");
     757            String[] groupsArray = expandedGroups.split(",");
    753758            for (String g : groupsArray)
    754759            {
  • main/trunk/greenstone3/src/java/org/greenstone/gsdl3/util/ModifyUsersDB.java

    r35287 r35298  
    121121            else
    122122            { // add new user
     123                groups = UserTermInfo.expandGroups(groups);
    123124                //System.err.println("**** Trying to add user: ");
    124125                //System.err.println("**** " + username + " " + password + " " + groups + " " + accountstatus + " " + comment + " " + email);
     
    138139            if (groups.equals(""))
    139140            {
    140                 // groups should be origGroups and not expandedGroups
    141                 // As we want to store the groups in DB as entered
    142                 // and not as programmatically expanded.
    143                 groups = user.getOrigGroups();
     141                // groups should be expandedGroups because we no longer store the groups in userDB
     142                // as user-entered or compacted, but as programmatically expanded.
     143                // This allows HttpServletRequest.isUserInRole() to now automatically retrieve the
     144                // expandedGroups list of a user to check collectionConfig.xml security elements against.
     145               
     146                groups = user.getExpandedGroups();
     147            } else {
     148                groups = UserTermInfo.expandGroups(groups); // ensure groups are stored expanded in userDB
    144149            }
    145150            if (accountstatus.equals(""))
  • main/trunk/greenstone3/src/java/org/greenstone/gsdl3/util/UserQueryResult.java

    r35286 r35298  
    3737    }
    3838
    39     public void addUserTerm(String username, String password, String groups, String accountstatus, String comment, String email)
     39    public void addUserTerm(String username, String password, String expandedGroups, String accountstatus, String comment, String email)
    4040    {
    41         UserTermInfo ui = new UserTermInfo(username, password, groups,
     41        UserTermInfo ui = new UserTermInfo(username, password, expandedGroups,
    4242                           accountstatus, comment, email);     
    4343        users.add(ui);
  • main/trunk/greenstone3/src/java/org/greenstone/gsdl3/util/UserTermInfo.java

    r35287 r35298  
    2727    private String username;
    2828    private String password;
    29     private String origGroups;
    30         private String expandedGroups = null;
     29        private String origGroups = null; // TODO: rename to compactedGroups and get-method, and update method calls accordingly
     30        private String expandedGroups;
    3131    private String accountstatus;
    3232    private String comment;
    3333    private String email;
    3434
    35     public UserTermInfo(String username, String password, String groups,
     35    // This constructor is called to instantiate a UserTermInfo object from entries in the userDB
     36    // which now stores expandedGroups instead of the user-entered origGroups
     37    // Use the other constructor if you know the user-entered groups
     38    public UserTermInfo(String username, String password, String expandedGroups,
    3639            String accountStatus, String comment, String email) {
    3740    this.username = username;
    3841    this.password = password;
    39     this.origGroups = groups;
    40     //this.expandedGroups = UserTermInfo.expandGroups(this.origGroups); // Will do lazy evaluation
     42
     43
     44    this.expandedGroups = expandedGroups;
     45    this.origGroups = UserTermInfo.compactGroups(this.expandedGroups); // alternative: don't set here and do lazy evaluation when getOrigGroups() is called
    4146    this.accountstatus = accountStatus;
    4247    this.comment = comment;
    4348    this.email = email;
    4449    }
     50
     51   
     52    // Not used, for completion's sake:
     53    // If you only have user-entered groups at hand, pass in null or "" for expandedGroups
     54    // to create a UserTermInfo object, which will work out the expandedGroups.
     55    /*
     56    public UserTermInfo(String username, String password, String userEnteredGroups, String expandedGroups,
     57            String accountStatus, String comment, String email) {
     58    this.username = username;
     59    this.password = password;
     60   
     61    this.origGroups = (userEnteredGroups == null) ? "" : userEnteredGroups;
     62    if(expandedGroups == null || expandedGroups.equals("")) {
     63        this.expandedGroups = UserTermInfo.expandGroups(this.origGroups);
     64    }
     65    this.accountstatus = accountStatus;
     66    this.comment = comment;
     67    this.email = email;
     68    }
     69    */
    4570   
    4671    public String toString()
     
    6792    public String getOrigGroups() {
    6893    return origGroups;
    69     }   
    70    
    71     public String getExpandedGroups() {
    72     //return expandedGroups;
    73     //return UserTermInfo.expandGroups(this.origGroups); // not storing, evaluating on demand
    74    
     94    //return UserTermInfo.compactGroups(this.expandedGroups); // not storing, evaluating on demand
     95
    7596    // lazy evaluation: getExpandedGroups() doesn't get called on every enquiry about
    7697    // a UserTermInfo struct. It only gets called on authenticated-ping system-action
     
    79100    // don't pre-calculate and store the expanded groups.
    80101    // Is it meaningful to store this at all Ever or better to always evaluate on demand?
    81     if(expandedGroups == null) {
    82         expandedGroups = UserTermInfo.expandGroups(this.origGroups);
    83     }
     102   
     103    }   
     104   
     105    public String getExpandedGroups() {
    84106    return expandedGroups;
     107    //return UserTermInfo.expandGroups(this.origGroups); // not storing, evaluating on demand
     108   
     109    // lazy evaluation: getExpandedGroups() doesn't get called on every enquiry about
     110    // a UserTermInfo struct. It only gets called on authenticated-ping system-action
     111    // (including via ServletRealmCheck being run from run from commandline or gliserver.pl)
     112    // So for a whole list of users and the UserTermInfo struct of each,
     113    // don't pre-calculate and store the expanded groups.
     114    // Is it meaningful to store this at all Ever or better to always evaluate on demand?
     115    ///if(expandedGroups == null) {
     116    ///    expandedGroups = UserTermInfo.expandGroups(this.origGroups);
     117    ///}
     118    ///return expandedGroups;
    85119    }
    86120
     
    101135    }
    102136
     137    /**
     138     * The user may have entered groups of the form "nz.ac.waikato.toto, nz.ac.waikato.cs.pinky, admin"
     139     * This would have got stored in the userDB in expanded form as: "admin, nz, nz.ac, nz.ac.waikato, nz.ac.waikato.cs, nz.ac.waikato.cs.pinky, nz.ac.waikato.toto"
     140     * This method should return the user entered form again, or the most compact form at any rate. So duplicates removed and in natural (alphabetic) ordering.
     141     * @param groups: given possibly expanded_groups, this method contracts them back to what the user would have entered for groups
     142     * @return the user-entered form for groups     
     143     * Tested:
     144     * String groups = "administrator,all-collections-editor,nz,nz.ac,nz.ac.waikato,nz.ac.waikato.cs.pinkies, nz.ac.waikato.totos.toto, nz.ac.waikato.cs.pinkies.pinky, personal-collections-editor";
     145     * and shuffled:
     146     * String groups = "nz,nz.ac,nz.ac.waikato,administrator,nz.ac.waikato.cs.pinkies, nz.ac.waikato.totos.toto, nz.ac.waikato.cs.pinkies.pinky, personal-collections-editor,all-collections-editor";
     147     * Result: administrator,all-collections-editor,nz.ac.waikato.cs.pinkies.pinky,nz.ac.waikato.totos.toto,personal-collections-editor:
     148     */
     149    public static String compactGroups(String groups) {
     150    if(groups.indexOf('.') == -1) {
     151        return groups;
     152    }
     153    else if(groups.equals("")) { // can return quickly if empty string
     154        return groups;
     155    }
     156    else { // groups is a non-zero comma-separated list because we've already tested there's a period mark in it somewhere
     157
     158        // Normalize step: make sure what's in the database has no duplicates and is in natural ordering       
     159        Set<String> groupSet = new TreeSet<String>();  // uses default (alphabetic/ascii) comparator
     160        String[] indivGroups = groups.split("\\s*,\\s*"); // simultaneously get rid of whitespace surrounding commas
     161        for(String s : indivGroups) {
     162        groupSet.add(s); // will get rid of duplicate prefixes, although it "should" have been stored without duplicates
     163        // But normalizing here ensures that the rest of the method always works with the correct ordering
     164        }
     165
     166        indivGroups = new String[groupSet.size()];
     167        indivGroups = groupSet.toArray(indivGroups); // now indivGroups is in natural ordering
     168        groupSet.clear();
     169        groupSet = null;
     170
     171        // The actual algorithm is now simple: if next group string doesn't start with current one
     172        // (so current group string is not prefix of next group string), we add it to our compactGroups list
     173        Set<String> compactGroups = new TreeSet<String>(); // uses default (alphabetic/ascii) comparator
     174        String currGroup, nextGroup;
     175        for(int i = 0; i < indivGroups.length-1; i++) {
     176        currGroup = indivGroups[i];
     177        nextGroup = indivGroups[i+1];
     178
     179        if(!nextGroup.startsWith(currGroup)) {
     180            compactGroups.add(currGroup);
     181        }
     182        }
     183
     184        // don't forget to add in the very last group string
     185        compactGroups.add(indivGroups[indivGroups.length-1]);
     186
     187
     188        // recreate comma-separate string of groups
     189        StringBuilder compactGroupList = new StringBuilder();
     190        for(String s : compactGroups) {
     191        compactGroupList.append(s);
     192        compactGroupList.append(',');
     193        }
     194        compactGroupList.deleteCharAt(compactGroupList.length()-1); // remove extraneous comma
     195       
     196        return compactGroupList.toString();
     197    }
     198   
     199   
     200    /*
     201    else {
     202     
     203
     204        String[] indivGroups = groups.split("\\s*,\\s*"); // simultaneously get rid of whitespace surrounding commas
     205        if(indivGroups.length == 0) {
     206        return groups;
     207        }
     208        Set<String> compactGroups = new TreeSet<String>();
     209        String currGroup = indivGroups[indivGroups.length-1];
     210        for(int i = indivGroups.length-1; i--; i > 0) {
     211        String prevGroup = indivGroups[i-1];
     212       
     213        int indexOfPeriod = currGroup.indexOf('.');
     214        if(indexOfPeriod == -1) {
     215           compactGroups.add(currGroup);
     216           currGroup = prevGroup;
     217        }
     218        else {
     219       
     220            if(currGroup.startsWith(prevGroup)) {
     221            compactGroups.add(currGroup);
     222            if(i == 0) {
     223                break;             
     224            } else {
     225                i--;
     226                currGroup = indivGroups[i];
     227            }
     228            }
     229            else {
     230            compactGroups.add(currGroup);
     231            currGroup = prevGroup;
     232            }
     233        }
     234        }
     235    }
     236    */
     237    }
     238
     239   
    103240    /** We might have groups of the form katoa.maori.(placename.)iwi-name.hapu-name.personal-name
    104241     * ("katoa" means all, we could also use "mema" meaning member for this initial position).
     
    138275        return groups;
    139276    }
     277    else if(groups.equals("")) { // can return quickly if empty string
     278        return groups;
     279    }
    140280    else {
    141281        Set<String> allGroups = new TreeSet<String>();
     
    159299   
    160300
    161         StringBuilder expandedGroupList = new StringBuilder();//"<groups = \"");
     301        StringBuilder expandedGroupList = new StringBuilder();
    162302        for(String s : allGroups) {
    163303        expandedGroupList.append(s);
     
    165305        }
    166306        expandedGroupList.deleteCharAt(expandedGroupList.length()-1); // remove extraneous comma
    167        
    168         //expandedGroupList.append("\">");
    169307       
    170308        return expandedGroupList.toString();
  • main/trunk/greenstone3/src/java/org/greenstone/gsdl3/util/txt2usersDB.java

    r35287 r35298  
    166166                                password = Authentication.hashPassword(password);
    167167                            } // if > 8 chars, password for user being added was already encrypted (hashed-and-hexed)
    168                             dw.addUser(username, password, groups, accountstatus, comment, email);
     168                            dw.addUser(username, password, UserTermInfo.expandGroups(groups), accountstatus, comment, email);
    169169                        }
    170170
     
    185185                            }
    186186
    187                             // groups should be origGroups and not expandedGroups
    188                             // As we want to store the groups in DB as entered
    189                             // and not as programmatically expanded.
    190                             groups = groups.equals("") ? user.getOrigGroups() : groups;
     187                            // groups should be expandedGroups because we no longer store the groups in userDB
     188                            // as user-entered or compacted, but as programmatically expanded.
     189                            // This allows HttpServletRequest.isUserInRole() to now automatically retrieve the
     190                            // expandedGroups list of a user to check collectionConfig.xml security elements against.
     191
     192                            groups = groups.equals("") ? user.getExpandedGroups() : UserTermInfo.expandGroups(groups);
    191193                            accountstatus = accountstatus.equals("") ? user.getAccountStatus() : accountstatus;
    192194                            comment = comment.equals("") ? user.getComment() : comment;
Note: See TracChangeset for help on using the changeset viewer.