github.com/cyverse/go-irodsclient@v0.13.2/irods/fs/usergroup.go (about)

     1  package fs
     2  
     3  import (
     4  	"fmt"
     5  	"strconv"
     6  
     7  	"github.com/cyverse/go-irodsclient/irods/common"
     8  	"github.com/cyverse/go-irodsclient/irods/connection"
     9  	"github.com/cyverse/go-irodsclient/irods/message"
    10  	"github.com/cyverse/go-irodsclient/irods/types"
    11  	"github.com/cyverse/go-irodsclient/irods/util"
    12  	"golang.org/x/xerrors"
    13  )
    14  
    15  // GetGroup returns the group
    16  func GetGroup(conn *connection.IRODSConnection, group string) (*types.IRODSUser, error) {
    17  	if conn == nil || !conn.IsConnected() {
    18  		return nil, xerrors.Errorf("connection is nil or disconnected")
    19  	}
    20  
    21  	// lock the connection
    22  	conn.Lock()
    23  	defer conn.Unlock()
    24  
    25  	users := []*types.IRODSUser{}
    26  
    27  	continueQuery := true
    28  	continueIndex := 0
    29  	for continueQuery {
    30  		query := message.NewIRODSMessageQueryRequest(common.MaxQueryRows, continueIndex, 0, 0)
    31  		query.AddSelect(common.ICAT_COLUMN_USER_ID, 1)
    32  		query.AddSelect(common.ICAT_COLUMN_USER_NAME, 1)
    33  		query.AddSelect(common.ICAT_COLUMN_USER_TYPE, 1)
    34  		query.AddSelect(common.ICAT_COLUMN_USER_ZONE, 1)
    35  
    36  		condNameVal := fmt.Sprintf("= '%s'", group)
    37  		query.AddCondition(common.ICAT_COLUMN_USER_NAME, condNameVal)
    38  		condTypeVal := fmt.Sprintf("= '%s'", types.IRODSUserRodsGroup)
    39  		query.AddCondition(common.ICAT_COLUMN_USER_TYPE, condTypeVal)
    40  
    41  		queryResult := message.IRODSMessageQueryResponse{}
    42  		err := conn.Request(query, &queryResult, nil)
    43  		if err != nil {
    44  			return nil, xerrors.Errorf("failed to receive a group query result message: %w", err)
    45  		}
    46  
    47  		err = queryResult.CheckError()
    48  		if err != nil {
    49  			return nil, xerrors.Errorf("received a group query error: %w", err)
    50  		}
    51  
    52  		if queryResult.RowCount == 0 {
    53  			break
    54  		}
    55  
    56  		if queryResult.AttributeCount > len(queryResult.SQLResult) {
    57  			return nil, xerrors.Errorf("failed to receive group attributes - requires %d, but received %d attributes", queryResult.AttributeCount, len(queryResult.SQLResult))
    58  		}
    59  
    60  		pagenatedUsers := make([]*types.IRODSUser, queryResult.RowCount)
    61  
    62  		for attr := 0; attr < queryResult.AttributeCount; attr++ {
    63  			sqlResult := queryResult.SQLResult[attr]
    64  			if len(sqlResult.Values) != queryResult.RowCount {
    65  				return nil, xerrors.Errorf("failed to receive group rows - requires %d, but received %d attributes", queryResult.RowCount, len(sqlResult.Values))
    66  			}
    67  
    68  			for row := 0; row < queryResult.RowCount; row++ {
    69  				value := sqlResult.Values[row]
    70  
    71  				if pagenatedUsers[row] == nil {
    72  					// create a new
    73  					pagenatedUsers[row] = &types.IRODSUser{
    74  						ID:   -1,
    75  						Zone: "",
    76  						Name: "",
    77  						Type: types.IRODSUserRodsUser,
    78  					}
    79  				}
    80  
    81  				switch sqlResult.AttributeIndex {
    82  				case int(common.ICAT_COLUMN_USER_ID):
    83  					userID, err := strconv.ParseInt(value, 10, 64)
    84  					if err != nil {
    85  						return nil, xerrors.Errorf("failed to parse user id '%s': %w", value, err)
    86  					}
    87  					pagenatedUsers[row].ID = userID
    88  				case int(common.ICAT_COLUMN_USER_ZONE):
    89  					pagenatedUsers[row].Zone = value
    90  				case int(common.ICAT_COLUMN_USER_NAME):
    91  					pagenatedUsers[row].Name = value
    92  				case int(common.ICAT_COLUMN_USER_TYPE):
    93  					pagenatedUsers[row].Type = types.IRODSUserType(value)
    94  				default:
    95  					// ignore
    96  				}
    97  			}
    98  		}
    99  
   100  		users = append(users, pagenatedUsers...)
   101  
   102  		continueIndex = queryResult.ContinueIndex
   103  		if continueIndex == 0 {
   104  			continueQuery = false
   105  		}
   106  	}
   107  
   108  	if len(users) == 0 {
   109  		return nil, xerrors.Errorf("failed to find the group for name %s: %w", group, types.NewUserNotFoundError(group))
   110  	}
   111  
   112  	return users[0], nil
   113  }
   114  
   115  // ListGroupUsers returns users in the group
   116  func ListGroupUsers(conn *connection.IRODSConnection, group string) ([]*types.IRODSUser, error) {
   117  	if conn == nil || !conn.IsConnected() {
   118  		return nil, xerrors.Errorf("connection is nil or disconnected")
   119  	}
   120  
   121  	// lock the connection
   122  	conn.Lock()
   123  	defer conn.Unlock()
   124  
   125  	users := []*types.IRODSUser{}
   126  
   127  	continueQuery := true
   128  	continueIndex := 0
   129  	for continueQuery {
   130  		query := message.NewIRODSMessageQueryRequest(common.MaxQueryRows, continueIndex, 0, 0)
   131  		query.AddSelect(common.ICAT_COLUMN_USER_ID, 1)
   132  		query.AddSelect(common.ICAT_COLUMN_USER_NAME, 1)
   133  		query.AddSelect(common.ICAT_COLUMN_USER_TYPE, 1)
   134  		query.AddSelect(common.ICAT_COLUMN_USER_ZONE, 1)
   135  
   136  		condNameVal := fmt.Sprintf("= '%s'", group)
   137  		query.AddCondition(common.ICAT_COLUMN_COLL_USER_GROUP_NAME, condNameVal)
   138  
   139  		queryResult := message.IRODSMessageQueryResponse{}
   140  		err := conn.Request(query, &queryResult, nil)
   141  		if err != nil {
   142  			return nil, xerrors.Errorf("failed to receive a group user query result message: %w", err)
   143  		}
   144  
   145  		err = queryResult.CheckError()
   146  		if err != nil {
   147  			if types.GetIRODSErrorCode(err) == common.CAT_NO_ROWS_FOUND {
   148  				// empty
   149  				break
   150  			}
   151  			return nil, xerrors.Errorf("received a group user query error: %w", err)
   152  		}
   153  
   154  		if queryResult.RowCount == 0 {
   155  			break
   156  		}
   157  
   158  		if queryResult.AttributeCount > len(queryResult.SQLResult) {
   159  			return nil, xerrors.Errorf("failed to receive group user attributes - requires %d, but received %d attributes", queryResult.AttributeCount, len(queryResult.SQLResult))
   160  		}
   161  
   162  		pagenatedUsers := make([]*types.IRODSUser, queryResult.RowCount)
   163  
   164  		for attr := 0; attr < queryResult.AttributeCount; attr++ {
   165  			sqlResult := queryResult.SQLResult[attr]
   166  			if len(sqlResult.Values) != queryResult.RowCount {
   167  				return nil, xerrors.Errorf("failed to receive group user rows - requires %d, but received %d attributes", queryResult.RowCount, len(sqlResult.Values))
   168  			}
   169  
   170  			for row := 0; row < queryResult.RowCount; row++ {
   171  				value := sqlResult.Values[row]
   172  
   173  				if pagenatedUsers[row] == nil {
   174  					// create a new
   175  					pagenatedUsers[row] = &types.IRODSUser{
   176  						ID:   -1,
   177  						Zone: "",
   178  						Name: "",
   179  						Type: types.IRODSUserRodsUser,
   180  					}
   181  				}
   182  
   183  				switch sqlResult.AttributeIndex {
   184  				case int(common.ICAT_COLUMN_USER_ID):
   185  					userID, err := strconv.ParseInt(value, 10, 64)
   186  					if err != nil {
   187  						return nil, xerrors.Errorf("failed to parse user id '%s': %w", value, err)
   188  					}
   189  					pagenatedUsers[row].ID = userID
   190  				case int(common.ICAT_COLUMN_USER_ZONE):
   191  					pagenatedUsers[row].Zone = value
   192  				case int(common.ICAT_COLUMN_USER_NAME):
   193  					pagenatedUsers[row].Name = value
   194  				case int(common.ICAT_COLUMN_USER_TYPE):
   195  					pagenatedUsers[row].Type = types.IRODSUserType(value)
   196  				default:
   197  					// ignore
   198  				}
   199  			}
   200  		}
   201  
   202  		users = append(users, pagenatedUsers...)
   203  
   204  		continueIndex = queryResult.ContinueIndex
   205  		if continueIndex == 0 {
   206  			continueQuery = false
   207  		}
   208  	}
   209  
   210  	return users, nil
   211  }
   212  
   213  // ListGroups returns all groups
   214  func ListGroups(conn *connection.IRODSConnection) ([]*types.IRODSUser, error) {
   215  	if conn == nil || !conn.IsConnected() {
   216  		return nil, xerrors.Errorf("connection is nil or disconnected")
   217  	}
   218  
   219  	// lock the connection
   220  	conn.Lock()
   221  	defer conn.Unlock()
   222  
   223  	groups := []*types.IRODSUser{}
   224  
   225  	continueQuery := true
   226  	continueIndex := 0
   227  	for continueQuery {
   228  		query := message.NewIRODSMessageQueryRequest(common.MaxQueryRows, continueIndex, 0, 0)
   229  		query.AddSelect(common.ICAT_COLUMN_USER_ID, 1)
   230  		query.AddSelect(common.ICAT_COLUMN_USER_NAME, 1)
   231  		query.AddSelect(common.ICAT_COLUMN_USER_TYPE, 1)
   232  		query.AddSelect(common.ICAT_COLUMN_USER_ZONE, 1)
   233  
   234  		condTypeVal := fmt.Sprintf("= '%s'", types.IRODSUserRodsGroup)
   235  		query.AddCondition(common.ICAT_COLUMN_USER_TYPE, condTypeVal)
   236  
   237  		queryResult := message.IRODSMessageQueryResponse{}
   238  		err := conn.Request(query, &queryResult, nil)
   239  		if err != nil {
   240  			return nil, xerrors.Errorf("failed to receive a group query result message: %w", err)
   241  		}
   242  
   243  		err = queryResult.CheckError()
   244  		if err != nil {
   245  			if types.GetIRODSErrorCode(err) == common.CAT_NO_ROWS_FOUND {
   246  				// empty
   247  				break
   248  			}
   249  			return nil, xerrors.Errorf("received a group query error: %w", err)
   250  		}
   251  
   252  		if queryResult.RowCount == 0 {
   253  			break
   254  		}
   255  
   256  		if queryResult.AttributeCount > len(queryResult.SQLResult) {
   257  			return nil, xerrors.Errorf("failed to receive group attributes - requires %d, but received %d attributes", queryResult.AttributeCount, len(queryResult.SQLResult))
   258  		}
   259  
   260  		pagenatedGroups := make([]*types.IRODSUser, queryResult.RowCount)
   261  
   262  		for attr := 0; attr < queryResult.AttributeCount; attr++ {
   263  			sqlResult := queryResult.SQLResult[attr]
   264  			if len(sqlResult.Values) != queryResult.RowCount {
   265  				return nil, xerrors.Errorf("failed to receive group rows - requires %d, but received %d attributes", queryResult.RowCount, len(sqlResult.Values))
   266  			}
   267  
   268  			for row := 0; row < queryResult.RowCount; row++ {
   269  				value := sqlResult.Values[row]
   270  
   271  				if pagenatedGroups[row] == nil {
   272  					// create a new
   273  					pagenatedGroups[row] = &types.IRODSUser{
   274  						ID:   -1,
   275  						Zone: "",
   276  						Name: "",
   277  						Type: types.IRODSUserRodsUser,
   278  					}
   279  				}
   280  
   281  				switch sqlResult.AttributeIndex {
   282  				case int(common.ICAT_COLUMN_USER_ID):
   283  					userID, err := strconv.ParseInt(value, 10, 64)
   284  					if err != nil {
   285  						return nil, xerrors.Errorf("failed to parse user id '%s': %w", value, err)
   286  					}
   287  					pagenatedGroups[row].ID = userID
   288  				case int(common.ICAT_COLUMN_USER_ZONE):
   289  					pagenatedGroups[row].Zone = value
   290  				case int(common.ICAT_COLUMN_USER_NAME):
   291  					pagenatedGroups[row].Name = value
   292  				case int(common.ICAT_COLUMN_USER_TYPE):
   293  					pagenatedGroups[row].Type = types.IRODSUserType(value)
   294  				default:
   295  					// ignore
   296  				}
   297  			}
   298  		}
   299  
   300  		groups = append(groups, pagenatedGroups...)
   301  
   302  		continueIndex = queryResult.ContinueIndex
   303  		if continueIndex == 0 {
   304  			continueQuery = false
   305  		}
   306  	}
   307  
   308  	return groups, nil
   309  }
   310  
   311  // ListUsers lists all users
   312  func ListUsers(conn *connection.IRODSConnection) ([]*types.IRODSUser, error) {
   313  	if conn == nil || !conn.IsConnected() {
   314  		return nil, xerrors.Errorf("connection is nil or disconnected")
   315  	}
   316  
   317  	// lock the connection
   318  	conn.Lock()
   319  	defer conn.Unlock()
   320  
   321  	users := []*types.IRODSUser{}
   322  
   323  	continueQuery := true
   324  	continueIndex := 0
   325  	for continueQuery {
   326  		query := message.NewIRODSMessageQueryRequest(common.MaxQueryRows, continueIndex, 0, 0)
   327  		query.AddSelect(common.ICAT_COLUMN_USER_ID, 1)
   328  		query.AddSelect(common.ICAT_COLUMN_USER_NAME, 1)
   329  		query.AddSelect(common.ICAT_COLUMN_USER_TYPE, 1)
   330  		query.AddSelect(common.ICAT_COLUMN_USER_ZONE, 1)
   331  
   332  		condTypeVal := fmt.Sprintf("<> '%s'", types.IRODSUserRodsGroup)
   333  		query.AddCondition(common.ICAT_COLUMN_USER_TYPE, condTypeVal)
   334  
   335  		queryResult := message.IRODSMessageQueryResponse{}
   336  		err := conn.Request(query, &queryResult, nil)
   337  		if err != nil {
   338  			return nil, xerrors.Errorf("failed to receive a user query result message: %w", err)
   339  		}
   340  
   341  		err = queryResult.CheckError()
   342  		if err != nil {
   343  			if types.GetIRODSErrorCode(err) == common.CAT_NO_ROWS_FOUND {
   344  				// empty
   345  				break
   346  			}
   347  			return nil, xerrors.Errorf("received a user query error: %w", err)
   348  		}
   349  
   350  		if queryResult.RowCount == 0 {
   351  			break
   352  		}
   353  
   354  		if queryResult.AttributeCount > len(queryResult.SQLResult) {
   355  			return nil, xerrors.Errorf("failed to receive user attributes - requires %d, but received %d attributes", queryResult.AttributeCount, len(queryResult.SQLResult))
   356  		}
   357  
   358  		pagenatedUsers := make([]*types.IRODSUser, queryResult.RowCount)
   359  
   360  		for attr := 0; attr < queryResult.AttributeCount; attr++ {
   361  			sqlResult := queryResult.SQLResult[attr]
   362  			if len(sqlResult.Values) != queryResult.RowCount {
   363  				return nil, xerrors.Errorf("failed to receive user rows - requires %d, but received %d attributes", queryResult.RowCount, len(sqlResult.Values))
   364  			}
   365  
   366  			for row := 0; row < queryResult.RowCount; row++ {
   367  				value := sqlResult.Values[row]
   368  
   369  				if pagenatedUsers[row] == nil {
   370  					// create a new
   371  					pagenatedUsers[row] = &types.IRODSUser{
   372  						ID:   -1,
   373  						Zone: "",
   374  						Name: "",
   375  						Type: types.IRODSUserRodsUser,
   376  					}
   377  				}
   378  
   379  				switch sqlResult.AttributeIndex {
   380  				case int(common.ICAT_COLUMN_USER_ID):
   381  					userID, err := strconv.ParseInt(value, 10, 64)
   382  					if err != nil {
   383  						return nil, xerrors.Errorf("failed to parse user id '%s': %w", value, err)
   384  					}
   385  					pagenatedUsers[row].ID = userID
   386  				case int(common.ICAT_COLUMN_USER_ZONE):
   387  					pagenatedUsers[row].Zone = value
   388  				case int(common.ICAT_COLUMN_USER_NAME):
   389  					pagenatedUsers[row].Name = value
   390  				case int(common.ICAT_COLUMN_USER_TYPE):
   391  					pagenatedUsers[row].Type = types.IRODSUserType(value)
   392  				default:
   393  					// ignore
   394  				}
   395  			}
   396  		}
   397  
   398  		users = append(users, pagenatedUsers...)
   399  
   400  		continueIndex = queryResult.ContinueIndex
   401  		if continueIndex == 0 {
   402  			continueQuery = false
   403  		}
   404  	}
   405  
   406  	return users, nil
   407  }
   408  
   409  // ListUserGroupNames lists the group names a user is a member of
   410  func ListUserGroupNames(conn *connection.IRODSConnection, user string) ([]string, error) {
   411  	if conn == nil || !conn.IsConnected() {
   412  		return nil, xerrors.Errorf("connection is nil or disconnected")
   413  	}
   414  
   415  	// lock the connection
   416  	conn.Lock()
   417  	defer conn.Unlock()
   418  
   419  	var groups []string
   420  
   421  	continueQuery := true
   422  	continueIndex := 0
   423  	for continueQuery {
   424  		query := message.NewIRODSMessageQueryRequest(common.MaxQueryRows, continueIndex, 0, 0)
   425  		query.AddSelect(common.ICAT_COLUMN_COLL_USER_GROUP_NAME, 1)
   426  
   427  		condTypeVal := fmt.Sprintf("= '%s'", user)
   428  		query.AddCondition(common.ICAT_COLUMN_USER_NAME, condTypeVal)
   429  
   430  		queryResult := message.IRODSMessageQueryResponse{}
   431  		err := conn.Request(query, &queryResult, nil)
   432  		if err != nil {
   433  			return nil, xerrors.Errorf("failed to receive a group query result message: %w", err)
   434  		}
   435  
   436  		err = queryResult.CheckError()
   437  		if err != nil {
   438  			if types.GetIRODSErrorCode(err) == common.CAT_NO_ROWS_FOUND {
   439  				// empty
   440  				break
   441  			}
   442  			return nil, xerrors.Errorf("received a group query error: %w", err)
   443  		}
   444  
   445  		if queryResult.RowCount == 0 {
   446  			break
   447  		}
   448  
   449  		if queryResult.AttributeCount > len(queryResult.SQLResult) {
   450  			return nil, xerrors.Errorf("failed to receive group attributes - requires %d, but received %d attributes", queryResult.AttributeCount, len(queryResult.SQLResult))
   451  		}
   452  
   453  		var groupNames []string
   454  
   455  		for attr := 0; attr < queryResult.AttributeCount; attr++ {
   456  			sqlResult := queryResult.SQLResult[attr]
   457  			if len(sqlResult.Values) != queryResult.RowCount {
   458  				return nil, xerrors.Errorf("failed to receive group rows - requires %d, but received %d attributes", queryResult.RowCount, len(sqlResult.Values))
   459  			}
   460  
   461  			for row := 0; row < queryResult.RowCount; row++ {
   462  				value := sqlResult.Values[row]
   463  
   464  				if value != user {
   465  					groupNames = append(groupNames, value)
   466  				}
   467  
   468  			}
   469  		}
   470  
   471  		groups = append(groups, groupNames...)
   472  
   473  		continueIndex = queryResult.ContinueIndex
   474  		if continueIndex == 0 {
   475  			continueQuery = false
   476  		}
   477  	}
   478  
   479  	return groups, nil
   480  }
   481  
   482  // CreateUser creates a user.
   483  func CreateUser(conn *connection.IRODSConnection, username string, zone string, userType string) error {
   484  	// lock the connection
   485  	conn.Lock()
   486  	defer conn.Unlock()
   487  
   488  	userZoneName := fmt.Sprintf("%s#%s", username, zone)
   489  
   490  	req := message.NewIRODSMessageAdminRequest("add", "user", userZoneName, userType, zone)
   491  
   492  	err := conn.RequestAndCheck(req, &message.IRODSMessageAdminResponse{}, nil)
   493  	if err != nil {
   494  		return xerrors.Errorf("received create user error: %w", err)
   495  	}
   496  	return nil
   497  }
   498  
   499  // ChangeUserPassword changes the password of a user object
   500  func ChangeUserPassword(conn *connection.IRODSConnection, username string, zone string, newPassword string) error {
   501  	// lock the connection
   502  	conn.Lock()
   503  	defer conn.Unlock()
   504  
   505  	userZoneName := fmt.Sprintf("%s#%s", username, zone)
   506  
   507  	account := conn.GetAccount()
   508  
   509  	oldPassword := account.Password
   510  	if account.AuthenticationScheme == types.AuthSchemePAM {
   511  		oldPassword = conn.GetGeneratedPasswordForPAMAuth()
   512  	}
   513  
   514  	scrambledPassword := util.ObfuscateNewPassword(newPassword, oldPassword, conn.GetClientSignature())
   515  
   516  	req := message.NewIRODSMessageAdminRequest("modify", "user", userZoneName, "password", scrambledPassword, zone)
   517  
   518  	err := conn.RequestAndCheckForPassword(req, &message.IRODSMessageAdminResponse{}, nil)
   519  	if err != nil {
   520  		return xerrors.Errorf("received change user password error: %w", err)
   521  	}
   522  	return nil
   523  }
   524  
   525  // ChangeUserType changes the type / role of a user object
   526  func ChangeUserType(conn *connection.IRODSConnection, username string, zone string, newType string) error {
   527  	// lock the connection
   528  	conn.Lock()
   529  	defer conn.Unlock()
   530  
   531  	userZoneName := fmt.Sprintf("%s#%s", username, zone)
   532  
   533  	req := message.NewIRODSMessageAdminRequest("modify", "user", userZoneName, "type", newType, zone)
   534  
   535  	err := conn.RequestAndCheck(req, &message.IRODSMessageAdminResponse{}, nil)
   536  	if err != nil {
   537  		return xerrors.Errorf("received change user type error: %w", err)
   538  	}
   539  	return nil
   540  }
   541  
   542  // RemoveUser removes a user or a group.
   543  func RemoveUser(conn *connection.IRODSConnection, username string, zone string) error {
   544  	// lock the connection
   545  	conn.Lock()
   546  	defer conn.Unlock()
   547  
   548  	req := message.NewIRODSMessageAdminRequest("rm", "user", username, zone)
   549  
   550  	err := conn.RequestAndCheck(req, &message.IRODSMessageAdminResponse{}, nil)
   551  	if err != nil {
   552  		return xerrors.Errorf("received remove user error: %w", err)
   553  	}
   554  	return nil
   555  }
   556  
   557  // CreateGroup creates a group.
   558  func CreateGroup(conn *connection.IRODSConnection, groupname string, groupType string) error {
   559  	// lock the connection
   560  	conn.Lock()
   561  	defer conn.Unlock()
   562  
   563  	req := message.NewIRODSMessageAdminRequest("add", "user", groupname, groupType)
   564  
   565  	err := conn.RequestAndCheck(req, &message.IRODSMessageUserAdminResponse{}, nil)
   566  	if err != nil {
   567  		return xerrors.Errorf("received create group error: %w", err)
   568  	}
   569  	return nil
   570  }
   571  
   572  // AddGroupMember adds a user to a group.
   573  func AddGroupMember(conn *connection.IRODSConnection, groupname string, username string, zone string) error {
   574  	// lock the connection
   575  	conn.Lock()
   576  	defer conn.Unlock()
   577  
   578  	req := message.NewIRODSMessageAdminRequest("modify", "group", groupname, "add", username, zone)
   579  
   580  	err := conn.RequestAndCheck(req, &message.IRODSMessageUserAdminResponse{}, nil)
   581  	if err != nil {
   582  		return xerrors.Errorf("received add group member error: %w", err)
   583  	}
   584  	return nil
   585  }
   586  
   587  // RemoveGroupMember removes a user from a group.
   588  func RemoveGroupMember(conn *connection.IRODSConnection, groupname string, username string, zone string) error {
   589  	// lock the connection
   590  	conn.Lock()
   591  	defer conn.Unlock()
   592  
   593  	req := message.NewIRODSMessageAdminRequest("modify", "group", groupname, "remove", username, zone)
   594  
   595  	err := conn.RequestAndCheck(req, &message.IRODSMessageUserAdminResponse{}, nil)
   596  	if err != nil {
   597  		return xerrors.Errorf("received remove group member error: %w", err)
   598  	}
   599  	return nil
   600  }
   601  
   602  // ListUserResourceQuota lists all existing resource quota of a user or group
   603  func ListUserResourceQuota(conn *connection.IRODSConnection, user string) ([]*types.IRODSQuota, error) {
   604  	if conn == nil || !conn.IsConnected() {
   605  		return nil, xerrors.Errorf("connection is nil or disconnected")
   606  	}
   607  
   608  	// lock the connection
   609  	conn.Lock()
   610  	defer conn.Unlock()
   611  
   612  	quota := []*types.IRODSQuota{}
   613  
   614  	continueQuery := true
   615  	continueIndex := 0
   616  	for continueQuery {
   617  		query := message.NewIRODSMessageQueryRequest(common.MaxQueryRows, continueIndex, 0, 0)
   618  		query.AddSelect(common.ICAT_COLUMN_QUOTA_RESC_NAME, 1)
   619  		query.AddSelect(common.ICAT_COLUMN_QUOTA_LIMIT, 1)
   620  
   621  		condTypeVal := fmt.Sprintf("= '%s'", user)
   622  		query.AddCondition(common.ICAT_COLUMN_QUOTA_USER_NAME, condTypeVal)
   623  
   624  		queryResult := message.IRODSMessageQueryResponse{}
   625  		err := conn.Request(query, &queryResult, nil)
   626  		if err != nil {
   627  			return nil, xerrors.Errorf("failed to receive a quota query result message: %w", err)
   628  		}
   629  
   630  		err = queryResult.CheckError()
   631  		if err != nil {
   632  			if types.GetIRODSErrorCode(err) == common.CAT_NO_ROWS_FOUND {
   633  				// empty
   634  				break
   635  			}
   636  			return nil, xerrors.Errorf("received a quota query error: %w", err)
   637  		}
   638  
   639  		if queryResult.RowCount == 0 {
   640  			break
   641  		}
   642  
   643  		if queryResult.AttributeCount > len(queryResult.SQLResult) {
   644  			return nil, xerrors.Errorf("failed to receive quota attributes - requires %d, but received %d attributes", queryResult.AttributeCount, len(queryResult.SQLResult))
   645  		}
   646  
   647  		pagenatedQuota := make([]*types.IRODSQuota, queryResult.RowCount)
   648  
   649  		for attr := 0; attr < queryResult.AttributeCount; attr++ {
   650  			sqlResult := queryResult.SQLResult[attr]
   651  			if len(sqlResult.Values) != queryResult.RowCount {
   652  				return nil, xerrors.Errorf("failed to receive quota rows - requires %d, but received %d attributes", queryResult.RowCount, len(sqlResult.Values))
   653  			}
   654  
   655  			for row := 0; row < queryResult.RowCount; row++ {
   656  				value := sqlResult.Values[row]
   657  
   658  				if pagenatedQuota[row] == nil {
   659  					// create a new
   660  					pagenatedQuota[row] = &types.IRODSQuota{
   661  						RescName: "",
   662  						Limit:    -1,
   663  					}
   664  				}
   665  
   666  				switch sqlResult.AttributeIndex {
   667  				case int(common.ICAT_COLUMN_QUOTA_RESC_NAME):
   668  					pagenatedQuota[row].RescName = value
   669  				case int(common.ICAT_COLUMN_QUOTA_LIMIT):
   670  					limit, err := strconv.ParseInt(value, 10, 64)
   671  					if err != nil {
   672  						return nil, xerrors.Errorf("failed to parse quota limit '%s': %w", value, err)
   673  					}
   674  					pagenatedQuota[row].Limit = limit
   675  				default:
   676  					// ignore
   677  				}
   678  			}
   679  		}
   680  
   681  		quota = append(quota, pagenatedQuota...)
   682  
   683  		continueIndex = queryResult.ContinueIndex
   684  		if continueIndex == 0 {
   685  			continueQuery = false
   686  		}
   687  	}
   688  
   689  	return quota, nil
   690  }
   691  
   692  // GetUserGlobalQuota returns the global quota of a user or group
   693  func GetUserGlobalQuota(conn *connection.IRODSConnection, user string) (*types.IRODSQuota, error) {
   694  	if conn == nil || !conn.IsConnected() {
   695  		return nil, xerrors.Errorf("connection is nil or disconnected")
   696  	}
   697  
   698  	// lock the connection
   699  	conn.Lock()
   700  	defer conn.Unlock()
   701  
   702  	quota := []*types.IRODSQuota{}
   703  
   704  	continueQuery := true
   705  	continueIndex := 0
   706  	for continueQuery {
   707  		query := message.NewIRODSMessageQueryRequest(common.MaxQueryRows, continueIndex, 0, 0)
   708  		query.AddSelect(common.ICAT_COLUMN_QUOTA_LIMIT, 1)
   709  
   710  		condTypeVal := fmt.Sprintf("= '%s'", user)
   711  		query.AddCondition(common.ICAT_COLUMN_QUOTA_USER_NAME, condTypeVal)
   712  		condTypeVal = fmt.Sprintf("= '%s'", "0")
   713  		query.AddCondition(common.ICAT_COLUMN_QUOTA_RESC_ID, condTypeVal)
   714  
   715  		queryResult := message.IRODSMessageQueryResponse{}
   716  		err := conn.Request(query, &queryResult, nil)
   717  		if err != nil {
   718  			return nil, xerrors.Errorf("failed to receive a quota query result message: %w", err)
   719  		}
   720  
   721  		err = queryResult.CheckError()
   722  		if err != nil {
   723  			return nil, xerrors.Errorf("received a quota query error: %w", err)
   724  		}
   725  
   726  		if queryResult.RowCount == 0 {
   727  			break
   728  		}
   729  
   730  		if queryResult.AttributeCount > len(queryResult.SQLResult) {
   731  			return nil, xerrors.Errorf("failed to receive quota attributes - requires %d, but received %d attributes", queryResult.AttributeCount, len(queryResult.SQLResult))
   732  		}
   733  
   734  		pagenatedQuota := make([]*types.IRODSQuota, queryResult.RowCount)
   735  
   736  		for attr := 0; attr < queryResult.AttributeCount; attr++ {
   737  			sqlResult := queryResult.SQLResult[attr]
   738  			if len(sqlResult.Values) != queryResult.RowCount {
   739  				return nil, xerrors.Errorf("failed to receive quota rows - requires %d, but received %d attributes", queryResult.RowCount, len(sqlResult.Values))
   740  			}
   741  
   742  			for row := 0; row < queryResult.RowCount; row++ {
   743  				value := sqlResult.Values[row]
   744  
   745  				if pagenatedQuota[row] == nil {
   746  					// create a new
   747  					pagenatedQuota[row] = &types.IRODSQuota{
   748  						RescName: "global",
   749  						Limit:    -1,
   750  					}
   751  				}
   752  
   753  				switch sqlResult.AttributeIndex {
   754  				case int(common.ICAT_COLUMN_QUOTA_LIMIT):
   755  					limit, err := strconv.ParseInt(value, 10, 64)
   756  					if err != nil {
   757  						return nil, xerrors.Errorf("failed to parse quota limit '%s': %w", value, err)
   758  					}
   759  					pagenatedQuota[row].Limit = limit
   760  				default:
   761  					// ignore
   762  				}
   763  			}
   764  		}
   765  
   766  		quota = append(quota, pagenatedQuota...)
   767  
   768  		continueIndex = queryResult.ContinueIndex
   769  		if continueIndex == 0 {
   770  			continueQuery = false
   771  		}
   772  	}
   773  
   774  	return quota[0], nil
   775  }
   776  
   777  // AddUserMeta sets metadata of a user object to given key values.
   778  func AddUserMeta(conn *connection.IRODSConnection, user string, metadata *types.IRODSMeta) error {
   779  	if conn == nil || !conn.IsConnected() {
   780  		return xerrors.Errorf("connection is nil or disconnected")
   781  	}
   782  
   783  	// lock the connection
   784  	conn.Lock()
   785  	defer conn.Unlock()
   786  
   787  	request := message.NewIRODSMessageAddMetadataRequest(types.IRODSUserMetaItemType, user, metadata)
   788  	response := message.IRODSMessageModifyMetadataResponse{}
   789  	return conn.RequestAndCheck(request, &response, nil)
   790  }
   791  
   792  // DeleteUserMeta removes the metadata of a user object.
   793  // The metadata AVU is selected on basis of AVUID if it is supplied, otherwise on basis of Name, Value and Units.
   794  func DeleteUserMeta(conn *connection.IRODSConnection, user string, metadata *types.IRODSMeta) error {
   795  	if conn == nil || !conn.IsConnected() {
   796  		return xerrors.Errorf("connection is nil or disconnected")
   797  	}
   798  
   799  	// lock the connection
   800  	conn.Lock()
   801  	defer conn.Unlock()
   802  
   803  	var request *message.IRODSMessageModifyMetadataRequest
   804  
   805  	if metadata.AVUID != 0 {
   806  		request = message.NewIRODSMessageRemoveMetadataByIDRequest(types.IRODSUserMetaItemType, user, metadata.AVUID)
   807  	} else if metadata.Units == "" && metadata.Value == "" {
   808  		request = message.NewIRODSMessageRemoveMetadataWildcardRequest(types.IRODSUserMetaItemType, user, metadata.Name)
   809  	} else {
   810  		request = message.NewIRODSMessageRemoveMetadataRequest(types.IRODSUserMetaItemType, user, metadata)
   811  	}
   812  
   813  	response := message.IRODSMessageModifyMetadataResponse{}
   814  	return conn.RequestAndCheck(request, &response, nil)
   815  }
   816  
   817  // ListUserMeta returns a user metadata for the path
   818  func ListUserMeta(conn *connection.IRODSConnection, user string) ([]*types.IRODSMeta, error) {
   819  	if conn == nil || !conn.IsConnected() {
   820  		return nil, xerrors.Errorf("connection is nil or disconnected")
   821  	}
   822  
   823  	// lock the connection
   824  	conn.Lock()
   825  	defer conn.Unlock()
   826  
   827  	metas := []*types.IRODSMeta{}
   828  
   829  	continueQuery := true
   830  	continueIndex := 0
   831  	for continueQuery {
   832  		query := message.NewIRODSMessageQueryRequest(common.MaxQueryRows, continueIndex, 0, 0)
   833  		query.AddSelect(common.ICAT_COLUMN_META_USER_ATTR_ID, 1)
   834  		query.AddSelect(common.ICAT_COLUMN_META_USER_ATTR_NAME, 1)
   835  		query.AddSelect(common.ICAT_COLUMN_META_USER_ATTR_VALUE, 1)
   836  		query.AddSelect(common.ICAT_COLUMN_META_USER_ATTR_UNITS, 1)
   837  
   838  		nameCondVal := fmt.Sprintf("= '%s'", user)
   839  		query.AddCondition(common.ICAT_COLUMN_USER_NAME, nameCondVal)
   840  
   841  		queryResult := message.IRODSMessageQueryResponse{}
   842  		err := conn.Request(query, &queryResult, nil)
   843  		if err != nil {
   844  			return nil, xerrors.Errorf("failed to receive a user metadata query result message: %w", err)
   845  		}
   846  
   847  		err = queryResult.CheckError()
   848  		if err != nil {
   849  			if types.GetIRODSErrorCode(err) == common.CAT_NO_ROWS_FOUND {
   850  				// empty
   851  				break
   852  			}
   853  			return nil, xerrors.Errorf("received a user metadata query error: %w", err)
   854  		}
   855  
   856  		if queryResult.RowCount == 0 {
   857  			break
   858  		}
   859  
   860  		if queryResult.AttributeCount > len(queryResult.SQLResult) {
   861  			return nil, xerrors.Errorf("failed to receive user metadata attributes - requires %d, but received %d attributes", queryResult.AttributeCount, len(queryResult.SQLResult))
   862  		}
   863  
   864  		pagenatedMetas := make([]*types.IRODSMeta, queryResult.RowCount)
   865  
   866  		for attr := 0; attr < queryResult.AttributeCount; attr++ {
   867  			sqlResult := queryResult.SQLResult[attr]
   868  			if len(sqlResult.Values) != queryResult.RowCount {
   869  				return nil, xerrors.Errorf("failed to receive user metadata rows - requires %d, but received %d attributes", queryResult.RowCount, len(sqlResult.Values))
   870  			}
   871  
   872  			for row := 0; row < queryResult.RowCount; row++ {
   873  				value := sqlResult.Values[row]
   874  
   875  				if pagenatedMetas[row] == nil {
   876  					// create a new
   877  					pagenatedMetas[row] = &types.IRODSMeta{
   878  						AVUID: -1,
   879  						Name:  "",
   880  						Value: "",
   881  						Units: "",
   882  					}
   883  				}
   884  
   885  				switch sqlResult.AttributeIndex {
   886  				case int(common.ICAT_COLUMN_META_USER_ATTR_ID):
   887  					avuID, err := strconv.ParseInt(value, 10, 64)
   888  					if err != nil {
   889  						return nil, xerrors.Errorf("failed to parse user metadata id '%s': %w", value, err)
   890  					}
   891  					pagenatedMetas[row].AVUID = avuID
   892  				case int(common.ICAT_COLUMN_META_USER_ATTR_NAME):
   893  					pagenatedMetas[row].Name = value
   894  				case int(common.ICAT_COLUMN_META_USER_ATTR_VALUE):
   895  					pagenatedMetas[row].Value = value
   896  				case int(common.ICAT_COLUMN_META_USER_ATTR_UNITS):
   897  					pagenatedMetas[row].Units = value
   898  				default:
   899  					// ignore
   900  				}
   901  			}
   902  		}
   903  
   904  		metas = append(metas, pagenatedMetas...)
   905  
   906  		continueIndex = queryResult.ContinueIndex
   907  		if continueIndex == 0 {
   908  			continueQuery = false
   909  		}
   910  	}
   911  
   912  	return metas, nil
   913  }
   914  
   915  // AddChildToResc adds a child to a parent resource
   916  func AddChildToResc(conn *connection.IRODSConnection, parent string, child string, options string) error {
   917  	// lock the connection
   918  	conn.Lock()
   919  	defer conn.Unlock()
   920  
   921  	req := message.NewIRODSMessageAdminRequest("add", "childtoresc", parent, child, options)
   922  
   923  	err := conn.RequestAndCheck(req, &message.IRODSMessageAdminResponse{}, nil)
   924  	if err != nil {
   925  		return xerrors.Errorf("received add child to resc error: %w", err)
   926  	}
   927  	return nil
   928  }
   929  
   930  // SetUserQuota sets quota for a given user and resource ('total' for global)
   931  func SetUserQuota(conn *connection.IRODSConnection, user string, resource string, value string) error {
   932  	// lock the connection
   933  	conn.Lock()
   934  	defer conn.Unlock()
   935  
   936  	req := message.NewIRODSMessageAdminRequest("set-quota", "user", user, resource, value)
   937  
   938  	err := conn.RequestAndCheck(req, &message.IRODSMessageAdminResponse{}, nil)
   939  	if err != nil {
   940  		return xerrors.Errorf("received set user quota error: %w", err)
   941  	}
   942  	return nil
   943  }
   944  
   945  // SetGroupQuota sets quota for a given user and resource ('total' for global)
   946  func SetGroupQuota(conn *connection.IRODSConnection, group string, resource string, value string) error {
   947  	// lock the connection
   948  	conn.Lock()
   949  	defer conn.Unlock()
   950  
   951  	req := message.NewIRODSMessageAdminRequest("set-quota", "group", group, resource, value)
   952  
   953  	err := conn.RequestAndCheck(req, &message.IRODSMessageAdminResponse{}, nil)
   954  	if err != nil {
   955  		return xerrors.Errorf("received set group quota error: %w", err)
   956  	}
   957  	return nil
   958  }