Skip to content
This repository has been archived by the owner on Mar 24, 2021. It is now read-only.

Commit

Permalink
Add command to display list of inactive users
Browse files Browse the repository at this point in the history
  • Loading branch information
ishank011 committed Nov 25, 2020
1 parent 848f58a commit 41c14d3
Show file tree
Hide file tree
Showing 3 changed files with 235 additions and 18 deletions.
17 changes: 9 additions & 8 deletions cmd/accounting.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,6 @@ import (
"context"
"encoding/json"
"fmt"
"github.com/cs3org/reva/pkg/eosclient"
"github.com/dustin/go-humanize"
"github.com/leekchan/accounting"
"github.com/mitchellh/mapstructure"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/tj/go-spin"
"gopkg.in/ldap.v3"
"io/ioutil"
"net/http"
"os"
Expand All @@ -23,6 +15,15 @@ import (
"sync"
"sync/atomic"
"time"

"github.com/cs3org/reva/pkg/eosclient"
"github.com/dustin/go-humanize"
"github.com/leekchan/accounting"
"github.com/mitchellh/mapstructure"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/tj/go-spin"
"gopkg.in/ldap.v3"
)

const FE = "CERNBox"
Expand Down
37 changes: 37 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@ import (
"bufio"
"context"
"database/sql"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"os"
"path"
"strings"
Expand Down Expand Up @@ -120,6 +124,39 @@ func getLDAP() *ldap.Conn {
return l
}

func getGrappaURLAndToken() (string, string, error) {
params := url.Values{
"grant_type": {"client_credentials"},
"audience": {viper.GetString("grappa_target_api")},
}

client := &http.Client{}
req, err := http.NewRequest("POST", viper.GetString("oidc_token_endpoint"), strings.NewReader(params.Encode()))
if err != nil {
return "", "", err
}
req.SetBasicAuth(viper.GetString("grappa_client_id"), viper.GetString("grappa_client_secret"))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value")

res, err := client.Do(req)
if err != nil {
return "", "", err
}
defer res.Body.Close()

body, err := ioutil.ReadAll(res.Body)
if err != nil {
return "", "", err
}

var result map[string]interface{}
err = json.Unmarshal(body, &result)
if err != nil {
return "", "", err
}
return viper.GetString("grappa_api_url"), result["access_token"].(string), nil
}

func getEOS(mgm string) *eosclient.Client {
eosClientOpts := &eosclient.Options{
URL: mgm,
Expand Down
199 changes: 189 additions & 10 deletions cmd/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,29 @@ package cmd

import (
"encoding/binary"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"os"
"strconv"
"strings"
"time"

"github.com/go-redis/redis"
"github.com/spf13/cobra"
"github.com/tj/go-spin"
"gopkg.in/ldap.v3"
)

const timeFormat = "2006-01-02T15:04:05.999999"

func init() {
rootCmd.AddCommand(userCmd)
userCmd.AddCommand(userCheckCmd)
userCmd.AddCommand(userGroupsCmd)
userCmd.AddCommand(userListInactiveCmd)
}

var userCmd = &cobra.Command{
Expand Down Expand Up @@ -44,16 +53,6 @@ var userCheckCmd = &cobra.Command{
},
}

var prettyUser = func(userInfos ...*userInfo) {
cols := []string{"Account", "Type", "Name", "Department", "Group", "Section", "Mail", "Phone"}
rows := make([][]string, 0, len(userInfos))
for _, ui := range userInfos {
row := []string{ui.Account, ui.AccountType, ui.Name, ui.Department, ui.Group, ui.Section, ui.Mail, ui.Phone}
rows = append(rows, row)
}
pretty(cols, rows)
}

var userGroupsCmd = &cobra.Command{
Use: "groups <username>",
Short: "Retrieves the groups the the user is member of",
Expand All @@ -71,6 +70,54 @@ var userGroupsCmd = &cobra.Command{
},
}

var userListInactiveCmd = &cobra.Command{
Use: "list-inactive <days>",
Short: "Lists the user accounts which have been inactive for more than a given number of days",
Run: func(cmd *cobra.Command, args []string) {
if len(args) != 1 {
exit(cmd)
}

days, err := strconv.Atoi(strings.TrimSpace(args[0]))
if err != nil {
log.Err(err).Msgf("value for days is not an integer: %s", args[0])
}

apiEP, token, err := getGrappaURLAndToken()
if err != nil {
log.Err(err).Msg("error getting token")
}

info, err := getInactiveUsers(apiEP, token, days)
if err != nil {
log.Err(err).Msg("error getting token")
}

prettyInactiveUsers(info...)

},
}

func prettyUser(userInfos ...*userInfo) {
cols := []string{"Account", "Type", "Name", "Department", "Group", "Section", "Mail", "Phone"}
rows := make([][]string, 0, len(userInfos))
for _, ui := range userInfos {
row := []string{ui.Account, ui.AccountType, ui.Name, ui.Department, ui.Group, ui.Section, ui.Mail, ui.Phone}
rows = append(rows, row)
}
pretty(cols, rows)
}

func prettyInactiveUsers(userInfos ...*userInfo) {
cols := []string{"Account", "Name", "Mail", "Blocking time"}
rows := make([][]string, 0, len(userInfos))
for _, ui := range userInfos {
row := []string{ui.Account, ui.Name, ui.Mail, ui.BlockingTime.String()}
rows = append(rows, row)
}
pretty(cols, rows)
}

func getHomePath(username string) string {
letter := string(username[0])
return fmt.Sprintf("/eos/user/%s/%s", letter, username)
Expand Down Expand Up @@ -263,6 +310,137 @@ func getUserGroups(uid string) []string {
return gids
}

func getInactiveUsers(apiEP, token string, days int) ([]*userInfo, error) {
url := "/api/v1.0/Identity?filter=activeUser:false&field=upn&field=primaryAccountEmail&field=displayName&field=type&field=uid&field=gid&field=primaryAccountId"
users := []*userInfo{}
i := 0
for {
u, nextPage, err := getInactiveUsersByPage(apiEP, url, token, days, i+1)
if err != nil {
return nil, err
}
i += 1
users = append(users, u...)
url = nextPage
if nextPage == "" {
break
}
}
return users, nil
}

func getInactiveUsersByPage(apiEP, url, token string, days, pageIndex int) ([]*userInfo, string, error) {
deadline := time.Now().AddDate(0, 0, -1*days)

url = apiEP + url
client := &http.Client{}
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, "", err
}
req.Header.Set("Authorization", "Bearer "+token)

res, err := client.Do(req)
if err != nil {
return nil, "", err
}
defer res.Body.Close()

body, err := ioutil.ReadAll(res.Body)
if err != nil {
return nil, "", err
}

var result map[string]interface{}
err = json.Unmarshal(body, &result)
if err != nil {
return nil, "", err
}
userData, ok := result["data"].([]interface{})
if !ok {
return nil, "", errors.New("rest: error in type assertion")
}

s := spin.New()

users := []*userInfo{}
for i, u := range userData {
fmt.Fprintf(os.Stderr, "\r %s Processing inactive users from page %d: %d/%d\n", s.Next(), pageIndex, i+1, len(userData))
info, ok := u.(map[string]interface{})
if !ok {
return nil, "", errors.New("rest: error in type assertion")
}

// Check blocking time of the primary account of the user
accountsURL := fmt.Sprintf("%s/api/v1.0/Account?filter=id:%s", apiEP, info["primaryAccountId"].(string))
req, err = http.NewRequest("GET", accountsURL, nil)
if err != nil {
return nil, "", err
}
req.Header.Set("Authorization", "Bearer "+token)

res, err = client.Do(req)
if err != nil {
return nil, "", err
}
defer res.Body.Close()

body, err = ioutil.ReadAll(res.Body)
if err != nil {
return nil, "", err
}

var result map[string]interface{}
err = json.Unmarshal(body, &result)
if err != nil {
return nil, "", err
}
accountData, ok := result["data"].([]interface{})
if !ok {
return nil, "", errors.New("rest: error in type assertion")
}
accountInfo, ok := accountData[0].(map[string]interface{})
if !ok {
return nil, "", errors.New("rest: error in type assertion")
}

blockingTime, ok := accountInfo["blockingTime"].(string)
if !ok {
continue
}

t, err := time.Parse(timeFormat, blockingTime)
if err != nil {
return nil, "", err
}
if t.After(deadline) {
users = append(users, &userInfo{
Account: info["upn"].(string),
Name: info["displayName"].(string),
Mail: info["primaryAccountEmail"].(string),
UID: fmt.Sprintf("%0.f", info["uid"]),
GID: fmt.Sprintf("%0.f", info["gid"]),
BlockingTime: t,
})
}
}

var nextPage string
if pagination, ok := result["pagination"].(map[string]interface{}); ok {
if links, ok := pagination["links"].(map[string]interface{}); ok {
nextPage, _ = links["next"].(string)
}
}

return users, nextPage, nil

}

func filterUsersByDate(users []*userInfo, days int) ([]*userInfo, error) {
// TODO: Add filters once bug in grappa is fixed
return users, nil
}

func newUserInfo() *userInfo {
return &userInfo{
AccountOwner: &userInfo{},
Expand All @@ -281,6 +459,7 @@ type userInfo struct {
AccountOwner *userInfo
AccountOwnerDN string
Phone string
BlockingTime time.Time
}

func (ui *userInfo) accountTypeHuman() string {
Expand Down

0 comments on commit 41c14d3

Please sign in to comment.