feat: Optimize the functionality of iam

Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com>
pull/926/head
Xinwei Xiong(cubxxw-openim) 2 years ago
parent 0ddae51ce7
commit c5e81f8e31
No known key found for this signature in database
GPG Key ID: 1BAD6F395338EFDE

@ -1,3 +1,17 @@
# Copyright © 2023 OpenIM. All rights reserved.
#
# 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.
apiVersion: v2
name: openim-chat
description: A Helm chart for Kubernetes

@ -1,3 +1,17 @@
# Copyright © 2023 OpenIM. All rights reserved.
#
# 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.
apiVersion: apps/v1
kind: Deployment
metadata:

@ -1,3 +1,17 @@
# Copyright © 2023 OpenIM. All rights reserved.
#
# 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.
{{- if .Values.autoscaling.enabled }}
apiVersion: autoscaling/v2beta1
kind: HorizontalPodAutoscaler

@ -1,3 +1,17 @@
# Copyright © 2023 OpenIM. All rights reserved.
#
# 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.
{{- if .Values.ingress.enabled -}}
{{- $fullName := include "openim-chat.fullname" . -}}
{{- $svcPort := .Values.service.port -}}

@ -1,3 +1,17 @@
# Copyright © 2023 OpenIM. All rights reserved.
#
# 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.
apiVersion: v1
kind: Service
metadata:

@ -1,3 +1,17 @@
# Copyright © 2023 OpenIM. All rights reserved.
#
# 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.
{{- if .Values.serviceAccount.create -}}
apiVersion: v1
kind: ServiceAccount

@ -1,3 +1,17 @@
# Copyright © 2023 OpenIM. All rights reserved.
#
# 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.
# Default values for openim-chat.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.

@ -1,3 +1,17 @@
# Copyright © 2023 OpenIM. All rights reserved.
#
# 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.
apiVersion: v2
name: openim-server
description: A Helm chart for Kubernetes

@ -1,3 +1,17 @@
# Copyright © 2023 OpenIM. All rights reserved.
#
# 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.
apiVersion: apps/v1
kind: Deployment
metadata:

@ -1,3 +1,17 @@
# Copyright © 2023 OpenIM. All rights reserved.
#
# 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.
{{- if .Values.autoscaling.enabled }}
apiVersion: autoscaling/v2beta1
kind: HorizontalPodAutoscaler

@ -1,3 +1,17 @@
# Copyright © 2023 OpenIM. All rights reserved.
#
# 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.
{{- if .Values.ingress.enabled -}}
{{- $fullName := include "openim-server.fullname" . -}}
{{- $svcPort := .Values.service.port -}}

@ -1,3 +1,17 @@
# Copyright © 2023 OpenIM. All rights reserved.
#
# 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.
apiVersion: v1
kind: Service
metadata:

@ -1,3 +1,17 @@
# Copyright © 2023 OpenIM. All rights reserved.
#
# 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.
{{- if .Values.serviceAccount.create -}}
apiVersion: v1
kind: ServiceAccount

@ -1,3 +1,17 @@
# Copyright © 2023 OpenIM. All rights reserved.
#
# 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.
# Default values for openim-server.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.

@ -1,3 +1,17 @@
# Copyright © 2023 OpenIM. All rights reserved.
#
# 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.
# Configuration for OpenIMServer
# -----------------------------------------------------------------

@ -1,3 +1,17 @@
# Copyright © 2023 OpenIM. All rights reserved.
#
# 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.
# ======================================
# ========= Basic Configuration ========
# ======================================

@ -1,4 +1,18 @@
#!/usr/bin/env bash
# Copyright © 2023 OpenIM. All rights reserved.
#
# 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.
# Wait for Kafka to be ready
until /opt/bitnami/kafka/bin/kafka-topics.sh --list --bootstrap-server localhost:9092; do
echo "Waiting for Kafka to be ready..."

@ -24,20 +24,20 @@ import (
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/iamctl/cmd/color"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/iamctl/cmd/completion"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/iamctl/cmd/info"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/iamctl/cmd/jwt"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/iamctl/cmd/new"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/iamctl/cmd/options"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/iamctl/cmd/policy"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/iamctl/cmd/secret"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/iamctl/cmd/set"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/iamctl/cmd/user"
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/iamctl/cmd/util"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/iamctl/cmd/validate"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/iamctl/cmd/version"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/iamctl/util/templates"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/color"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/completion"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/info"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/jwt"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/new"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/options"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/policy"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/secret"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/set"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/user"
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/inernal/iamctl/cmd/util"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/validate"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/version"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
genericapiserver "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/pkg/server"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/pkg/cli/genericclioptions"
)

@ -0,0 +1,352 @@
// Copyright © 2023 OpenIM. All rights reserved.
//
// 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 color print colors supported by the current terminal.
package color
import (
"fmt"
"strings"
"github.com/fatih/color"
"github.com/olekukonko/tablewriter"
"github.com/openim-sigs/component-base/pkg/util/stringutil"
"github.com/spf13/cobra"
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
)
// ColorOptions is an options struct to support color subcommands.
type ColorOptions struct {
Type []string
Example bool
genericclioptions.IOStreams
}
var (
colorLong = templates.LongDesc(`Print the colors supported by the current terminal.
Color lets you use colorized outputs in terms of ANSI Escape Codes in Go (Golang).
It has support for Windows too! The API can be used in several ways, pick one that suits you.
Find more information at:
https://github.com/fatih/color`)
colorExample = templates.Examples(`
# Print supported foreground and background colors
iamctl color
# Print supported colors by type
iamctl color -t fg-hi
# Print all supported colors
iamctl color -t all`)
availableTypes = []string{"fg", "fg-hi", "bg", "bg-hi", "base", "all"}
colorCodeExample = templates.Examples(`
### 1. Standard colors
// Print with default helper functions
color.Cyan("Prints text in cyan.")
// A newline will be appended automatically
color.Blue("Prints %s in blue.", "text")
// These are using the default foreground colors
color.Red("We have red")
color.Magenta("And many others ..")
### 2. Mix and reuse colors
// Create a new color object
c := color.New(color.FgCyan).Add(color.Underline)
c.Println("Prints cyan text with an underline.")
// Or just add them to New()
d := color.New(color.FgCyan, color.Bold)
d.Printf("This prints bold cyan %s\n", "too!.")
// Mix up foreground and background colors, create new mixes!
red := color.New(color.FgRed)
boldRed := red.Add(color.Bold)
boldRed.Println("This will print text in bold red.")
whiteBackground := red.Add(color.BgWhite)
whiteBackground.Println("Red text with white background.")
### 3. Use your own output (io.Writer)
// Use your own io.Writer output
color.New(color.FgBlue).Fprintln(myWriter, "blue color!")
blue := color.New(color.FgBlue)
blue.Fprint(writer, "This will print text in blue.")
### 4. Custom print functions (PrintFunc)
// Create a custom print function for convenience
red := color.New(color.FgRed).PrintfFunc()
red("Warning")
red("Error: %s", err)
// Mix up multiple attributes
notice := color.New(color.Bold, color.FgGreen).PrintlnFunc()
notice("Don't forget this...")
### 5. Custom fprint functions (FprintFunc)
blue := color.New(FgBlue).FprintfFunc()
blue(myWriter, "important notice: %s", stars)
// Mix up with multiple attributes
success := color.New(color.Bold, color.FgGreen).FprintlnFunc()
success(myWriter, "Don't forget this...")
### 6. Insert into noncolor strings (SprintFunc)
// Create SprintXxx functions to mix strings with other non-colorized strings:
yellow := color.New(color.FgYellow).SprintFunc()
red := color.New(color.FgRed).SprintFunc()
fmt.Printf("This is a %s and this is %s.\n", yellow("warning"), red("error"))
info := color.New(color.FgWhite, color.BgGreen).SprintFunc()
fmt.Printf("This %s rocks!\n", info("package"))
// Use helper functions
fmt.Println("This", color.RedString("warning"), "should be not neglected.")
fmt.Printf("%v %v\n", color.GreenString("Info:"), "an important message.")
// Windows supported too! Just don't forget to change the output to color.Output
fmt.Fprintf(color.Output, "Windows support: %s", color.GreenString("PASS"))
### 7. Plug into existing code
// Use handy standard colors
color.Set(color.FgYellow)
fmt.Println("Existing text will now be in yellow")
fmt.Printf("This one %s\n", "too")
color.Unset() // Don't forget to unset
// You can mix up parameters
color.Set(color.FgMagenta, color.Bold)
defer color.Unset() // Use it in your function
fmt.Println("All text will now be bold magenta.")
### 8. Disable/Enable color
There might be a case where you want to explicitly disable/enable color output. the
go-isatty package will automatically disable color output for non-tty output streams
(for example if the output were piped directly to less)
Color has support to disable/enable colors both globally and for single color
definitions. For example suppose you have a CLI app and a --no-color bool flag. You
can easily disable the color output with:
var flagNoColor = flag.Bool("no-color", false, "Disable color output")
if *flagNoColor {
color.NoColor = true // disables colorized output
}
It also has support for single color definitions (local). You can
disable/enable color output on the fly:
c := color.New(color.FgCyan)
c.Println("Prints cyan text")
c.DisableColor()
c.Println("This is printed without any color")
c.EnableColor()
c.Println("This prints again cyan...")`)
)
// NewColorOptions returns an initialized ColorOptions instance.
func NewColorOptions(ioStreams genericclioptions.IOStreams) *ColorOptions {
return &ColorOptions{
Type: []string{},
Example: false,
IOStreams: ioStreams,
}
}
// NewCmdColor returns new initialized instance of color sub command.
func NewCmdColor(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
o := NewColorOptions(ioStreams)
cmd := &cobra.Command{
Use: "color",
DisableFlagsInUseLine: true,
Aliases: []string{},
Short: "Print colors supported by the current terminal",
TraverseChildren: true,
Long: colorLong,
Example: colorExample,
// NoArgs, ArbitraryArgs, OnlyValidArgs, MinimumArgs, MaximumArgs, ExactArgs, ExactArgs
ValidArgs: []string{},
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate(cmd, args))
cmdutil.CheckErr(o.Run(args))
},
SuggestFor: []string{},
}
cmd.Flags().StringSliceVarP(&o.Type, "type", "t", o.Type,
fmt.Sprintf("Specify the color type, available types: `%s`.", strings.Join(availableTypes, ",")))
cmd.Flags().BoolVar(&o.Example, "example", o.Example, "Print code to show how to use color package.")
return cmd
}
// Complete completes all the required options.
func (o *ColorOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
if len(o.Type) == 0 {
o.Type = []string{"fg", "bg"}
return nil
}
if stringutil.StringIn("all", o.Type) {
o.Type = []string{"fg", "fg-hi", "bg", "bg-hi", "base"}
}
return nil
}
// Validate makes sure there is no discrepency in command options.
func (o *ColorOptions) Validate(cmd *cobra.Command, args []string) error {
for _, t := range o.Type {
if !stringutil.StringIn(t, availableTypes) {
return cmdutil.UsageErrorf(cmd, "--type must be in: %s", strings.Join(availableTypes, ","))
}
}
return nil
}
// Run executes a color subcommand using the specified options.
func (o *ColorOptions) Run(args []string) error {
if o.Example {
fmt.Fprint(o.Out, colorCodeExample+"\n")
return nil
}
var data [][]string
// print table to stdout
table := tablewriter.NewWriter(o.Out)
// set table attributes
table.SetAutoMergeCells(true)
table.SetRowLine(false)
table.SetBorder(false)
table.SetAutoWrapText(false)
table.SetAutoFormatHeaders(true)
table.SetHeaderAlignment(tablewriter.ALIGN_CENTER)
table.SetAlignment(tablewriter.ALIGN_LEFT)
table.SetHeader([]string{"Category", "Color Name", "Effect"})
table.SetHeaderColor(tablewriter.Colors{tablewriter.FgGreenColor},
tablewriter.Colors{tablewriter.FgRedColor},
tablewriter.Colors{tablewriter.FgCyanColor})
for _, t := range o.Type {
switch t {
// Foreground text colors
case "fg":
fg := [][]string{
{"fg", "black", color.BlackString("color.BlackString")},
{"fg", "red", color.RedString("color.RedString")},
{"fg", "green", color.GreenString("color.GreenString")},
{"fg", "yellow", color.YellowString("color.YellowString")},
{"fg", "blue", color.BlueString("color.BlueString")},
{"fg", "magenta", color.MagentaString("color.MagentaString")},
{"fg", "Cyan", color.CyanString("color.CyanString")},
{"fg", "white", color.WhiteString("color.WhiteString")},
}
data = append(data, fg...)
// Foreground Hi-Intensity text colors
case "fg-hi":
fg := [][]string{
{"fg-hi", "black", color.HiBlackString("color.HiBlackString")},
{"fg-hi", "red", color.HiRedString("color.HiRedString")},
{"fg-hi", "green", color.HiGreenString("color.HiGreenString")},
{"fg-hi", "yellow", color.HiYellowString("color.HiYellowString")},
{"fg-hi", "blue", color.HiBlueString("color.HiBlueString")},
{"fg-hi", "magenta", color.HiMagentaString("color.HiMagentaString")},
{"fg-hi", "Cyan", color.HiCyanString("color.HiCyanString")},
{"fg-hi", "white", color.HiWhiteString("color.HiWhiteString")},
}
data = append(data, fg...)
// Background text colors
case "bg":
bg := [][]string{
{"bg", "black", color.New(color.FgWhite, color.BgBlack).SprintFunc()("color.BgBlack")},
{"bg", "red", color.New(color.FgBlack, color.BgRed).SprintFunc()("color.BgRed")},
{"bg", "greep", color.New(color.FgBlack, color.BgGreen).SprintFunc()("color.BgGreen")},
{"bg", "yellow", color.New(color.FgWhite, color.BgYellow).SprintFunc()("color.BgYellow")},
{"bg", "blue", color.New(color.FgWhite, color.BgBlue).SprintFunc()("color.BgBlue")},
{"bg", "magenta", color.New(color.FgWhite, color.BgMagenta).SprintFunc()("color.BgMagenta")},
{"bg", "cyan", color.New(color.FgWhite, color.BgCyan).SprintFunc()("color.BgCyan")},
{"bg", "white", color.New(color.FgBlack, color.BgWhite).SprintFunc()("color.BgWhite")},
}
data = append(data, bg...)
// Background Hi-Intensity text colors
case "bg-hi":
bghi := [][]string{
{"bg-hi", "black", color.New(color.FgWhite, color.BgHiBlack).SprintFunc()("color.BgHiBlack")},
{"bg-hi", "red", color.New(color.FgBlack, color.BgHiRed).SprintFunc()("color.BgHiRed")},
{"bg-hi", "greep", color.New(color.FgBlack, color.BgHiGreen).SprintFunc()("color.BgHiGreen")},
{"bg-hi", "yellow", color.New(color.FgWhite, color.BgHiYellow).SprintFunc()("color.BgHiYellow")},
{"bg-hi", "blue", color.New(color.FgWhite, color.BgHiBlue).SprintFunc()("color.BgHiBlue")},
{"bg-hi", "magenta", color.New(color.FgWhite, color.BgHiMagenta).SprintFunc()("color.BgHiMagenta")},
{"bg-hi", "cyan", color.New(color.FgWhite, color.BgHiCyan).SprintFunc()("color.BgHiCyan")},
{"bg-hi", "white", color.New(color.FgBlack, color.BgHiWhite).SprintFunc()("color.BgHiWhite")},
}
data = append(data, bghi...)
// Base attributes
case "base":
base := [][]string{
{"base", "black", color.New(color.FgGreen, color.Reset).SprintFunc()("color.Reset")},
{"base", "red", color.New(color.FgGreen, color.Bold).SprintFunc()("color.Bold")},
{"base", "greep", color.New(color.FgGreen, color.Faint).SprintFunc()("color.Faint")},
{"base", "yellow", color.New(color.FgGreen, color.Italic).SprintFunc()("color.Italic")},
{"base", "blue", color.New(color.FgGreen, color.Underline).SprintFunc()("color.Underline")},
{"base", "magenta", color.New(color.FgGreen, color.BlinkSlow).SprintFunc()("color.BlinkSlow")},
{"base", "cyan", color.New(color.FgGreen, color.BlinkRapid).SprintFunc()("color.BlinkRapid")},
{"base", "white", color.New(color.FgGreen, color.ReverseVideo).SprintFunc()("color.ReverseVideo")},
{"base", "white", color.New(color.FgGreen, color.Concealed).SprintFunc()("color.Concealed")},
{"base", "white", color.New(color.FgGreen, color.CrossedOut).SprintFunc()("color.CrossedOut")},
}
data = append(data, base...)
default:
}
}
table.AppendBulk(data)
table.Render()
return nil
}

@ -0,0 +1,283 @@
// Copyright © 2023 OpenIM. All rights reserved.
//
// 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 completion output shell completion code for the specified shell (bash or zsh).
package completion
import (
"bytes"
"io"
"github.com/spf13/cobra"
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
)
const defaultBoilerPlate = `
# Copyright © 2023 OpenIM. All rights reserved.
#
# 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.
`
var (
completionLong = templates.LongDesc(`
Output shell completion code for the specified shell (bash or zsh).
The shell code must be evaluated to provide interactive
completion of iamctl commands. This can be done by sourcing it from
the .bash_profile.
Detailed instructions on how to do this are available here:
http://github.com/marmotedu/iam/docs/installation/iamctl.md#enabling-shell-autocompletion
Note for zsh users: [1] zsh completions are only supported in versions of zsh >= 5.2`)
completionExample = templates.Examples(`
# Installing bash completion on macOS using homebrew
## If running Bash 3.2 included with macOS
brew install bash-completion
## or, if running Bash 4.1+
brew install bash-completion@2
## If iamctl is installed via homebrew, this should start working immediately.
## If you've installed via other means, you may need add the completion to your completion directory
iamctl completion bash > $(brew --prefix)/etc/bash_completion.d/iamctl
# Installing bash completion on Linux
## If bash-completion is not installed on Linux, please install the 'bash-completion' package
## via your distribution's package manager.
## Load the iamctl completion code for bash into the current shell
source <(iamctl completion bash)
## Write bash completion code to a file and source if from .bash_profile
iamctl completion bash > ~/.iam/completion.bash.inc
printf "
# IAM shell completion
source '$HOME/.iam/completion.bash.inc'
" >> $HOME/.bash_profile
source $HOME/.bash_profile
# Load the iamctl completion code for zsh[1] into the current shell
source <(iamctl completion zsh)
# Set the iamctl completion code for zsh[1] to autoload on startup
iamctl completion zsh > "${fpath[1]}/_iamctl"`)
)
var completionShells = map[string]func(out io.Writer, boilerPlate string, cmd *cobra.Command) error{
"bash": runCompletionBash,
"zsh": runCompletionZsh,
}
// NewCmdCompletion creates the `completion` command.
func NewCmdCompletion(out io.Writer, boilerPlate string) *cobra.Command {
shells := []string{}
for s := range completionShells {
shells = append(shells, s)
}
cmd := &cobra.Command{
Use: "completion SHELL",
DisableFlagsInUseLine: true,
Short: "Output shell completion code for the specified shell (bash or zsh)",
Long: completionLong,
Example: completionExample,
Run: func(cmd *cobra.Command, args []string) {
err := RunCompletion(out, boilerPlate, cmd, args)
cmdutil.CheckErr(err)
},
ValidArgs: shells,
}
return cmd
}
// RunCompletion checks given arguments and executes command.
func RunCompletion(out io.Writer, boilerPlate string, cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return cmdutil.UsageErrorf(cmd, "Shell not specified.")
}
if len(args) > 1 {
return cmdutil.UsageErrorf(cmd, "Too many arguments. Expected only the shell type.")
}
run, found := completionShells[args[0]]
if !found {
return cmdutil.UsageErrorf(cmd, "Unsupported shell type %q.", args[0])
}
return run(out, boilerPlate, cmd.Parent())
}
func runCompletionBash(out io.Writer, boilerPlate string, iamctl *cobra.Command) error {
if len(boilerPlate) == 0 {
boilerPlate = defaultBoilerPlate
}
if _, err := out.Write([]byte(boilerPlate)); err != nil {
return err
}
return iamctl.GenBashCompletion(out)
}
func runCompletionZsh(out io.Writer, boilerPlate string, iamctl *cobra.Command) error {
zshHead := "#compdef iamctl\n"
if _, err := out.Write([]byte(zshHead)); err != nil {
return err
}
if len(boilerPlate) == 0 {
boilerPlate = defaultBoilerPlate
}
if _, err := out.Write([]byte(boilerPlate)); err != nil {
return err
}
zshInitialization := `
__iamctl_bash_source() {
alias shopt=':'
emulate -L sh
setopt kshglob noshglob braceexpand
source "$@"
}
__iamctl_type() {
# -t is not supported by zsh
if [ "$1" == "-t" ]; then
shift
# fake Bash 4 to disable "complete -o nospace". Instead
# "compopt +-o nospace" is used in the code to toggle trailing
# spaces. We don't support that, but leave trailing spaces on
# all the time
if [ "$1" = "__iamctl_compopt" ]; then
echo builtin
return 0
fi
fi
type "$@"
}
__iamctl_compgen() {
local completions w
completions=( $(compgen "$@") ) || return $?
# filter by given word as prefix
while [[ "$1" = -* && "$1" != -- ]]; do
shift
shift
done
if [[ "$1" == -- ]]; then
shift
fi
for w in "${completions[@]}"; do
if [[ "${w}" = "$1"* ]]; then
echo "${w}"
fi
done
}
__iamctl_compopt() {
true # don't do anything. Not supported by bashcompinit in zsh
}
__iamctl_ltrim_colon_completions()
{
if [[ "$1" == *:* && "$COMP_WORDBREAKS" == *:* ]]; then
# Remove colon-word prefix from COMPREPLY items
local colon_word=${1%${1##*:}}
local i=${#COMPREPLY[*]}
while [[ $((--i)) -ge 0 ]]; do
COMPREPLY[$i]=${COMPREPLY[$i]#"$colon_word"}
done
fi
}
__iamctl_get_comp_words_by_ref() {
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[${COMP_CWORD}-1]}"
words=("${COMP_WORDS[@]}")
cword=("${COMP_CWORD[@]}")
}
__iamctl_filedir() {
# Don't need to do anything here.
# Otherwise we will get trailing space without "compopt -o nospace"
true
}
autoload -U +X bashcompinit && bashcompinit
# use word boundary patterns for BSD or GNU sed
LWORD='[[:<:]]'
RWORD='[[:>:]]'
if sed --help 2>&1 | grep -q 'GNU\|BusyBox'; then
LWORD='\<'
RWORD='\>'
fi
__iamctl_convert_bash_to_zsh() {
sed \
-e 's/declare -F/whence -w/' \
-e 's/_get_comp_words_by_ref "\$@"/_get_comp_words_by_ref "\$*"/' \
-e 's/local \([a-zA-Z0-9_]*\)=/local \1; \1=/' \
-e 's/flags+=("\(--.*\)=")/flags+=("\1"); two_word_flags+=("\1")/' \
-e 's/must_have_one_flag+=("\(--.*\)=")/must_have_one_flag+=("\1")/' \
-e "s/${LWORD}_filedir${RWORD}/__iamctl_filedir/g" \
-e "s/${LWORD}_get_comp_words_by_ref${RWORD}/__iamctl_get_comp_words_by_ref/g" \
-e "s/${LWORD}__ltrim_colon_completions${RWORD}/__iamctl_ltrim_colon_completions/g" \
-e "s/${LWORD}compgen${RWORD}/__iamctl_compgen/g" \
-e "s/${LWORD}compopt${RWORD}/__iamctl_compopt/g" \
-e "s/${LWORD}declare${RWORD}/builtin declare/g" \
-e "s/\\\$(type${RWORD}/\$(__iamctl_type/g" \
<<'BASH_COMPLETION_EOF'
`
if _, err := out.Write([]byte(zshInitialization)); err != nil {
return err
}
buf := new(bytes.Buffer)
if err := iamctl.GenBashCompletion(buf); err != nil {
return err
}
if _, err := out.Write(buf.Bytes()); err != nil {
return err
}
zshTail := `
BASH_COMPLETION_EOF
}
__iamctl_bash_source <(__iamctl_convert_bash_to_zsh)
`
if _, err := out.Write([]byte(zshTail)); err != nil {
return err
}
return nil
}

@ -0,0 +1,119 @@
// Copyright © 2023 OpenIM. All rights reserved.
//
// 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 info print the host information.
package info
import (
"fmt"
"reflect"
"strconv"
hoststat "github.com/likexian/host-stat-go"
"github.com/openim-sigs/component-base/pkg/util/iputil"
"github.com/spf13/cobra"
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
)
// Info defines the host information struct.
type Info struct {
HostName string
IPAddress string
OSRelease string
CPUCore uint64
MemTotal string
MemFree string
}
// InfoOptions is an options struct to support 'info' sub command.
type InfoOptions struct {
genericclioptions.IOStreams
}
var infoExample = templates.Examples(`
# Print the host information
iamctl info`)
// NewInfoOptions returns an initialized InfoOptions instance.
func NewInfoOptions(ioStreams genericclioptions.IOStreams) *InfoOptions {
return &InfoOptions{
IOStreams: ioStreams,
}
}
// NewCmdInfo returns new initialized instance of 'info' sub command.
func NewCmdInfo(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
o := NewInfoOptions(ioStreams)
cmd := &cobra.Command{
Use: "info",
DisableFlagsInUseLine: true,
Aliases: []string{},
Short: "Print the host information",
Long: "Print the host information.",
Example: infoExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Run(args))
},
SuggestFor: []string{},
}
return cmd
}
// Run executes an info sub command using the specified options.
func (o *InfoOptions) Run(args []string) error {
var info Info
hostInfo, err := hoststat.GetHostInfo()
if err != nil {
return fmt.Errorf("get host info failed!error:%w", err)
}
info.HostName = hostInfo.HostName
info.OSRelease = hostInfo.Release + " " + hostInfo.OSBit
memStat, err := hoststat.GetMemStat()
if err != nil {
return fmt.Errorf("get mem stat failed!error:%w", err)
}
info.MemTotal = strconv.FormatUint(memStat.MemTotal, 10) + "M"
info.MemFree = strconv.FormatUint(memStat.MemFree, 10) + "M"
info.IPAddress = iputil.GetLocalIP()
cpuStat, err := hoststat.GetCPUInfo()
if err != nil {
return fmt.Errorf("get cpu stat failed!error:%w", err)
}
info.CPUCore = cpuStat.CoreCount
s := reflect.ValueOf(&info).Elem()
typeOfInfo := s.Type()
for i := 0; i < s.NumField(); i++ {
f := s.Field(i)
v := fmt.Sprintf("%v", f.Interface())
if v != "" {
fmt.Fprintf(o.Out, "%12s %v\n", typeOfInfo.Field(i).Name+":", f.Interface())
}
}
return nil
}

@ -0,0 +1,48 @@
// Copyright © 2023 OpenIM. All rights reserved.
//
// 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 jwt
import (
"fmt"
"strings"
"github.com/openim-sigs/component-base/pkg/json"
)
// ArgList defines a new pflag Value.
type ArgList map[string]string
// String return value of ArgList in string format.
func (l ArgList) String() string {
data, _ := json.Marshal(l)
return string(data)
}
// Set sets the value of ArgList.
func (l ArgList) Set(arg string) error {
parts := strings.SplitN(arg, "=", 2)
if len(parts) != 2 {
return fmt.Errorf("invalid argument '%v'. Must use format 'key=value'. %v", arg, parts)
}
l[parts[0]] = parts[1]
return nil
}
// Type returns the type name of ArgList.
func (l ArgList) Type() string {
return "map"
}

@ -0,0 +1,47 @@
// Copyright © 2023 OpenIM. All rights reserved.
//
// 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 jwt can be used to sign/show/verify jwt token with given secretID and secretKey.
package jwt
import (
"github.com/spf13/cobra"
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
)
var jwtLong = templates.LongDesc(`
JWT command.
This commands is used to sigin/show/verify jwt token.`)
// NewCmdJWT returns new initialized instance of 'jwt' sub command.
func NewCmdJWT(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
cmd := &cobra.Command{
Use: "jwt SUBCOMMAND",
DisableFlagsInUseLine: true,
Short: "JWT command-line tool",
Long: jwtLong,
Run: cmdutil.DefaultSubCommandRun(ioStreams.ErrOut),
}
// add subcommands
cmd.AddCommand(NewCmdSign(f, ioStreams))
cmd.AddCommand(NewCmdShow(f, ioStreams))
cmd.AddCommand(NewCmdVerify(f, ioStreams))
return cmd
}

@ -0,0 +1,144 @@
// Copyright © 2023 OpenIM. All rights reserved.
//
// 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 jwt
import (
"fmt"
"regexp"
"github.com/golang-jwt/jwt/v4"
"github.com/openim-sigs/component-base/pkg/json"
"github.com/spf13/cobra"
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
)
const (
showUsageStr = "show TOKEN"
)
// ShowOptions is an options struct to support show subcommands.
type ShowOptions struct {
Compact bool
genericclioptions.IOStreams
}
var (
showExample = templates.Examples(`
# Show header and Claims for a JWT token
iamctl jwt show XXX.XXX.XXX`)
showUsageErrStr = fmt.Sprintf("expected '%s'.\nTOKEN is required arguments for the show command", showUsageStr)
)
// NewShowOptions returns an initialized ShowOptions instance.
func NewShowOptions(ioStreams genericclioptions.IOStreams) *ShowOptions {
return &ShowOptions{
Compact: false,
IOStreams: ioStreams,
}
}
// NewCmdShow returns new initialized instance of show sub command.
func NewCmdShow(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
o := NewShowOptions(ioStreams)
cmd := &cobra.Command{
Use: showUsageStr,
DisableFlagsInUseLine: true,
Aliases: []string{},
Short: "Show header and claims for a JWT token",
Long: "Show header and claims for a JWT token",
TraverseChildren: true,
Example: showExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate(cmd, args))
cmdutil.CheckErr(o.Run(args))
},
SuggestFor: []string{},
Args: func(cmd *cobra.Command, args []string) error {
if len(args) != 1 {
return cmdutil.UsageErrorf(cmd, showUsageErrStr)
}
return nil
},
}
// mark flag as deprecated
cmd.Flags().BoolVar(&o.Compact, "compact", o.Compact, "output compact JSON.")
return cmd
}
// Complete completes all the required options.
func (o *ShowOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
return nil
}
// Validate makes sure there is no discrepency in command options.
func (o *ShowOptions) Validate(cmd *cobra.Command, args []string) error {
return nil
}
// Run executes a show subcommand using the specified options.
func (o *ShowOptions) Run(args []string) error {
// get the token
tokenData := []byte(args[0])
// trim possible whitespace from token
tokenData = regexp.MustCompile(`\s*$`).ReplaceAll(tokenData, []byte{})
token, err := jwt.Parse(string(tokenData), nil)
if token == nil {
return fmt.Errorf("malformed token: %w", err)
}
// Print the token details
fmt.Println("Header:")
if err := printJSON(o.Compact, token.Header); err != nil {
return fmt.Errorf("failed to output header: %w", err)
}
fmt.Println("Claims:")
if err := printJSON(o.Compact, token.Claims); err != nil {
return fmt.Errorf("failed to output claims: %w", err)
}
return nil
}
// printJSON print a json object in accordance with the prophecy (or the command line options).
func printJSON(compact bool, j interface{}) error {
var out []byte
var err error
if !compact {
out, err = json.MarshalIndent(j, "", " ")
} else {
out, err = json.Marshal(j)
}
if err == nil {
fmt.Println(string(out))
}
return err
}

@ -0,0 +1,185 @@
// Copyright © 2023 OpenIM. All rights reserved.
//
// 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 jwt
import (
"errors"
"fmt"
"time"
"github.com/golang-jwt/jwt/v4"
"github.com/spf13/cobra"
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
"github.com/marmotedu/iam/internal/pkg/middleware/auth"
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
)
const (
signUsageStr = "sign SECRETID SECRETKEY"
)
// ErrSigningMethod defines invalid signing method error.
var ErrSigningMethod = errors.New("invalid signing method")
// SignOptions is an options struct to support sign subcommands.
type SignOptions struct {
Timeout time.Duration
NotBefore time.Duration
Algorithm string
Audience string
Issuer string
Claims ArgList
Head ArgList
genericclioptions.IOStreams
}
var (
signExample = templates.Examples(`
# Sign a token with secretID and secretKey
iamctl sign tgydj8d9EQSnFqKf iBdEdFNBLN1nR3fV
# Sign a token with expires and sign method
iamctl sign tgydj8d9EQSnFqKf iBdEdFNBLN1nR3fV --timeout=2h --algorithm=HS256`)
signUsageErrStr = fmt.Sprintf(
"expected '%s'.\nSECRETID and SECRETKEY are required arguments for the sign command",
signUsageStr,
)
)
// NewSignOptions returns an initialized SignOptions instance.
func NewSignOptions(ioStreams genericclioptions.IOStreams) *SignOptions {
return &SignOptions{
Timeout: 2 * time.Hour,
Algorithm: "HS256",
Audience: auth.AuthzAudience,
Issuer: "iamctl",
Claims: make(ArgList),
Head: make(ArgList),
IOStreams: ioStreams,
}
}
// NewCmdSign returns new initialized instance of sign sub command.
func NewCmdSign(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
o := NewSignOptions(ioStreams)
cmd := &cobra.Command{
Use: signUsageStr,
DisableFlagsInUseLine: true,
Aliases: []string{},
Short: "Sign a jwt token with given secretID and secretKey",
Long: "Sign a jwt token with given secretID and secretKey",
TraverseChildren: true,
Example: signExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate(cmd, args))
cmdutil.CheckErr(o.Run(args))
},
SuggestFor: []string{},
Args: func(cmd *cobra.Command, args []string) error {
if len(args) != 2 {
return cmdutil.UsageErrorf(cmd, signUsageErrStr)
}
return nil
},
}
// mark flag as deprecated
cmd.Flags().DurationVar(&o.Timeout, "timeout", o.Timeout, "JWT token expires time.")
cmd.Flags().DurationVar(
&o.NotBefore,
"not-before",
o.NotBefore,
"Identifies the time before which the JWT MUST NOT be accepted for processing.",
)
cmd.Flags().StringVar(
&o.Algorithm,
"algorithm",
o.Algorithm,
"Signing algorithm - possible values are HS256, HS384, HS512.",
)
cmd.Flags().StringVar(
&o.Audience,
"audience",
o.Audience,
"Identifies the recipients that the JWT is intended for.",
)
cmd.Flags().StringVar(&o.Issuer, "issuer", o.Issuer, "Identifies the principal that issued the JWT.")
cmd.Flags().Var(&o.Claims, "claim", "Add additional claims. may be used more than once.")
cmd.Flags().Var(&o.Head, "header", "Add additional header params. may be used more than once.")
return cmd
}
// Complete completes all the required options.
func (o *SignOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
return nil
}
// Validate makes sure there is no discrepency in command options.
func (o *SignOptions) Validate(cmd *cobra.Command, args []string) error {
switch o.Algorithm {
case "HS256", "HS384", "HS512":
default:
return ErrSigningMethod
}
return nil
}
// Run executes a sign subcommand using the specified options.
func (o *SignOptions) Run(args []string) error {
claims := jwt.MapClaims{
"exp": time.Now().Add(o.Timeout).Unix(),
"iat": time.Now().Unix(),
"nbf": time.Now().Add(o.NotBefore).Unix(),
"aud": o.Audience,
"iss": o.Issuer,
}
// add command line claims
if len(o.Claims) > 0 {
for k, v := range o.Claims {
claims[k] = v
}
}
// create a new token
token := jwt.NewWithClaims(jwt.GetSigningMethod(o.Algorithm), claims)
// add command line headers
if len(o.Head) > 0 {
for k, v := range o.Head {
token.Header[k] = v
}
}
token.Header["kid"] = args[0]
tokenString, err := token.SignedString([]byte(args[1]))
if err != nil {
return err
}
fmt.Fprintf(o.Out, tokenString+"\n")
return nil
}

@ -0,0 +1,150 @@
// Copyright © 2023 OpenIM. All rights reserved.
//
// 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 jwt
import (
"fmt"
"regexp"
"github.com/golang-jwt/jwt/v4"
"github.com/spf13/cobra"
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
)
const (
veirfyUsageStr = "veirfy SECRETKEY TOKEN"
)
// VerifyOptions is an options struct to support verify subcommands.
type VerifyOptions struct {
Compact bool
Debug bool
genericclioptions.IOStreams
}
var (
verifyExample = templates.Examples(`
# Verify a JWT token
iamctl jwt verify XXX xxxxx.yyyyy.zzzzz`)
verifyUsageErrStr = fmt.Sprintf(
"expected '%s'.\nSECRETKEY and TOKEN are required arguments for the subcmd1 command",
veirfyUsageStr,
)
)
// NewVerifyOptions returns an initialized VerifyOptions instance.
func NewVerifyOptions(ioStreams genericclioptions.IOStreams) *VerifyOptions {
return &VerifyOptions{
Compact: false,
Debug: false,
IOStreams: ioStreams,
}
}
// NewCmdVerify returns new initialized instance of verify sub command.
func NewCmdVerify(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
o := NewVerifyOptions(ioStreams)
cmd := &cobra.Command{
Use: "verify",
DisableFlagsInUseLine: true,
Aliases: []string{"sub2"},
Short: "Verify a JWT token",
Long: "Verify a JWT token",
TraverseChildren: true,
Example: verifyExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate(cmd, args))
cmdutil.CheckErr(o.Run(args))
},
SuggestFor: []string{},
Args: func(cmd *cobra.Command, args []string) error {
if len(args) != 2 {
return cmdutil.UsageErrorf(cmd, verifyUsageErrStr)
}
return nil
},
}
// mark flag as deprecated
cmd.Flags().BoolVar(&o.Compact, "compact", o.Compact, "Output compact JSON.")
cmd.Flags().BoolVar(&o.Debug, "debug", o.Debug, "Print out all kinds of debug data.")
return cmd
}
// Complete completes all the required options.
func (o *VerifyOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
return nil
}
// Validate makes sure there is no discrepency in command options.
func (o *VerifyOptions) Validate(cmd *cobra.Command, args []string) error {
return nil
}
// Run executes a verify subcommand using the specified options.
func (o *VerifyOptions) Run(args []string) error {
// get the token
tokenData := []byte(args[1])
// trim possible whitespace from token
tokenData = regexp.MustCompile(`\s*$`).ReplaceAll(tokenData, []byte{})
// Parse the token. Load the key from command line option
token, err := jwt.Parse(string(tokenData), func(t *jwt.Token) (interface{}, error) {
return []byte(args[0]), nil
})
// Print some debug data
if o.Debug && token != nil {
fmt.Println("Header:")
if pErr := printJSON(o.Compact, token.Header); pErr != nil {
return fmt.Errorf("failed to output header: %w", pErr)
}
fmt.Println("Claims:")
if pErr := printJSON(o.Compact, token.Claims); pErr != nil {
return fmt.Errorf("failed to output claims: %w", pErr)
}
}
// Print an error if we can't parse for some reason
if err != nil {
return fmt.Errorf("couldn't parse token: %w", err)
}
// Is token invalid?
if !token.Valid {
return fmt.Errorf("token is invalid")
}
if !o.Debug {
// Print the token details
if err := printJSON(o.Compact, token.Claims); err != nil {
return fmt.Errorf("failed to output claims: %w", err)
}
}
return nil
}

@ -0,0 +1,619 @@
// Copyright © 2023 OpenIM. All rights reserved.
//
// 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 new used to generate demo command code.
// nolint: predeclared
package new
import (
"fmt"
"os"
"path/filepath"
"strings"
"text/template"
"github.com/openim-sigs/component-base/pkg/util/fileutil"
"github.com/spf13/cobra"
"golang.org/x/text/cases"
"golang.org/x/text/language"
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
)
const (
newUsageStr = "new CMD_NAME | CMD_NAME CMD_DESCRIPTION"
)
var (
newLong = templates.LongDesc(`Used to generate demo command source code file.
Can use this command generate a command template file, and do some modify based on your needs.
This can improve your R&D efficiency.`)
newExample = templates.Examples(`
# Create a default 'test' command file without a description
iamctl new test
# Create a default 'test' command file in /tmp/
iamctl new test -d /tmp/
# Create a default 'test' command file with a description
iamctl new test "This is a test command"
# Create command 'test' with two subcommands
iamctl new -g test "This is a test command with two subcommands"`)
newUsageErrStr = fmt.Sprintf(
"expected '%s'.\nat least CMD_NAME is a required argument for the new command",
newUsageStr,
)
cmdTemplate = `package {{.CommandName}}
import (
"fmt"
"github.com/spf13/cobra"
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
)
const (
{{.CommandName}}UsageStr = "{{.CommandName}} USERNAME PASSWORD"
maxStringLength = 17
)
// {{.CommandFunctionName}}Options is an options struct to support '{{.CommandName}}' sub command.
type {{.CommandFunctionName}}Options struct {
// options
StringOption string
StringSliceOption []string
IntOption int
BoolOption bool
// args
Username string
Password string
genericclioptions.IOStreams
}
var (
{{.CommandName}}Long = templates.LongDesc({{.Dot}}A longer description that spans multiple lines and likely contains examples
and usage of using your command. For example:
Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.{{.Dot}})
{{.CommandName}}Example = templates.Examples({{.Dot}}
# Print all option values for {{.CommandName}}
iamctl {{.CommandName}} marmotedu marmotedupass{{.Dot}})
{{.CommandName}}UsageErrStr = fmt.Sprintf("expected '%s'.\nUSERNAME and PASSWORD are required arguments for the {{.CommandName}} command", {{.CommandName}}UsageStr)
)
// New{{.CommandFunctionName}}Options returns an initialized {{.CommandFunctionName}}Options instance.
func New{{.CommandFunctionName}}Options(ioStreams genericclioptions.IOStreams) *{{.CommandFunctionName}}Options {
return &{{.CommandFunctionName}}Options{
StringOption: "default",
IOStreams: ioStreams,
}
}
// NewCmd{{.CommandFunctionName}} returns new initialized instance of '{{.CommandName}}' sub command.
func NewCmd{{.CommandFunctionName}}(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
o := New{{.CommandFunctionName}}Options(ioStreams)
cmd := &cobra.Command{
Use: {{.CommandName}}UsageStr,
DisableFlagsInUseLine: true,
Aliases: []string{},
Short: "{{.CommandDescription}}",
TraverseChildren: true,
Long: {{.CommandName}}Long,
Example: {{.CommandName}}Example,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate(cmd, args))
cmdutil.CheckErr(o.Run(args))
},
SuggestFor: []string{},
Args: func(cmd *cobra.Command, args []string) error {
// nolint: gomnd // no need
if len(args) < 2 {
return cmdutil.UsageErrorf(cmd, {{.CommandName}}UsageErrStr)
}
// if need args equal to zero, uncomment the following code
/*
if len(args) != 0 {
return cmdutil.UsageErrorf(cmd, "Unexpected args: %v", args)
}
*/
return nil
},
}
// mark flag as deprecated
_ = cmd.Flags().MarkDeprecated("deprecated-opt", "This flag is deprecated and will be removed in future.")
cmd.Flags().StringVarP(&o.StringOption, "string", "", o.StringOption, "String option.")
cmd.Flags().StringSliceVar(&o.StringSliceOption, "slice", o.StringSliceOption, "String slice option.")
cmd.Flags().IntVarP(&o.IntOption, "int", "i", o.IntOption, "Int option.")
cmd.Flags().BoolVarP(&o.BoolOption, "bool", "b", o.BoolOption, "Bool option.")
return cmd
}
// Complete completes all the required options.
func (o *{{.CommandFunctionName}}Options) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
if o.StringOption != "" {
o.StringOption += "(complete)"
}
o.Username = args[0]
o.Password = args[1]
return nil
}
// Validate makes sure there is no discrepency in command options.
func (o *{{.CommandFunctionName}}Options) Validate(cmd *cobra.Command, args []string) error {
if len(o.StringOption) > maxStringLength {
return cmdutil.UsageErrorf(cmd, "--string length must less than 18")
}
if o.IntOption < 0 {
return cmdutil.UsageErrorf(cmd, "--int must be a positive integer: %v", o.IntOption)
}
return nil
}
// Run executes a {{.CommandName}} sub command using the specified options.
func (o *{{.CommandFunctionName}}Options) Run(args []string) error {
fmt.Fprintf(o.Out, "The following is option values:\n")
fmt.Fprintf(o.Out, "==> --string: %v\n==> --slice: %v\n==> --int: %v\n==> --bool: %v\n",
o.StringOption, o.StringSliceOption, o.IntOption, o.BoolOption)
fmt.Fprintf(o.Out, "\nThe following is args values:\n")
fmt.Fprintf(o.Out, "==> username: %v\n==> password: %v\n", o.Username, o.Password)
return nil
}
`
maincmdTemplate = `package {{.CommandName}}
import (
"github.com/spf13/cobra"
"github.com/spf13/viper"
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
)
const maxStringLength = 17
var (
{{.CommandName}}Long = templates.LongDesc({{.Dot}}
Demo command.
This commands show you how to implement a command with two sub commands.{{.Dot}})
)
// NewCmd{{.CommandFunctionName}} returns new initialized instance of '{{.CommandName}}' sub command.
func NewCmd{{.CommandFunctionName}}(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
cmd := &cobra.Command{
Use: "{{.CommandName}} SUBCOMMAND",
DisableFlagsInUseLine: true,
Short: "{{.CommandDescription}}",
Long: {{.CommandName}}Long,
Run: cmdutil.DefaultSubCommandRun(ioStreams.ErrOut),
}
// add subcommands
cmd.AddCommand(NewCmdSubCmd1(f, ioStreams))
cmd.AddCommand(NewCmdSubCmd2(f, ioStreams))
// add persistent flags for '{{.CommandName}}'
cmdutil.AddCleanFlags(cmd)
// persistent flag, we can get the value in subcommand via {{.Dot}}viper.Get{{.Dot}}
cmd.PersistentFlags().StringP("persistent", "p", "this is a persistent option", "Cobra persistent option.")
// bind flags with viper
viper.BindPFlag("persistent", cmd.PersistentFlags().Lookup("persistent"))
return cmd
}
`
subcmd1Template = `package {{.CommandName}}
import (
"fmt"
"github.com/spf13/cobra"
"github.com/spf13/viper"
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
)
const (
subcmd1UsageStr = "subcmd1 USERNAME PASSWORD"
)
// SubCmd1Options is an options struct to support subcmd1 subcommands.
type SubCmd1Options struct {
// options
StringOption string
StringSliceOption []string
IntOption int
BoolOption bool
PersistentOption string
// args
Username string
Password string
genericclioptions.IOStreams
}
var (
subcmd1Long = templates.LongDesc({{.Dot}}A longer description that spans multiple lines and likely contains examples
and usage of using your command. For example:
Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.{{.Dot}})
subcmd1Example = templates.Examples({{.Dot}}
# Print all option values for subcmd1
iamctl {{.CommandName}} subcmd1 marmotedu marmotedupass
# Print all option values for subcmd1 with --persistent specified
iamctl {{.CommandName}} subcmd1 marmotedu marmotedupass --persistent="specified persistent option in command line"{{.Dot}})
subcmd1UsageErrStr = fmt.Sprintf("expected '%s'.\nUSERNAME and PASSWORD are required arguments for the subcmd1 command", subcmd1UsageStr)
)
// NewSubCmd1Options returns an initialized SubCmd1Options instance.
func NewSubCmd1Options(ioStreams genericclioptions.IOStreams) *SubCmd1Options {
return &SubCmd1Options{
StringOption: "default",
IOStreams: ioStreams,
}
}
// NewCmdSubCmd1 returns new initialized instance of subcmd1 sub command.
func NewCmdSubCmd1(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
o := NewSubCmd1Options(ioStreams)
cmd := &cobra.Command{
Use: "subcmd1 USERNAME PASSWORD",
DisableFlagsInUseLine: true,
Aliases: []string{"sub1"},
Short: "A brief description of your command",
TraverseChildren: true,
Long: subcmd1Long,
Example: subcmd1Example,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate(cmd, args))
cmdutil.CheckErr(o.Run(args))
},
SuggestFor: []string{},
Args: func(cmd *cobra.Command, args []string) error {
// nolint: gomnd // no need
if len(args) < 2 {
return cmdutil.UsageErrorf(cmd, subcmd1UsageErrStr)
}
return nil
},
}
// mark flag as deprecated
_ = cmd.Flags().MarkDeprecated("deprecated-opt", "This flag is deprecated and will be removed in future.")
cmd.Flags().StringVarP(&o.StringOption, "string", "", o.StringOption, "String option.")
cmd.Flags().StringSliceVar(&o.StringSliceOption, "slice", o.StringSliceOption, "String slice option.")
cmd.Flags().IntVarP(&o.IntOption, "int", "i", o.IntOption, "Int option.")
cmd.Flags().BoolVarP(&o.BoolOption, "bool", "b", o.BoolOption, "Bool option.")
return cmd
}
// Complete completes all the required options.
func (o *SubCmd1Options) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
if o.StringOption != "" {
o.StringOption += "(complete)"
}
o.PersistentOption = viper.GetString("persistent")
o.Username = args[0]
o.Password = args[1]
return nil
}
// Validate makes sure there is no discrepency in command options.
func (o *SubCmd1Options) Validate(cmd *cobra.Command, args []string) error {
if len(o.StringOption) > maxStringLength {
return cmdutil.UsageErrorf(cmd, "--string length must less than 18")
}
if o.IntOption < 0 {
return cmdutil.UsageErrorf(cmd, "--int must be a positive integer: %v", o.IntOption)
}
return nil
}
// Run executes a subcmd1 subcommand using the specified options.
func (o *SubCmd1Options) Run(args []string) error {
fmt.Fprintf(o.Out, "The following is option values:\n")
fmt.Fprintf(o.Out, "==> --string: %v\n==> --slice: %v\n==> --int: %v\n==> --bool: %v\n==> --persistent: %v\n",
o.StringOption, o.StringSliceOption, o.IntOption, o.BoolOption, o.PersistentOption)
fmt.Fprintf(o.Out, "\nThe following is args values:\n")
fmt.Fprintf(o.Out, "==> username: %v\n==> password: %v\n", o.Username, o.Password)
return nil
}
`
subcmd2Template = `package {{.CommandName}}
import (
"fmt"
"github.com/spf13/cobra"
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
)
// SubCmd2Options is an options struct to support subcmd2 subcommands.
type SubCmd2Options struct {
StringOption string
StringSliceOption []string
IntOption int
BoolOption bool
genericclioptions.IOStreams
}
var (
subcmd2Long = templates.LongDesc({{.Dot}}A longer description that spans multiple lines and likely contains examples
and usage of using your command. For example:
Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.{{.Dot}})
subcmd2Example = templates.Examples({{.Dot}}
# Print all option values for subcmd2
iamctl {{.CommandName}} subcmd2{{.Dot}})
)
// NewSubCmd2Options returns an initialized SubCmd2Options instance.
func NewSubCmd2Options(ioStreams genericclioptions.IOStreams) *SubCmd2Options {
return &SubCmd2Options{
StringOption: "default",
IOStreams: ioStreams,
}
}
// NewCmdSubCmd2 returns new initialized instance of subcmd2 sub command.
func NewCmdSubCmd2(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
o := NewSubCmd2Options(ioStreams)
cmd := &cobra.Command{
Use: "subcmd2",
DisableFlagsInUseLine: true,
Aliases: []string{"sub2"},
Short: "A brief description of your command",
TraverseChildren: true,
Long: subcmd2Long,
Example: subcmd2Example,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate(cmd, args))
cmdutil.CheckErr(o.Run(args))
},
SuggestFor: []string{},
}
// mark flag as deprecated
cmd.Flags().StringVarP(&o.StringOption, "string", "", o.StringOption, "String option.")
cmd.Flags().StringSliceVar(&o.StringSliceOption, "slice", o.StringSliceOption, "String slice option.")
cmd.Flags().IntVarP(&o.IntOption, "int", "i", o.IntOption, "Int option.")
cmd.Flags().BoolVarP(&o.BoolOption, "bool", "b", o.BoolOption, "Bool option.")
return cmd
}
// Complete completes all the required options.
func (o *SubCmd2Options) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
if len(args) != 0 {
return cmdutil.UsageErrorf(cmd, "Unexpected args: %v", args)
}
if o.StringOption != "" {
o.StringOption += "(complete)"
}
return nil
}
// Validate makes sure there is no discrepency in command options.
func (o *SubCmd2Options) Validate(cmd *cobra.Command, args []string) error {
if len(o.StringOption) > maxStringLength {
return cmdutil.UsageErrorf(cmd, "--string length must less than 18")
}
if o.IntOption < 0 {
return cmdutil.UsageErrorf(cmd, "--int must be a positive integer: %v", o.IntOption)
}
return nil
}
// Run executes a subcmd2 subcommand using the specified options.
func (o *SubCmd2Options) Run(args []string) error {
fmt.Fprintf(o.Out, "The following is option values:\n")
fmt.Fprintf(o.Out, "==> --string: %v\n==> --slice: %v\n==> --int: %v\n==> --bool: %v\n",
o.StringOption, o.StringSliceOption, o.IntOption, o.BoolOption)
return nil
}
`
)
// NewOptions is an options struct to support 'new' sub command.
type NewOptions struct {
Group bool
Outdir string
// command template options, will render to command template
CommandName string
CommandDescription string
CommandFunctionName string
Dot string
genericclioptions.IOStreams
}
// NewNewOptions returns an initialized NewOptions instance.
func NewNewOptions(ioStreams genericclioptions.IOStreams) *NewOptions {
return &NewOptions{
Group: false,
Outdir: ".",
CommandDescription: "A brief description of your command",
Dot: "`",
IOStreams: ioStreams,
}
}
// NewCmdNew returns new initialized instance of 'new' sub command.
func NewCmdNew(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
o := NewNewOptions(ioStreams)
cmd := &cobra.Command{
Use: newUsageStr,
DisableFlagsInUseLine: true,
Short: "Generate demo command code",
Long: newLong,
Example: newExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(cmd, args))
cmdutil.CheckErr(o.Validate(cmd))
cmdutil.CheckErr(o.Run(args))
},
Aliases: []string{},
SuggestFor: []string{},
}
cmd.Flags().BoolVarP(&o.Group, "group", "g", o.Group, "Generate two subcommands.")
cmd.Flags().StringVarP(&o.Outdir, "outdir", "d", o.Outdir, "Where to create demo command files.")
return cmd
}
// Complete completes all the required options.
func (o *NewOptions) Complete(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return cmdutil.UsageErrorf(cmd, newUsageErrStr)
}
o.CommandName = strings.ToLower(args[0])
if len(args) > 1 {
o.CommandDescription = args[1]
}
o.CommandFunctionName = cases.Title(language.English).String(o.CommandName)
return nil
}
// Validate makes sure there is no discrepency in command options.
func (o *NewOptions) Validate(cmd *cobra.Command) error {
return nil
}
// Run executes a new sub command using the specified options.
func (o *NewOptions) Run(args []string) error {
if o.Group {
return o.CreateCommandWithSubCommands()
}
return o.CreateCommand()
}
// CreateCommand create the command with options.
func (o *NewOptions) CreateCommand() error {
return o.GenerateGoCode(o.CommandName+".go", cmdTemplate)
}
// CreateCommandWithSubCommands create sub commands with options.
func (o *NewOptions) CreateCommandWithSubCommands() error {
if err := o.GenerateGoCode(o.CommandName+".go", maincmdTemplate); err != nil {
return err
}
if err := o.GenerateGoCode(o.CommandName+"_subcmd1.go", subcmd1Template); err != nil {
return err
}
if err := o.GenerateGoCode(o.CommandName+"_subcmd2.go", subcmd2Template); err != nil {
return err
}
return nil
}
// GenerateGoCode generate go source file.
func (o *NewOptions) GenerateGoCode(name, codeTemplate string) error {
tmpl, err := template.New("cmd").Parse(codeTemplate)
if err != nil {
return err
}
err = fileutil.EnsureDirAll(o.Outdir)
if err != nil {
return err
}
filename := filepath.Join(o.Outdir, name)
fd, err := os.Create(filename)
if err != nil {
return err
}
defer fd.Close()
err = tmpl.Execute(fd, o)
if err != nil {
return err
}
fmt.Printf("Command file generated: %s\n", filename)
return nil
}

@ -0,0 +1,50 @@
// Copyright © 2023 OpenIM. All rights reserved.
//
// 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 options print a list of global command-line options (applies to all commands).
package options
import (
"io"
"github.com/spf13/cobra"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
)
var optionsExample = templates.Examples(`
# Print flags inherited by all commands
iamctl options`)
// NewCmdOptions implements the options command.
func NewCmdOptions(out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "options",
Short: "Print the list of flags inherited by all commands",
Long: "Print the list of flags inherited by all commands",
Example: optionsExample,
Run: func(cmd *cobra.Command, args []string) {
_ = cmd.Usage()
},
}
// The `options` command needs write its output to the `out` stream
// (typically stdout). Without calling SetOutput here, the Usage()
// function call will fall back to stderr.
cmd.SetOutput(out)
templates.UseOptionsTemplates(cmd)
return cmd
}

@ -0,0 +1,48 @@
// Copyright © 2023 OpenIM. All rights reserved.
//
// 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 policy provides functions to manage authorization policies on iam platform.
package policy
import (
"github.com/spf13/cobra"
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
)
var policyLong = templates.LongDesc(`
Authorization policy management commands.
This commands allow you to manage your authorization policy on iam platform.`)
// NewCmdPolicy returns new initialized instance of 'policy' sub command.
func NewCmdPolicy(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
cmd := &cobra.Command{
Use: "policy SUBCOMMAND",
DisableFlagsInUseLine: true,
Short: "Manage authorization policies on iam platform",
Long: policyLong,
Run: cmdutil.DefaultSubCommandRun(ioStreams.ErrOut),
}
cmd.AddCommand(NewCmdCreate(f, ioStreams))
cmd.AddCommand(NewCmdGet(f, ioStreams))
cmd.AddCommand(NewCmdList(f, ioStreams))
cmd.AddCommand(NewCmdDelete(f, ioStreams))
cmd.AddCommand(NewCmdUpdate(f, ioStreams))
return cmd
}

@ -0,0 +1,137 @@
// Copyright © 2023 OpenIM. All rights reserved.
//
// 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 policy
import (
"context"
"fmt"
v1 "github.com/marmotedu/api/apiserver/v1"
apiclientv1 "github.com/marmotedu/marmotedu-sdk-go/marmotedu/service/iam/apiserver/v1"
"github.com/openim-sigs/component-base/pkg/json"
metav1 "github.com/openim-sigs/component-base/pkg/meta/v1"
"github.com/ory/ladon"
"github.com/spf13/cobra"
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
)
const (
createUsageStr = "create POLICY_NAME POLICY"
)
// CreateOptions is an options struct to support create subcommands.
type CreateOptions struct {
Policy *v1.Policy
Client apiclientv1.APIV1Interface
genericclioptions.IOStreams
}
var (
createExample = templates.Examples(`
# Create a authorization policy
iamctl policy create foo '{"description":"This is a updated policy","subjects":["users:<peter|ken>","users:maria","groups:admins"],"actions":["delete","<create|update>"],"effect":"allow","resources":["resources:articles:<.*>","resources:printer"],"conditions":{"remoteIPAddress":{"type":"CIDRCondition","options":{"cidr":"192.168.0.1/16"}}}}'`)
createUsageErrStr = fmt.Sprintf(
"expected '%s'.\nPOLICY_NAME and POLICY are required arguments for the create command",
createUsageStr,
)
)
// NewCreateOptions returns an initialized CreateOptions instance.
func NewCreateOptions(ioStreams genericclioptions.IOStreams) *CreateOptions {
return &CreateOptions{
IOStreams: ioStreams,
}
}
// NewCmdCreate returns new initialized instance of create sub command.
func NewCmdCreate(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
o := NewCreateOptions(ioStreams)
cmd := &cobra.Command{
Use: createUsageStr,
DisableFlagsInUseLine: true,
Aliases: []string{},
Short: "Create a authorization policy resource",
TraverseChildren: true,
Long: "Create a authorization policy resource.",
Example: createExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate(cmd, args))
cmdutil.CheckErr(o.Run(args))
},
SuggestFor: []string{},
}
return cmd
}
// Complete completes all the required options.
func (o *CreateOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
if len(args) < 2 {
return cmdutil.UsageErrorf(cmd, createUsageErrStr)
}
var pol ladon.DefaultPolicy
if err := json.Unmarshal([]byte(args[1]), &pol); err != nil {
return err
}
o.Policy = &v1.Policy{
ObjectMeta: metav1.ObjectMeta{
Name: args[0],
},
Policy: v1.AuthzPolicy{
DefaultPolicy: pol,
},
}
clientConfig, err := f.ToRESTConfig()
if err != nil {
return err
}
o.Client, err = apiclientv1.NewForConfig(clientConfig)
if err != nil {
return err
}
return nil
}
// Validate makes sure there is no discrepency in command options.
func (o *CreateOptions) Validate(cmd *cobra.Command, args []string) error {
if errs := o.Policy.Validate(); len(errs) != 0 {
return errs.ToAggregate()
}
return nil
}
// Run executes a create subcommand using the specified options.
func (o *CreateOptions) Run(args []string) error {
ret, err := o.Client.Policies().Create(context.TODO(), o.Policy, metav1.CreateOptions{})
if err != nil {
return err
}
fmt.Fprintf(o.Out, "policy/%s created\n", ret.Name)
return nil
}

@ -0,0 +1,115 @@
// Copyright © 2023 OpenIM. All rights reserved.
//
// 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 policy
import (
"context"
"fmt"
"github.com/marmotedu/marmotedu-sdk-go/marmotedu/service/iam"
metav1 "github.com/openim-sigs/component-base/pkg/meta/v1"
"github.com/spf13/cobra"
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
)
const (
deleteUsageStr = "delete POLICY_NAME"
)
// DeleteOptions is an options struct to support delete subcommands.
type DeleteOptions struct {
Name string
iamclient iam.IamInterface
genericclioptions.IOStreams
}
var (
deleteExample = templates.Examples(`
# Delete a policy resource
iamctl policy delete foo`)
deleteUsageErrStr = fmt.Sprintf(
"expected '%s'.\nPOLICY_NAME is required arguments for the delete command",
deleteUsageStr,
)
)
// NewDeleteOptions returns an initialized DeleteOptions instance.
func NewDeleteOptions(ioStreams genericclioptions.IOStreams) *DeleteOptions {
return &DeleteOptions{
IOStreams: ioStreams,
}
}
// NewCmdDelete returns new initialized instance of delete sub command.
func NewCmdDelete(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
o := NewDeleteOptions(ioStreams)
cmd := &cobra.Command{
Use: "delete",
DisableFlagsInUseLine: true,
Aliases: []string{},
Short: "Delete a authorization policy resource",
TraverseChildren: true,
Long: "Delete a authorization policy resource.",
Example: deleteExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate(cmd, args))
cmdutil.CheckErr(o.Run())
},
SuggestFor: []string{},
}
return cmd
}
// Complete completes all the required options.
func (o *DeleteOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
var err error
if len(args) == 0 {
return cmdutil.UsageErrorf(cmd, deleteUsageErrStr)
}
o.Name = args[0]
o.iamclient, err = f.IAMClient()
if err != nil {
return err
}
return nil
}
// Validate makes sure there is no discrepency in command options.
func (o *DeleteOptions) Validate(cmd *cobra.Command, args []string) error {
return nil
}
// Run executes a delete subcommand using the specified options.
func (o *DeleteOptions) Run() error {
if err := o.iamclient.APIV1().Policies().Delete(context.TODO(), o.Name, metav1.DeleteOptions{}); err != nil {
return err
}
fmt.Fprintf(o.Out, "policy/%s deleted\n", o.Name)
return nil
}

@ -0,0 +1,123 @@
// Copyright © 2023 OpenIM. All rights reserved.
//
// 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 policy
import (
"bytes"
"context"
"fmt"
"github.com/fatih/color"
"github.com/marmotedu/marmotedu-sdk-go/marmotedu/service/iam"
"github.com/openim-sigs/component-base/pkg/json"
metav1 "github.com/openim-sigs/component-base/pkg/meta/v1"
"github.com/spf13/cobra"
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
)
const (
getUsageStr = "get POLICY_NAME"
)
// GetOptions is an options struct to support get subcommands.
type GetOptions struct {
Name string
iamclient iam.IamInterface
genericclioptions.IOStreams
}
var (
getExample = templates.Examples(`
# Display a policy resource
iamctl policy get foo`)
getUsageErrStr = fmt.Sprintf("expected '%s'.\nPOLICY_NAME is required arguments for the get command", getUsageStr)
)
// NewGetOptions returns an initialized GetOptions instance.
func NewGetOptions(ioStreams genericclioptions.IOStreams) *GetOptions {
return &GetOptions{
IOStreams: ioStreams,
}
}
// NewCmdGet returns new initialized instance of get sub command.
func NewCmdGet(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
o := NewGetOptions(ioStreams)
cmd := &cobra.Command{
Use: getUsageStr,
DisableFlagsInUseLine: true,
Aliases: []string{},
Short: "Display a authorization policy resource",
TraverseChildren: true,
Long: "Display a authorization policy resource.",
Example: getExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate(cmd, args))
cmdutil.CheckErr(o.Run(args))
},
SuggestFor: []string{},
}
return cmd
}
// Complete completes all the required options.
func (o *GetOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
var err error
if len(args) == 0 {
return cmdutil.UsageErrorf(cmd, getUsageErrStr)
}
o.Name = args[0]
o.iamclient, err = f.IAMClient()
if err != nil {
return err
}
return nil
}
// Validate makes sure there is no discrepency in command options.
func (o *GetOptions) Validate(cmd *cobra.Command, args []string) error {
return nil
}
// Run executes a get subcommand using the specified options.
func (o *GetOptions) Run(args []string) error {
policy, err := o.iamclient.APIV1().Policies().Get(context.TODO(), o.Name, metav1.GetOptions{})
if err != nil {
return err
}
bf := bytes.NewBuffer([]byte{})
jsonEncoder := json.NewEncoder(bf)
jsonEncoder.SetEscapeHTML(false)
if err := jsonEncoder.Encode(policy.Policy); err != nil {
return err
}
fmt.Fprintf(o.Out, "%12s %s\n", color.RedString(policy.Name+":"), bf.String())
return nil
}

@ -0,0 +1,127 @@
// Copyright © 2023 OpenIM. All rights reserved.
//
// 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 policy
import (
"bytes"
"context"
"fmt"
"github.com/fatih/color"
"github.com/marmotedu/marmotedu-sdk-go/marmotedu/service/iam"
"github.com/openim-sigs/component-base/pkg/json"
metav1 "github.com/openim-sigs/component-base/pkg/meta/v1"
"github.com/spf13/cobra"
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
)
const (
defaultLimit = 1000
)
// ListOptions is an options struct to support list subcommands.
type ListOptions struct {
Offset int64
Limit int64
iamclient iam.IamInterface
genericclioptions.IOStreams
}
var listExample = templates.Examples(`
# Display all policy resources
iamctl poicy list
# Display all policy resources with offset and limit
iamctl policy list --offset=0 --limit=10`)
// NewListOptions returns an initialized ListOptions instance.
func NewListOptions(ioStreams genericclioptions.IOStreams) *ListOptions {
return &ListOptions{
Offset: 0,
Limit: defaultLimit,
IOStreams: ioStreams,
}
}
// NewCmdList returns new initialized instance of list sub command.
func NewCmdList(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
o := NewListOptions(ioStreams)
cmd := &cobra.Command{
Use: "list",
DisableFlagsInUseLine: true,
Aliases: []string{},
Short: "Display all authorization policy resources",
TraverseChildren: true,
Long: "Display all authorization policy resources.",
Example: listExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate(cmd, args))
cmdutil.CheckErr(o.Run(args))
},
SuggestFor: []string{},
}
cmd.Flags().Int64VarP(&o.Offset, "offset", "o", o.Offset, "Specify the offset of the first row to be returned.")
cmd.Flags().Int64VarP(&o.Limit, "limit", "l", o.Limit, "Specify the amount records to be returned.")
return cmd
}
// Complete completes all the required options.
func (o *ListOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
var err error
o.iamclient, err = f.IAMClient()
if err != nil {
return err
}
return nil
}
// Validate makes sure there is no discrepency in command options.
func (o *ListOptions) Validate(cmd *cobra.Command, args []string) error {
return nil
}
// Run executes a list subcommand using the specified options.
func (o *ListOptions) Run(args []string) error {
policies, err := o.iamclient.APIV1().Policies().List(context.TODO(), metav1.ListOptions{
Offset: &o.Offset,
Limit: &o.Limit,
})
if err != nil {
return err
}
for _, pol := range policies.Items {
bf := bytes.NewBuffer([]byte{})
jsonEncoder := json.NewEncoder(bf)
jsonEncoder.SetEscapeHTML(false)
if err := jsonEncoder.Encode(pol.Policy); err != nil {
return err
}
fmt.Fprintf(o.Out, "%12s %s\n", color.RedString(pol.Name+":"), bf.String())
}
return nil
}

@ -0,0 +1,131 @@
// Copyright © 2023 OpenIM. All rights reserved.
//
// 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 policy
import (
"context"
"fmt"
v1 "github.com/marmotedu/api/apiserver/v1"
"github.com/marmotedu/marmotedu-sdk-go/marmotedu/service/iam"
"github.com/openim-sigs/component-base/pkg/json"
metav1 "github.com/openim-sigs/component-base/pkg/meta/v1"
"github.com/ory/ladon"
"github.com/spf13/cobra"
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
)
const (
updateUsageStr = "update POLICY_NAME POLICY"
)
// UpdateOptions is an options struct to support update subcommands.
type UpdateOptions struct {
Policy *v1.Policy
iamclient iam.IamInterface
genericclioptions.IOStreams
}
var (
updateExample = templates.Examples(`
# Update a authorization policy with new policy.
iamctl policy update foo "{"description":"This is a updated policy","subjects":["users:<peter|ken>","users:maria","groups:admins"],"actions":["delete","<create|update>"],"effect":"allow","resources":["resources:articles:<.*>","resources:printer"],"conditions":{"remoteIPAddress":{"type":"CIDRCondition","options":{"cidr":"192.168.0.1/16"}}}}"`)
updateUsageErrStr = fmt.Sprintf(
"expected '%s'.\nPOLICY_NAME and POLICY is required arguments for the update command",
updateUsageStr,
)
)
// NewUpdateOptions returns an initialized UpdateOptions instance.
func NewUpdateOptions(ioStreams genericclioptions.IOStreams) *UpdateOptions {
return &UpdateOptions{
IOStreams: ioStreams,
}
}
// NewCmdUpdate returns new initialized instance of update sub command.
func NewCmdUpdate(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
o := NewUpdateOptions(ioStreams)
cmd := &cobra.Command{
Use: updateUsageStr,
DisableFlagsInUseLine: true,
Aliases: []string{},
Short: "Update a authorization policy resource",
TraverseChildren: true,
Long: "Update a authorization policy resource.",
Example: updateExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate(cmd, args))
cmdutil.CheckErr(o.Run(args))
},
SuggestFor: []string{},
}
return cmd
}
// Complete completes all the required options.
func (o *UpdateOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
var err error
if len(args) < 2 {
return cmdutil.UsageErrorf(cmd, updateUsageErrStr)
}
var pol ladon.DefaultPolicy
if err = json.Unmarshal([]byte(args[1]), &pol); err != nil {
return err
}
o.Policy = &v1.Policy{
ObjectMeta: metav1.ObjectMeta{
Name: args[0],
},
Policy: v1.AuthzPolicy{
DefaultPolicy: pol,
},
}
o.iamclient, err = f.IAMClient()
if err != nil {
return err
}
return nil
}
// Validate makes sure there is no discrepency in command options.
func (o *UpdateOptions) Validate(cmd *cobra.Command, args []string) error {
return nil
}
// Run executes a update subcommand using the specified options.
func (o *UpdateOptions) Run(args []string) error {
ret, err := o.iamclient.APIV1().Policies().Update(context.TODO(), o.Policy, metav1.UpdateOptions{})
if err != nil {
return err
}
fmt.Fprintf(o.Out, "policy/%s updated\n", ret.Name)
return nil
}

@ -0,0 +1,61 @@
// Copyright © 2023 OpenIM. All rights reserved.
//
// 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 secret provides functions to manage secrets on iam platform.
package secret
import (
"github.com/olekukonko/tablewriter"
"github.com/spf13/cobra"
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
)
var secretLong = templates.LongDesc(`
Secret management commands.
This commands allow you to manage your secret on iam platform.`)
// NewCmdSecret returns new initialized instance of 'secret' sub command.
func NewCmdSecret(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
cmd := &cobra.Command{
Use: "secret SUBCOMMAND",
DisableFlagsInUseLine: true,
Short: "Manage secrets on iam platform",
Long: secretLong,
Run: cmdutil.DefaultSubCommandRun(ioStreams.ErrOut),
}
cmd.AddCommand(NewCmdCreate(f, ioStreams))
cmd.AddCommand(NewCmdGet(f, ioStreams))
cmd.AddCommand(NewCmdList(f, ioStreams))
cmd.AddCommand(NewCmdDelete(f, ioStreams))
cmd.AddCommand(NewCmdUpdate(f, ioStreams))
return cmd
}
// setHeader set headers for secret commands.
func setHeader(table *tablewriter.Table) *tablewriter.Table {
table.SetHeader([]string{"Name", "SecretID", "SecretKey", "Expires", "Created"})
table.SetHeaderColor(tablewriter.Colors{tablewriter.FgGreenColor},
tablewriter.Colors{tablewriter.FgRedColor},
tablewriter.Colors{tablewriter.FgCyanColor},
tablewriter.Colors{tablewriter.FgMagentaColor},
tablewriter.Colors{tablewriter.FgGreenColor})
return table
}

@ -0,0 +1,145 @@
// Copyright © 2023 OpenIM. All rights reserved.
//
// 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 secret
import (
"context"
"fmt"
"time"
v1 "github.com/marmotedu/api/apiserver/v1"
apiclientv1 "github.com/marmotedu/marmotedu-sdk-go/marmotedu/service/iam/apiserver/v1"
metav1 "github.com/openim-sigs/component-base/pkg/meta/v1"
"github.com/spf13/cobra"
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
)
const (
createUsageStr = "create SECRET_NAME"
)
// CreateOptions is an options struct to support create subcommands.
type CreateOptions struct {
Description string
Expires int64
Secret *v1.Secret
Client apiclientv1.APIV1Interface
genericclioptions.IOStreams
}
var (
createLong = templates.LongDesc(`Create secret resource.
This will generate secretID and secretKey which can be used to sign JWT token.`)
createExample = templates.Examples(`
# Create secret which will expired after 2 hours
iamctl secret create foo
# Create secret with a specified expire time and description
iamctl secret create foo --expires=1988121600 --description="secret for iam"`)
createUsageErrStr = fmt.Sprintf(
"expected '%s'.\nSECRET_NAME is required arguments for the create command",
createUsageStr,
)
)
// NewCreateOptions returns an initialized CreateOptions instance.
func NewCreateOptions(ioStreams genericclioptions.IOStreams) *CreateOptions {
return &CreateOptions{
Expires: time.Now().Add(144 * time.Hour).Unix(),
IOStreams: ioStreams,
}
}
// NewCmdCreate returns new initialized instance of create sub command.
func NewCmdCreate(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
o := NewCreateOptions(ioStreams)
cmd := &cobra.Command{
Use: createUsageStr,
DisableFlagsInUseLine: true,
Aliases: []string{},
Short: "Create secret resource",
TraverseChildren: true,
Long: createLong,
Example: createExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate(cmd, args))
cmdutil.CheckErr(o.Run(args))
},
SuggestFor: []string{},
}
cmd.Flags().StringVar(&o.Description, "description", o.Description, "The descriptin of the secret.")
cmd.Flags().Int64Var(&o.Expires, "expires", o.Expires, "The expire time of the secret.")
return cmd
}
// Complete completes all the required options.
func (o *CreateOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return cmdutil.UsageErrorf(cmd, createUsageErrStr)
}
o.Secret = &v1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: args[0],
},
Expires: o.Expires,
Description: o.Description,
}
clientConfig, err := f.ToRESTConfig()
if err != nil {
return err
}
o.Client, err = apiclientv1.NewForConfig(clientConfig)
if err != nil {
return err
}
return nil
}
// Validate makes sure there is no discrepency in command options.
func (o *CreateOptions) Validate(cmd *cobra.Command, args []string) error {
if errs := o.Secret.Validate(); len(errs) != 0 {
return errs.ToAggregate()
}
return nil
}
// Run executes a create subcommand using the specified options.
func (o *CreateOptions) Run(args []string) error {
secret, err := o.Client.Secrets().Create(context.TODO(), o.Secret, metav1.CreateOptions{})
if err != nil {
return err
}
fmt.Fprintf(o.Out, "secret/%s created\n", secret.Name)
return nil
}

@ -0,0 +1,115 @@
// Copyright © 2023 OpenIM. All rights reserved.
//
// 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 secret
import (
"context"
"fmt"
"github.com/marmotedu/marmotedu-sdk-go/marmotedu/service/iam"
metav1 "github.com/openim-sigs/component-base/pkg/meta/v1"
"github.com/spf13/cobra"
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
)
const (
deleteUsageStr = "delete SECRET_NAME"
)
// DeleteOptions is an options struct to support delete subcommands.
type DeleteOptions struct {
Name string
genericclioptions.IOStreams
iamclient iam.IamInterface
}
var (
deleteExample = templates.Examples(`
# Delete secret foo
iamctl secret delete foo`)
deleteUsageErrStr = fmt.Sprintf(
"expected '%s'.\nSECRET_NAME is required arguments for the delete command",
deleteUsageStr,
)
)
// NewDeleteOptions returns an initialized DeleteOptions instance.
func NewDeleteOptions(ioStreams genericclioptions.IOStreams) *DeleteOptions {
return &DeleteOptions{
IOStreams: ioStreams,
}
}
// NewCmdDelete returns new initialized instance of delete sub command.
func NewCmdDelete(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
o := NewDeleteOptions(ioStreams)
cmd := &cobra.Command{
Use: deleteUsageStr,
DisableFlagsInUseLine: true,
Aliases: []string{},
Short: "Delete a secret resource",
TraverseChildren: true,
Long: "Delete a secret resource.",
Example: deleteExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate(cmd, args))
cmdutil.CheckErr(o.Run())
},
SuggestFor: []string{},
}
return cmd
}
// Complete completes all the required options.
func (o *DeleteOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
var err error
if len(args) == 0 {
return cmdutil.UsageErrorf(cmd, deleteUsageErrStr)
}
o.Name = args[0]
o.iamclient, err = f.IAMClient()
if err != nil {
return err
}
return nil
}
// Validate makes sure there is no discrepency in command options.
func (o *DeleteOptions) Validate(cmd *cobra.Command, args []string) error {
return nil
}
// Run executes a delete subcommand using the specified options.
func (o *DeleteOptions) Run() error {
if err := o.iamclient.APIV1().Secrets().Delete(context.TODO(), o.Name, metav1.DeleteOptions{}); err != nil {
return err
}
fmt.Fprintf(o.Out, "secret/%s deleted\n", o.Name)
return nil
}

@ -0,0 +1,130 @@
// Copyright © 2023 OpenIM. All rights reserved.
//
// 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 secret
import (
"context"
"fmt"
"time"
"github.com/marmotedu/marmotedu-sdk-go/marmotedu/service/iam"
"github.com/olekukonko/tablewriter"
metav1 "github.com/openim-sigs/component-base/pkg/meta/v1"
"github.com/spf13/cobra"
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
)
const (
getUsageStr = "get SECRET_NAME"
)
// GetOptions is an options struct to support get subcommands.
type GetOptions struct {
Name string
iamclient iam.IamInterface
genericclioptions.IOStreams
}
var (
getExample = templates.Examples(`
# Get a specified secret information
iamctl secret get foo`)
getUsageErrStr = fmt.Sprintf("expected '%s'.\nSECRET_NAME is required arguments for the get command", getUsageStr)
)
// NewGetOptions returns an initialized GetOptions instance.
func NewGetOptions(ioStreams genericclioptions.IOStreams) *GetOptions {
return &GetOptions{
IOStreams: ioStreams,
}
}
// NewCmdGet returns new initialized instance of get sub command.
func NewCmdGet(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
o := NewGetOptions(ioStreams)
cmd := &cobra.Command{
Use: "get SECRET_NAME",
DisableFlagsInUseLine: true,
Aliases: []string{},
Short: "Display a secret resource",
TraverseChildren: true,
Long: "Display a secret resource.",
Example: getExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate(cmd, args))
cmdutil.CheckErr(o.Run(args))
},
SuggestFor: []string{},
}
return cmd
}
// Complete completes all the required options.
func (o *GetOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
var err error
if len(args) == 0 {
return cmdutil.UsageErrorf(cmd, getUsageErrStr)
}
o.Name = args[0]
o.iamclient, err = f.IAMClient()
if err != nil {
return err
}
return nil
}
// Validate makes sure there is no discrepency in command options.
func (o *GetOptions) Validate(cmd *cobra.Command, args []string) error {
return nil
}
// Run executes a get subcommand using the specified options.
func (o *GetOptions) Run(args []string) error {
secret, err := o.iamclient.APIV1().Secrets().Get(context.TODO(), o.Name, metav1.GetOptions{})
if err != nil {
return err
}
table := tablewriter.NewWriter(o.Out)
data := [][]string{
{
secret.Name,
secret.SecretID,
secret.SecretKey,
time.Unix(secret.Expires, 0).Format("2006-01-02 15:04:05"),
secret.CreatedAt.Format("2006-01-02 15:04:05"),
},
}
table = setHeader(table)
table = cmdutil.TableWriterDefaultConfig(table)
table.AppendBulk(data)
table.Render()
return nil
}

@ -0,0 +1,131 @@
// Copyright © 2023 OpenIM. All rights reserved.
//
// 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 secret
import (
"context"
"time"
"github.com/marmotedu/marmotedu-sdk-go/marmotedu/service/iam"
"github.com/olekukonko/tablewriter"
metav1 "github.com/openim-sigs/component-base/pkg/meta/v1"
"github.com/spf13/cobra"
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
)
const (
defaltLimit = 1000
)
// ListOptions is an options struct to support list subcommands.
type ListOptions struct {
Offset int64
Limit int64
iamclient iam.IamInterface
genericclioptions.IOStreams
}
var listExample = templates.Examples(`
# List all secrets
iamctl secret list
# List secrets with limit and offset
iamctl secret list --offset=0 --limit=5`)
// NewListOptions returns an initialized ListOptions instance.
func NewListOptions(ioStreams genericclioptions.IOStreams) *ListOptions {
return &ListOptions{
IOStreams: ioStreams,
Offset: 0,
Limit: defaltLimit,
}
}
// NewCmdList returns new initialized instance of list sub command.
func NewCmdList(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
o := NewListOptions(ioStreams)
cmd := &cobra.Command{
Use: "list",
DisableFlagsInUseLine: true,
Aliases: []string{},
Short: "Display all secret resources",
TraverseChildren: true,
Long: "Display all secret resources.",
Example: listExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate(cmd, args))
cmdutil.CheckErr(o.Run(args))
},
SuggestFor: []string{},
}
cmd.Flags().Int64VarP(&o.Offset, "offset", "o", o.Offset, "Specify the offset of the first row to be returned.")
cmd.Flags().Int64VarP(&o.Limit, "limit", "l", o.Limit, "Specify the amount records to be returned.")
return cmd
}
// Complete completes all the required options.
func (o *ListOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
var err error
o.iamclient, err = f.IAMClient()
if err != nil {
return err
}
return nil
}
// Validate makes sure there is no discrepency in command options.
func (o *ListOptions) Validate(cmd *cobra.Command, args []string) error {
return nil
}
// Run executes a list subcommand using the specified options.
func (o *ListOptions) Run(args []string) error {
secrets, err := o.iamclient.APIV1().Secrets().List(context.TODO(), metav1.ListOptions{
Offset: &o.Offset,
Limit: &o.Limit,
})
if err != nil {
return err
}
data := make([][]string, 0, len(secrets.Items))
table := tablewriter.NewWriter(o.Out)
for _, secret := range secrets.Items {
data = append(data, []string{
secret.Name,
secret.SecretID,
secret.SecretKey,
time.Unix(secret.Expires, 0).Format("2006-01-02 15:04:05"),
secret.CreatedAt.Format("2006-01-02 15:04:05"),
})
}
table = setHeader(table)
table = cmdutil.TableWriterDefaultConfig(table)
table.AppendBulk(data)
table.Render()
return nil
}

@ -0,0 +1,128 @@
// Copyright © 2023 OpenIM. All rights reserved.
//
// 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 secret
import (
"context"
"fmt"
v1 "github.com/marmotedu/api/apiserver/v1"
"github.com/marmotedu/marmotedu-sdk-go/marmotedu/service/iam"
metav1 "github.com/openim-sigs/component-base/pkg/meta/v1"
"github.com/spf13/cobra"
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
)
const (
updateUsageStr = "update SECRET_NAME"
)
// UpdateOptions is an options struct to support update subcommands.
type UpdateOptions struct {
Description string
Expires int64
Secret *v1.Secret
iamclient iam.IamInterface
genericclioptions.IOStreams
}
var (
updateExample = templates.Examples(`
# Update a secret resource
iamctl secret update foo --expires=4h --description="new description"`)
updateUsageErrStr = fmt.Sprintf(
"expected '%s'.\nSECRET_NAME is required arguments for the update command",
updateUsageStr,
)
)
// NewUpdateOptions returns an initialized UpdateOptions instance.
func NewUpdateOptions(ioStreams genericclioptions.IOStreams) *UpdateOptions {
return &UpdateOptions{
IOStreams: ioStreams,
}
}
// NewCmdUpdate returns new initialized instance of update sub command.
func NewCmdUpdate(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
o := NewUpdateOptions(ioStreams)
cmd := &cobra.Command{
Use: "update SECRET_NAME",
DisableFlagsInUseLine: true,
Aliases: []string{},
Short: "Update a secret resource",
TraverseChildren: true,
Long: "Update a secret resource.",
Example: updateExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate(cmd, args))
cmdutil.CheckErr(o.Run(args))
},
SuggestFor: []string{},
}
cmd.Flags().StringVar(&o.Description, "description", o.Description, "The description of the secret.")
cmd.Flags().Int64Var(&o.Expires, "expires", o.Expires, "The expires of the secret.")
return cmd
}
// Complete completes all the required options.
func (o *UpdateOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
var err error
if len(args) == 0 {
return cmdutil.UsageErrorf(cmd, updateUsageErrStr)
}
o.Secret = &v1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: args[0],
},
Description: o.Description,
Expires: o.Expires,
}
o.iamclient, err = f.IAMClient()
if err != nil {
return err
}
return nil
}
// Validate makes sure there is no discrepency in command options.
func (o *UpdateOptions) Validate(cmd *cobra.Command, args []string) error {
return nil
}
// Run executes a update subcommand using the specified options.
func (o *UpdateOptions) Run(args []string) error {
secret, err := o.iamclient.APIV1().Secrets().Update(context.TODO(), o.Secret, metav1.UpdateOptions{})
if err != nil {
return err
}
fmt.Fprintf(o.Out, "secret/%s updated\n", secret.Name)
return nil
}

@ -0,0 +1,45 @@
// Copyright © 2023 OpenIM. All rights reserved.
//
// 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 set used to set specific features on objects.
package set
import (
"github.com/spf13/cobra"
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
)
var setLong = templates.LongDesc(`
Configure objects.
These commands help you make changes to existing objects.`)
// NewCmdSet returns an initialized Command instance for 'set' sub command.
func NewCmdSet(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
cmd := &cobra.Command{
Use: "set SUBCOMMAND",
DisableFlagsInUseLine: true,
Short: "Set specific features on objects",
Long: setLong,
Run: cmdutil.DefaultSubCommandRun(ioStreams.ErrOut),
}
// add subcommands
// cmd.AddCommand(NewCmdDB(f, ioStreams))
return cmd
}

@ -0,0 +1,213 @@
// Copyright © 2023 OpenIM. All rights reserved.
//
// 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 set
import (
"fmt"
"github.com/jinzhu/gorm"
// import mysql driver.
_ "github.com/jinzhu/gorm/dialects/mysql"
v1 "github.com/marmotedu/api/apiserver/v1"
metav1 "github.com/openim-sigs/component-base/pkg/meta/v1"
"github.com/spf13/cobra"
"github.com/spf13/viper"
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
)
// DBOptions is an options struct to support 'db' sub command.
type DBOptions struct {
host string
username string
password string
Database string
drop bool
admin bool
genericclioptions.IOStreams
}
var setExample = templates.Examples(`
# Create new iam platform database and tables
iamctl set db --mysql.host=127.0.0.1:3306 --mysql.username=iam --mysql.password=iamxxxx --mysql.database=iam
# Create new iam platform database and tables with a administrator inserted
iamctl set db --admin --mysql.host=127.0.0.1:3306 --mysql.username=iam --mysql.password=iamxxxx --mysql.database=iam
# drop and create iam platform database and tables
iamctl set db -d --mysql.host=127.0.0.1:3306 --mysql.username=iam --mysql.password=iamxxxx --mysql.database=iam`)
// NewDBOptions returns an initialized DBOptions instance.
func NewDBOptions(ioStreams genericclioptions.IOStreams) *DBOptions {
return &DBOptions{
host: "127.0.0.1:3306",
username: "root",
password: "root",
Database: "iam",
drop: false,
admin: false,
IOStreams: ioStreams,
}
}
// NewCmdDB returns new initialized instance of 'db' sub command.
func NewCmdDB(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
o := NewDBOptions(ioStreams)
cmd := &cobra.Command{
Use: "db",
DisableFlagsInUseLine: true,
Short: "Initialize the iam database",
Long: "Initialize the iam database.",
Example: setExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete())
cmdutil.CheckErr(o.Validate())
cmdutil.CheckErr(o.Run())
},
Aliases: []string{},
SuggestFor: []string{},
}
cmd.Flags().StringVar(&o.host, "host", o.host, "MySQL service host address.")
cmd.Flags().StringVar(&o.username, "username", o.username, "username for access to mysql service.")
cmd.Flags().StringVar(&o.password, "password", o.password,
"password for access to mysql, should be used pair with password.")
cmd.Flags().StringVar(&o.Database, "database", o.Database, "Database name for the server to use.")
cmd.Flags().BoolVarP(&o.drop, "drop", "d", o.drop, "drop database if exists, pls double check the db name!")
cmd.Flags().BoolVar(&o.admin, "admin", o.drop, "Insert a administrator user to the database.")
_ = viper.BindPFlags(cmd.Flags())
return cmd
}
// Complete completes all the required options.
func (o *DBOptions) Complete() error {
// o.host = viper.GetString("host")
// o.username = viper.GetString("username")
// o.password = viper.GetString("password")
// o.Database = viper.GetString("database")
// o.drop = viper.GetBool("drop")
// o.admin = viper.GetBool("admin")
return nil
}
// Validate makes sure there is no discrepency in command options.
func (o *DBOptions) Validate() error {
return nil
}
// Run executes a db sub command using the specified options.
func (o *DBOptions) Run() error {
if err := o.ensureSchema(); err != nil {
return err
}
dsn := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8&parseTime=%t&loc=%s",
o.username, o.password, o.host, o.Database, true, "Local")
db, err := gorm.Open("mysql", dsn)
if err != nil {
return err
}
defer db.Close()
if db.HasTable(&v1.User{}) {
db.AutoMigrate(&v1.User{})
} else {
db.Debug().CreateTable(&v1.User{})
}
if db.HasTable(&v1.Secret{}) {
db.AutoMigrate(&v1.Secret{})
} else {
db.CreateTable(&v1.Secret{})
}
if db.HasTable(&v1.Policy{}) {
db.AutoMigrate(&v1.Policy{})
} else {
db.CreateTable(&v1.Policy{})
}
fmt.Fprintf(o.Out, "update table success\n")
if o.admin {
if err := o.insertAdministrator(db); err != nil {
return err
}
}
return nil
}
func (o *DBOptions) ensureSchema() error {
dsn := fmt.Sprintf("%s:%s@tcp(%s)/?charset=utf8", o.username, o.password, o.host)
db, err := gorm.Open("mysql", dsn)
if err != nil {
return err
}
defer db.Close()
if o.drop {
dropSQL := fmt.Sprintf("DROP DATABASE IF EXISTS %s", o.Database)
if err := db.Exec(dropSQL).Error; err != nil {
return err
}
fmt.Fprintf(o.Out, "drop database %s success\n", o.Database)
}
createSQL := fmt.Sprintf("CREATE DATABASE if not exists %s CHARSET utf8 COLLATE utf8_general_ci", o.Database)
if err := db.Exec(createSQL).Error; err != nil {
return err
}
fmt.Fprintf(o.Out, "create database %s success\n", o.Database)
return nil
}
func (o *DBOptions) insertAdministrator(db *gorm.DB) error {
if !o.drop {
return nil
}
// insert administrator user
user := v1.User{
ObjectMeta: metav1.ObjectMeta{
Name: "admin",
},
Nickname: "admin",
Password: "Admin@2020",
Email: "colin404@foxmail.com",
Phone: "1812884xxxx",
IsAdmin: 1,
}
if err := db.Create(&user).Error; err != nil {
return err
}
fmt.Fprintf(o.Out, "insert administrator success\n")
return nil
}

@ -0,0 +1,62 @@
// Copyright © 2023 OpenIM. All rights reserved.
//
// 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 user provides functions to manage users on iam platform.
package user
import (
"github.com/olekukonko/tablewriter"
"github.com/spf13/cobra"
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
)
var userLong = templates.LongDesc(`
User management commands.
Administrator can use all subcommands, non-administrator only allow to use create/get/upate. When call get/update non-administrator only allow to operate their own resources, if permission not allowed, will return an 'Permission denied' error.`)
// NewCmdUser returns new initialized instance of 'user' sub command.
func NewCmdUser(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
cmd := &cobra.Command{
Use: "user SUBCOMMAND",
DisableFlagsInUseLine: true,
Short: "Manage users on iam platform",
Long: userLong,
Run: cmdutil.DefaultSubCommandRun(ioStreams.ErrOut),
}
cmd.AddCommand(NewCmdCreate(f, ioStreams))
cmd.AddCommand(NewCmdGet(f, ioStreams))
cmd.AddCommand(NewCmdList(f, ioStreams))
cmd.AddCommand(NewCmdDelete(f, ioStreams))
cmd.AddCommand(NewCmdUpdate(f, ioStreams))
return cmd
}
// setHeader set headers for user commands.
func setHeader(table *tablewriter.Table) *tablewriter.Table {
table.SetHeader([]string{"Name", "Nickname", "Email", "Phone", "Created", "Updated"})
table.SetHeaderColor(tablewriter.Colors{tablewriter.FgGreenColor},
tablewriter.Colors{tablewriter.FgRedColor},
tablewriter.Colors{tablewriter.FgCyanColor},
tablewriter.Colors{tablewriter.FgMagentaColor},
tablewriter.Colors{tablewriter.FgGreenColor},
tablewriter.Colors{tablewriter.FgWhiteColor})
return table
}

@ -0,0 +1,149 @@
// Copyright © 2023 OpenIM. All rights reserved.
//
// 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 user
import (
"context"
"fmt"
v1 "github.com/marmotedu/api/apiserver/v1"
apiclientv1 "github.com/marmotedu/marmotedu-sdk-go/marmotedu/service/iam/apiserver/v1"
metav1 "github.com/openim-sigs/component-base/pkg/meta/v1"
"github.com/spf13/cobra"
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
)
const (
createUsageStr = "create USERNAME PASSWORD EMAIL"
)
// CreateOptions is an options struct to support create subcommands.
type CreateOptions struct {
Nickname string
Phone string
User *v1.User
Client apiclientv1.APIV1Interface
genericclioptions.IOStreams
}
var (
createLong = templates.LongDesc(`Create a user on iam platform.
If nickname not specified, username will be used.`)
createExample = templates.Examples(`
# Create user with given input
iamctl user create foo Foo@2020 foo@foxmail.com
# Create user wt
iamctl user create foo Foo@2020 foo@foxmail.com --phone=18128845xxx --nickname=colin`)
createUsageErrStr = fmt.Sprintf(
"expected '%s'.\nUSERNAME, PASSWORD and EMAIL are required arguments for the create command",
createUsageStr,
)
)
// NewCreateOptions returns an initialized CreateOptions instance.
func NewCreateOptions(ioStreams genericclioptions.IOStreams) *CreateOptions {
return &CreateOptions{
IOStreams: ioStreams,
}
}
// NewCmdCreate returns new initialized instance of create sub command.
func NewCmdCreate(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
o := NewCreateOptions(ioStreams)
cmd := &cobra.Command{
Use: createUsageStr,
DisableFlagsInUseLine: true,
Aliases: []string{},
Short: "Create a user resource",
TraverseChildren: true,
Long: createLong,
Example: createExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate(cmd, args))
cmdutil.CheckErr(o.Run(args))
},
SuggestFor: []string{},
}
// mark flag as deprecated
cmd.Flags().StringVar(&o.Nickname, "nickname", o.Nickname, "The nickname of the user.")
cmd.Flags().StringVar(&o.Phone, "phone", o.Phone, "The phone number of the user.")
return cmd
}
// Complete completes all the required options.
func (o *CreateOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
var err error
if len(args) < 3 {
return cmdutil.UsageErrorf(cmd, createUsageErrStr)
}
if o.Nickname == "" {
o.Nickname = args[0]
}
o.User = &v1.User{
ObjectMeta: metav1.ObjectMeta{
Name: args[0],
},
Nickname: o.Nickname,
Password: args[1],
Email: args[2],
Phone: o.Phone,
}
clientConfig, err := f.ToRESTConfig()
if err != nil {
return err
}
o.Client, err = apiclientv1.NewForConfig(clientConfig)
if err != nil {
return err
}
return nil
}
// Validate makes sure there is no discrepency in command options.
func (o *CreateOptions) Validate(cmd *cobra.Command, args []string) error {
if errs := o.User.Validate(); len(errs) != 0 {
return errs.ToAggregate()
}
return nil
}
// Run executes a create subcommand using the specified options.
func (o *CreateOptions) Run(args []string) error {
ret, err := o.Client.Users().Create(context.TODO(), o.User, metav1.CreateOptions{})
if err != nil {
return err
}
fmt.Fprintf(o.Out, "user/%s created\n", ret.Name)
return nil
}

@ -0,0 +1,114 @@
// Copyright © 2023 OpenIM. All rights reserved.
//
// 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 user
import (
"context"
"fmt"
"github.com/marmotedu/marmotedu-sdk-go/marmotedu/service/iam"
metav1 "github.com/openim-sigs/component-base/pkg/meta/v1"
"github.com/spf13/cobra"
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
)
const (
deleteUsageStr = "delete USERNAME"
)
// DeleteOptions is an options struct to support delete subcommands.
type DeleteOptions struct {
Name string
iamclient iam.IamInterface
genericclioptions.IOStreams
}
var (
deleteExample = templates.Examples(`
# Delete user foo from platform
iamctl user delete foo`)
deleteUsageErrStr = fmt.Sprintf(
"expected '%s'.\nUSERNAME is required arguments for the delete command",
deleteUsageStr,
)
)
// NewDeleteOptions returns an initialized DeleteOptions instance.
func NewDeleteOptions(ioStreams genericclioptions.IOStreams) *DeleteOptions {
return &DeleteOptions{
IOStreams: ioStreams,
}
}
// NewCmdDelete returns new initialized instance of delete sub command.
func NewCmdDelete(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
o := NewDeleteOptions(ioStreams)
cmd := &cobra.Command{
Use: deleteUsageStr,
DisableFlagsInUseLine: true,
Aliases: []string{},
Short: "Delete a user resource from iam platform (Administrator rights required)",
TraverseChildren: true,
Long: "Delete a user resource from iam platform, only administrator can do this operation.",
Example: deleteExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate(cmd, args))
cmdutil.CheckErr(o.Run())
},
SuggestFor: []string{},
}
return cmd
}
// Complete completes all the required options.
func (o *DeleteOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
var err error
if len(args) == 0 {
return cmdutil.UsageErrorf(cmd, deleteUsageErrStr)
}
o.Name = args[0]
o.iamclient, err = f.IAMClient()
if err != nil {
return err
}
return nil
}
// Validate makes sure there is no discrepency in command options.
func (o *DeleteOptions) Validate(cmd *cobra.Command, args []string) error {
return nil
}
// Run executes a delete subcommand using the specified options.
func (o *DeleteOptions) Run() error {
if err := o.iamclient.APIV1().Users().Delete(context.TODO(), o.Name, metav1.DeleteOptions{}); err != nil {
return err
}
fmt.Fprintf(o.Out, "user/%s deleted\n", o.Name)
return nil
}

@ -0,0 +1,129 @@
// Copyright © 2023 OpenIM. All rights reserved.
//
// 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 user
import (
"context"
"fmt"
"github.com/marmotedu/marmotedu-sdk-go/marmotedu/service/iam"
"github.com/olekukonko/tablewriter"
metav1 "github.com/openim-sigs/component-base/pkg/meta/v1"
"github.com/spf13/cobra"
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
)
const (
getUsageStr = "get USERNAME"
)
// GetOptions is an options struct to support get subcommands.
type GetOptions struct {
Name string
iamclient iam.IamInterface
genericclioptions.IOStreams
}
var (
getExample = templates.Examples(`
# Get user foo detail information
iamctl user get foo`)
getUsageErrStr = fmt.Sprintf("expected '%s'.\nUSERNAME is required arguments for the get command", getUsageStr)
)
// NewGetOptions returns an initialized GetOptions instance.
func NewGetOptions(ioStreams genericclioptions.IOStreams) *GetOptions {
return &GetOptions{
IOStreams: ioStreams,
}
}
// NewCmdGet returns new initialized instance of get sub command.
func NewCmdGet(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
o := NewGetOptions(ioStreams)
cmd := &cobra.Command{
Use: getUsageStr,
DisableFlagsInUseLine: true,
Aliases: []string{},
Short: "Display a user resource.",
TraverseChildren: true,
Long: `Display a user resource.`,
Example: getExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate(cmd, args))
cmdutil.CheckErr(o.Run(args))
},
SuggestFor: []string{},
}
return cmd
}
// Complete completes all the required options.
func (o *GetOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
var err error
if len(args) == 0 {
return cmdutil.UsageErrorf(cmd, getUsageErrStr)
}
o.Name = args[0]
o.iamclient, err = f.IAMClient()
if err != nil {
return err
}
return nil
}
// Validate makes sure there is no discrepency in command options.
func (o *GetOptions) Validate(cmd *cobra.Command, args []string) error {
return nil
}
// Run executes a get subcommand using the specified options.
func (o *GetOptions) Run(args []string) error {
user, err := o.iamclient.APIV1().Users().Get(context.TODO(), o.Name, metav1.GetOptions{})
if err != nil {
return err
}
table := tablewriter.NewWriter(o.Out)
data := [][]string{
{
user.Name,
user.Nickname,
user.Email,
user.Phone,
user.CreatedAt.Format("2006-01-02 15:04:05"),
user.UpdatedAt.Format("2006-01-02 15:04:05"),
},
}
table = setHeader(table)
table = cmdutil.TableWriterDefaultConfig(table)
table.AppendBulk(data)
table.Render()
return nil
}

@ -0,0 +1,128 @@
// Copyright © 2023 OpenIM. All rights reserved.
//
// 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 user
import (
"context"
"github.com/marmotedu/marmotedu-sdk-go/marmotedu/service/iam"
"github.com/olekukonko/tablewriter"
metav1 "github.com/openim-sigs/component-base/pkg/meta/v1"
"github.com/spf13/cobra"
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
)
const (
defaultLimit = 1000
)
// ListOptions is an options struct to support list subcommands.
type ListOptions struct {
Offset int64
Limit int64
iamclient iam.IamInterface
genericclioptions.IOStreams
}
var listExample = templates.Examples(`
# List all users
iamctl user list
# List users with limit and offset
iamctl user list --offset=0 --limit=10`)
// NewListOptions returns an initialized ListOptions instance.
func NewListOptions(ioStreams genericclioptions.IOStreams) *ListOptions {
return &ListOptions{
IOStreams: ioStreams,
Offset: 0,
Limit: defaultLimit,
}
}
// NewCmdList returns new initialized instance of list sub command.
func NewCmdList(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
o := NewListOptions(ioStreams)
cmd := &cobra.Command{
Use: "list",
DisableFlagsInUseLine: true,
Aliases: []string{},
Short: "Display all users in iam platform (Administrator rights required)",
TraverseChildren: true,
Long: "Display all users in iam platform (Administrator rights required).",
Example: listExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate(cmd, args))
cmdutil.CheckErr(o.Run(args))
},
SuggestFor: []string{},
}
cmd.Flags().Int64VarP(&o.Offset, "offset", "o", o.Offset, "Specify the offset of the first row to be returned.")
cmd.Flags().Int64VarP(&o.Limit, "limit", "l", o.Limit, "Specify the amount records to be returned.")
return cmd
}
// Complete completes all the required options.
func (o *ListOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
var err error
o.iamclient, err = f.IAMClient()
if err != nil {
return err
}
return nil
}
// Validate makes sure there is no discrepency in command options.
func (o *ListOptions) Validate(cmd *cobra.Command, args []string) error {
return nil
}
// Run executes a list subcommand using the specified options.
func (o *ListOptions) Run(args []string) error {
users, err := o.iamclient.APIV1().Users().List(context.TODO(), metav1.ListOptions{
Offset: &o.Offset,
Limit: &o.Limit,
})
if err != nil {
return err
}
data := make([][]string, 0, 1)
table := tablewriter.NewWriter(o.Out)
for _, user := range users.Items {
data = append(data, []string{
user.Name, user.Nickname, user.Email,
user.Phone, user.CreatedAt.Format("2006-01-02 15:04:05"), user.UpdatedAt.Format("2006-01-02 15:04:05"),
})
}
table = setHeader(table)
table = cmdutil.TableWriterDefaultConfig(table)
table.AppendBulk(data)
table.Render()
return nil
}

@ -0,0 +1,142 @@
// Copyright © 2023 OpenIM. All rights reserved.
//
// 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 user
import (
"context"
"fmt"
"github.com/marmotedu/marmotedu-sdk-go/marmotedu/service/iam"
metav1 "github.com/openim-sigs/component-base/pkg/meta/v1"
"github.com/spf13/cobra"
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
)
const (
updateUsageStr = "update USERNAME"
)
// UpdateOptions is an options struct to support update subcommands.
type UpdateOptions struct {
Name string
Nickname string
Email string
Phone string
iamclient iam.IamInterface
genericclioptions.IOStreams
}
var (
updateLong = templates.LongDesc(`Update a user resource.
Can only update nickname, email and phone.
NOTICE: field will be updated to zero value if not specified.`)
updateExample = templates.Examples(`
# Update use foo's information
iamctl user update foo --nickname=foo2 --email=foo@qq.com --phone=1812883xxxx`)
updateUsageErrStr = fmt.Sprintf(
"expected '%s'.\nUSERNAME is required arguments for the update command",
updateUsageStr,
)
)
// NewUpdateOptions returns an initialized UpdateOptions instance.
func NewUpdateOptions(ioStreams genericclioptions.IOStreams) *UpdateOptions {
return &UpdateOptions{
IOStreams: ioStreams,
}
}
// NewCmdUpdate returns new initialized instance of update sub command.
func NewCmdUpdate(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
o := NewUpdateOptions(ioStreams)
cmd := &cobra.Command{
Use: updateUsageStr,
DisableFlagsInUseLine: true,
Aliases: []string{},
Short: "Update a user resource",
TraverseChildren: true,
Long: updateLong,
Example: updateExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate(cmd, args))
cmdutil.CheckErr(o.Run(args))
},
SuggestFor: []string{},
}
cmd.Flags().StringVar(&o.Nickname, "nickname", o.Nickname, "The nickname of the user.")
cmd.Flags().StringVar(&o.Email, "email", o.Email, "The email of the user.")
cmd.Flags().StringVar(&o.Phone, "phone", o.Phone, "The phone number of the user.")
return cmd
}
// Complete completes all the required options.
func (o *UpdateOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
var err error
if len(args) == 0 {
return cmdutil.UsageErrorf(cmd, updateUsageErrStr)
}
o.Name = args[0]
o.iamclient, err = f.IAMClient()
if err != nil {
return err
}
return nil
}
// Validate makes sure there is no discrepency in command options.
func (o *UpdateOptions) Validate(cmd *cobra.Command, args []string) error {
return nil
}
// Run executes an update subcommand using the specified options.
func (o *UpdateOptions) Run(args []string) error {
user, err := o.iamclient.APIV1().Users().Get(context.TODO(), o.Name, metav1.GetOptions{})
if err != nil {
return err
}
if o.Nickname != "" {
user.Nickname = o.Nickname
}
if o.Email != "" {
user.Email = o.Email
}
if o.Phone != "" {
user.Phone = o.Phone
}
ret, err := o.iamclient.APIV1().Users().Update(context.TODO(), user, metav1.UpdateOptions{})
if err != nil {
return err
}
fmt.Fprintf(o.Out, "user/%s updated\n", ret.Name)
return nil
}

@ -0,0 +1,43 @@
// Copyright © 2023 OpenIM. All rights reserved.
//
// 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 util
import (
"github.com/marmotedu/marmotedu-sdk-go/marmotedu/service/iam"
restclient "github.com/marmotedu/marmotedu-sdk-go/rest"
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
)
// Factory provides abstractions that allow the IAM command to be extended across multiple types
// of resources and different API sets.
// The rings are here for a reason. In order for composers to be able to provide alternative factory implementations
// they need to provide low level pieces of *certain* functions so that when the factory calls back into itself
// it uses the custom version of the function. Rather than try to enumerate everything that someone would want to
// override
// we split the factory into rings, where each ring can depend on methods in an earlier ring, but cannot depend
// upon peer methods in its own ring.
// TODO: make the functions interfaces
// TODO: pass the various interfaces on the factory directly into the command constructors (so the
// commands are decoupled from the factory).
type Factory interface {
genericclioptions.RESTClientGetter
// IAMClient gives you back an external iamclient
IAMClient() (*iam.IamClient, error)
// Returns a RESTClient for accessing IAM resources or an error.
RESTClient() (*restclient.RESTClient, error)
}

@ -0,0 +1,66 @@
// Copyright © 2023 OpenIM. All rights reserved.
//
// 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.
// this file contains factories with no other dependencies
package util
import (
"github.com/marmotedu/marmotedu-sdk-go/marmotedu/service/iam"
restclient "github.com/marmotedu/marmotedu-sdk-go/rest"
"github.com/marmotedu/marmotedu-sdk-go/tools/clientcmd"
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
)
type factoryImpl struct {
clientGetter genericclioptions.RESTClientGetter
}
func NewFactory(clientGetter genericclioptions.RESTClientGetter) Factory {
if clientGetter == nil {
panic("attempt to instantiate client_access_factory with nil clientGetter")
}
f := &factoryImpl{
clientGetter: clientGetter,
}
return f
}
func (f *factoryImpl) ToRESTConfig() (*restclient.Config, error) {
return f.clientGetter.ToRESTConfig()
}
func (f *factoryImpl) ToRawIAMConfigLoader() clientcmd.ClientConfig {
return f.clientGetter.ToRawIAMConfigLoader()
}
func (f *factoryImpl) IAMClient() (*iam.IamClient, error) {
clientConfig, err := f.ToRESTConfig()
if err != nil {
return nil, err
}
return iam.NewForConfig(clientConfig)
}
func (f *factoryImpl) RESTClient() (*restclient.RESTClient, error) {
clientConfig, err := f.ToRESTConfig()
if err != nil {
return nil, err
}
setIAMDefaults(clientConfig)
return restclient.RESTClientFor(clientConfig)
}

@ -0,0 +1,397 @@
// Copyright © 2023 OpenIM. All rights reserved.
//
// 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 util
import (
"bytes"
"fmt"
"io"
"net/http"
"net/url"
"os"
"strings"
"time"
"github.com/marmotedu/errors"
"github.com/marmotedu/marmotedu-sdk-go/marmotedu"
restclient "github.com/marmotedu/marmotedu-sdk-go/rest"
"github.com/olekukonko/tablewriter"
"github.com/parnurzeal/gorequest"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/marmotedu/iam/pkg/log"
)
const (
// DefaultErrorExitCode defines the default exit code.
DefaultErrorExitCode = 1
)
type debugError interface {
DebugError() (msg string, args []interface{})
}
var fatalErrHandler = fatal
// BehaviorOnFatal allows you to override the default behavior when a fatal
// error occurs, which is to call os.Exit(code). You can pass 'panic' as a function
// here if you prefer the panic() over os.Exit(1).
func BehaviorOnFatal(f func(string, int)) {
fatalErrHandler = f
}
// DefaultBehaviorOnFatal allows you to undo any previous override. Useful in
// tests.
func DefaultBehaviorOnFatal() {
fatalErrHandler = fatal
}
// fatal prints the message (if provided) and then exits.
func fatal(msg string, code int) {
if len(msg) > 0 {
// add newline if needed
if !strings.HasSuffix(msg, "\n") {
msg += "\n"
}
fmt.Fprint(os.Stderr, msg)
}
os.Exit(code)
}
// ErrExit may be passed to CheckError to instruct it to output nothing but exit with
// status code 1.
var ErrExit = fmt.Errorf("exit")
// CheckErr prints a user-friendly error to STDERR and exits with a non-zero
// exit code. Unrecognized errors will be printed with an "error: " prefix.
//
// This method is generic to the command in use and may be used by non-IAM
// commands.
func CheckErr(err error) {
checkErr(err, fatalErrHandler)
}
// CheckDiffErr prints a user-friendly error to STDERR and exits with a
// non-zero and non-one exit code. Unrecognized errors will be printed
// with an "error: " prefix.
//
// This method is meant specifically for `iamctl diff` and may be used
// by other commands.
func CheckDiffErr(err error) {
checkErr(err, func(msg string, code int) {
fatalErrHandler(msg, code+1)
})
}
// checkErr formats a given error as a string and calls the passed handleErr
// func with that string and an iamctl exit code.
func checkErr(err error, handleErr func(string, int)) {
// unwrap aggregates of 1
if agg, ok := err.(errors.Aggregate); ok && len(agg.Errors()) == 1 {
err = agg.Errors()[0]
}
if err == nil {
return
}
switch {
case err == ErrExit:
handleErr("", DefaultErrorExitCode)
default:
switch err := err.(type) {
case errors.Aggregate:
handleErr(MultipleErrors(``, err.Errors()), DefaultErrorExitCode)
default: // for any other error type
msg, ok := StandardErrorMessage(err)
if !ok {
msg = err.Error()
if !strings.HasPrefix(msg, "error: ") {
msg = fmt.Sprintf("error: %s", msg)
}
}
handleErr(msg, DefaultErrorExitCode)
}
}
}
// StandardErrorMessage translates common errors into a human readable message, or returns
// false if the error is not one of the recognized types. It may also log extended information to klog.
//
// This method is generic to the command in use and may be used by non-IAM
// commands.
func StandardErrorMessage(err error) (string, bool) {
if debugErr, ok := err.(debugError); ok {
log.Infof(debugErr.DebugError())
}
if t, ok := err.(*url.Error); ok {
log.Infof("Connection error: %s %s: %v", t.Op, t.URL, t.Err)
if strings.Contains(t.Err.Error(), "connection refused") {
host := t.URL
if server, err := url.Parse(t.URL); err == nil {
host = server.Host
}
return fmt.Sprintf(
"The connection to the server %s was refused - did you specify the right host or port?",
host,
), true
}
return fmt.Sprintf("Unable to connect to the server: %v", t.Err), true
}
return "", false
}
// MultilineError returns a string representing an error that splits sub errors into their own
// lines. The returned string will end with a newline.
func MultilineError(prefix string, err error) string {
if agg, ok := err.(errors.Aggregate); ok {
errs := errors.Flatten(agg).Errors()
buf := &bytes.Buffer{}
switch len(errs) {
case 0:
return fmt.Sprintf("%s%v\n", prefix, err)
case 1:
return fmt.Sprintf("%s%v\n", prefix, messageForError(errs[0]))
default:
fmt.Fprintln(buf, prefix)
for _, err := range errs {
fmt.Fprintf(buf, "* %v\n", messageForError(err))
}
return buf.String()
}
}
return fmt.Sprintf("%s%s\n", prefix, err)
}
// MultipleErrors returns a newline delimited string containing
// the prefix and referenced errors in standard form.
func MultipleErrors(prefix string, errs []error) string {
buf := &bytes.Buffer{}
for _, err := range errs {
fmt.Fprintf(buf, "%s%v\n", prefix, messageForError(err))
}
return buf.String()
}
// messageForError returns the string representing the error.
func messageForError(err error) string {
msg, ok := StandardErrorMessage(err)
if !ok {
msg = err.Error()
}
return msg
}
// UsageErrorf returns error with command path.
func UsageErrorf(cmd *cobra.Command, format string, args ...interface{}) error {
msg := fmt.Sprintf(format, args...)
return fmt.Errorf("%s\nSee '%s -h' for help and examples", msg, cmd.CommandPath())
}
// IsFilenameSliceEmpty checkes where filenames and directory are both zero value.
func IsFilenameSliceEmpty(filenames []string, directory string) bool {
return len(filenames) == 0 && directory == ""
}
// GetFlagString returns the value of the given flag.
func GetFlagString(cmd *cobra.Command, flag string) string {
s, err := cmd.Flags().GetString(flag)
if err != nil {
log.Fatalf("error accessing flag %s for command %s: %v", flag, cmd.Name(), err)
}
return s
}
// GetFlagStringSlice can be used to accept multiple argument with flag repetition (e.g. -f arg1,arg2 -f arg3 ...).
func GetFlagStringSlice(cmd *cobra.Command, flag string) []string {
s, err := cmd.Flags().GetStringSlice(flag)
if err != nil {
log.Fatalf("error accessing flag %s for command %s: %v", flag, cmd.Name(), err)
}
return s
}
// GetFlagStringArray can be used to accept multiple argument with flag repetition (e.g. -f arg1 -f arg2 ...).
func GetFlagStringArray(cmd *cobra.Command, flag string) []string {
s, err := cmd.Flags().GetStringArray(flag)
if err != nil {
log.Fatalf("error accessing flag %s for command %s: %v", flag, cmd.Name(), err)
}
return s
}
// GetFlagBool returns the value of the given flag.
func GetFlagBool(cmd *cobra.Command, flag string) bool {
b, err := cmd.Flags().GetBool(flag)
if err != nil {
log.Fatalf("error accessing flag %s for command %s: %v", flag, cmd.Name(), err)
}
return b
}
// GetFlagInt returns the value of the given flag.
func GetFlagInt(cmd *cobra.Command, flag string) int {
i, err := cmd.Flags().GetInt(flag)
if err != nil {
log.Fatalf("error accessing flag %s for command %s: %v", flag, cmd.Name(), err)
}
return i
}
// GetFlagInt32 returns the value of the given flag.
func GetFlagInt32(cmd *cobra.Command, flag string) int32 {
i, err := cmd.Flags().GetInt32(flag)
if err != nil {
log.Fatalf("error accessing flag %s for command %s: %v", flag, cmd.Name(), err)
}
return i
}
// GetFlagInt64 returns the value of the given flag.
func GetFlagInt64(cmd *cobra.Command, flag string) int64 {
i, err := cmd.Flags().GetInt64(flag)
if err != nil {
log.Fatalf("error accessing flag %s for command %s: %v", flag, cmd.Name(), err)
}
return i
}
// GetFlagDuration return the value of the given flag.
func GetFlagDuration(cmd *cobra.Command, flag string) time.Duration {
d, err := cmd.Flags().GetDuration(flag)
if err != nil {
log.Fatalf("error accessing flag %s for command %s: %v", flag, cmd.Name(), err)
}
return d
}
// AddCleanFlags add clean flags.
func AddCleanFlags(cmd *cobra.Command) {
cmd.Flags().StringP("user", "u", "", "Specify the user name.")
cmd.Flags().BoolP("erase", "c", false, "Erase the records from the db")
}
// ValidateOptions defines the validate options.
type ValidateOptions struct {
EnableValidation bool
}
// IsSiblingCommandExists receives a pointer to a cobra command and a target string.
// Returns true if the target string is found in the list of sibling commands.
func IsSiblingCommandExists(cmd *cobra.Command, targetCmdName string) bool {
for _, c := range cmd.Parent().Commands() {
if c.Name() == targetCmdName {
return true
}
}
return false
}
// DefaultSubCommandRun prints a command's help string to the specified output if no
// arguments (sub-commands) are provided, or a usage error otherwise.
func DefaultSubCommandRun(out io.Writer) func(c *cobra.Command, args []string) {
return func(c *cobra.Command, args []string) {
c.SetOutput(out)
RequireNoArguments(c, args)
c.Help()
CheckErr(ErrExit)
}
}
// RequireNoArguments exits with a usage error if extra arguments are provided.
func RequireNoArguments(c *cobra.Command, args []string) {
if len(args) > 0 {
CheckErr(UsageErrorf(c, "unknown command %q", strings.Join(args, " ")))
}
}
// ManualStrip is used for dropping comments from a YAML file.
func ManualStrip(file []byte) []byte {
stripped := []byte{}
lines := bytes.Split(file, []byte("\n"))
for i, line := range lines {
if bytes.HasPrefix(bytes.TrimSpace(line), []byte("#")) {
continue
}
stripped = append(stripped, line...)
if i < len(lines)-1 {
stripped = append(stripped, '\n')
}
}
return stripped
}
// Warning write warning message to io.Writer.
func Warning(cmdErr io.Writer, newGeneratorName, oldGeneratorName string) {
fmt.Fprintf(cmdErr, "WARNING: New generator %q specified, "+
"but it isn't available. "+
"Falling back to %q.\n",
newGeneratorName,
oldGeneratorName,
)
}
// CombineRequestErr combines the http response error and error in errs array.
func CombineRequestErr(resp gorequest.Response, body string, errs []error) error {
var e, sep string
if len(errs) > 0 {
for _, err := range errs {
e = sep + err.Error()
sep = "\n"
}
return errors.New(e)
}
if resp.StatusCode != http.StatusOK {
return errors.New(body)
}
return nil
}
func NewForConfigOrDie() *marmotedu.Clientset {
clientConfig := &restclient.Config{
Host: viper.GetString("server.address"),
BearerToken: viper.GetString("user.token"),
Username: viper.GetString("user.username"),
Password: viper.GetString("user.password"),
SecretID: viper.GetString("user.secret-id"),
SecretKey: viper.GetString("user.secret-key"),
Timeout: viper.GetDuration("server.timeout"),
MaxRetries: viper.GetInt("server.max-retries"),
RetryInterval: viper.GetDuration("server.retry-interval"),
}
return marmotedu.NewForConfigOrDie(clientConfig)
}
func TableWriterDefaultConfig(table *tablewriter.Table) *tablewriter.Table {
table.SetAutoWrapText(false)
table.SetAutoFormatHeaders(true)
table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
table.SetAlignment(tablewriter.ALIGN_LEFT)
table.SetCenterSeparator("")
table.SetColumnSeparator("")
table.SetRowSeparator("")
table.SetHeaderLine(false)
table.SetBorder(false)
table.SetTablePadding(" ") // pad with two space
table.SetNoWhiteSpace(true)
return table
}

@ -0,0 +1,141 @@
// Copyright © 2023 OpenIM. All rights reserved.
//
// 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 util
import (
"context"
"fmt"
"sync"
"github.com/marmotedu/marmotedu-sdk-go/rest"
"github.com/marmotedu/marmotedu-sdk-go/tools/clientcmd"
"github.com/openim-sigs/component-base/pkg/runtime"
"github.com/openim-sigs/component-base/pkg/scheme"
"github.com/openim-sigs/component-base/pkg/version"
"github.com/spf13/pflag"
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
)
const (
flagMatchBinaryVersion = "match-server-version"
)
// MatchVersionFlags is for setting the "match server version" function.
type MatchVersionFlags struct {
Delegate genericclioptions.RESTClientGetter
RequireMatchedServerVersion bool
checkServerVersion sync.Once
matchesServerVersionErr error
}
var _ genericclioptions.RESTClientGetter = &MatchVersionFlags{}
func (f *MatchVersionFlags) checkMatchingServerVersion() error {
f.checkServerVersion.Do(func() {
if !f.RequireMatchedServerVersion {
return
}
clientConfig, err := f.Delegate.ToRESTConfig()
if err != nil {
f.matchesServerVersionErr = err
return
}
setIAMDefaults(clientConfig)
restClient, err := rest.RESTClientFor(clientConfig)
if err != nil {
f.matchesServerVersionErr = err
return
}
var sVer *version.Info
if err := restClient.Get().AbsPath("/version").Do(context.TODO()).Into(&sVer); err != nil {
f.matchesServerVersionErr = err
return
}
clientVersion := version.Get()
// GitVersion includes GitCommit and GitTreeState, but best to be safe?
if clientVersion.GitVersion != sVer.GitVersion || clientVersion.GitCommit != sVer.GitCommit ||
clientVersion.GitTreeState != sVer.GitTreeState {
f.matchesServerVersionErr = fmt.Errorf(
"server version (%#v) differs from client version (%#v)",
sVer,
version.Get(),
)
}
})
return f.matchesServerVersionErr
}
// ToRESTConfig implements RESTClientGetter.
// Returns a REST client configuration based on a provided path
// to a .iamconfig file, loading rules, and config flag overrides.
// Expects the AddFlags method to have been called.
func (f *MatchVersionFlags) ToRESTConfig() (*rest.Config, error) {
if err := f.checkMatchingServerVersion(); err != nil {
return nil, err
}
clientConfig, err := f.Delegate.ToRESTConfig()
if err != nil {
return nil, err
}
// TODO we should not have to do this. It smacks of something going wrong.
setIAMDefaults(clientConfig)
return clientConfig, nil
}
func (f *MatchVersionFlags) ToRawIAMConfigLoader() clientcmd.ClientConfig {
return f.Delegate.ToRawIAMConfigLoader()
}
func (f *MatchVersionFlags) AddFlags(flags *pflag.FlagSet) {
flags.BoolVar(
&f.RequireMatchedServerVersion,
flagMatchBinaryVersion,
f.RequireMatchedServerVersion,
"Require server version to match client version",
)
}
func NewMatchVersionFlags(delegate genericclioptions.RESTClientGetter) *MatchVersionFlags {
return &MatchVersionFlags{
Delegate: delegate,
}
}
// setIAMDefaults sets default values on the provided client config for accessing the
// IAM API or returns an error if any of the defaults are impossible or invalid.
// TODO this isn't what we want. Each iamclient should be setting defaults as it sees fit.
func setIAMDefaults(config *rest.Config) error {
// TODO remove this hack. This is allowing the GetOptions to be serialized.
config.GroupVersion = &scheme.GroupVersion{Group: "iam.api", Version: "v1"}
if config.APIPath == "" {
config.APIPath = "/api"
}
if config.Negotiator == nil {
// This codec factory ensures the resources are not converted. Therefore, resources
// will not be round-tripped through internal versions. Defaulting does not happen
// on the client.
config.Negotiator = runtime.NewSimpleClientNegotiator()
}
return rest.SetIAMDefaults(config)
}

@ -0,0 +1,126 @@
// Copyright © 2023 OpenIM. All rights reserved.
//
// 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 validate validate the basic environment for iamctl to run.
package validate
import (
"fmt"
"net"
"net/url"
"github.com/fatih/color"
"github.com/olekukonko/tablewriter"
"github.com/spf13/cobra"
"github.com/spf13/viper"
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
)
// ValidateOptions is an options struct to support 'validate' sub command.
type ValidateOptions struct {
genericclioptions.IOStreams
}
// ValidateInfo defines the validate information.
type ValidateInfo struct {
ItemName string
Status string
Message string
}
var validateExample = templates.Examples(`
# Validate the basic environment for iamctl to run
iamctl validate`)
// NewValidateOptions returns an initialized ValidateOptions instance.
func NewValidateOptions(ioStreams genericclioptions.IOStreams) *ValidateOptions {
return &ValidateOptions{
IOStreams: ioStreams,
}
}
// NewCmdValidate returns new initialized instance of 'validate' sub command.
func NewCmdValidate(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
o := NewValidateOptions(ioStreams)
cmd := &cobra.Command{
Use: "validate",
DisableFlagsInUseLine: true,
Aliases: []string{},
Short: "Validate the basic environment for iamctl to run",
TraverseChildren: true,
Long: "Validate the basic environment for iamctl to run.",
Example: validateExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate(cmd, args))
cmdutil.CheckErr(o.Run(args))
},
SuggestFor: []string{},
}
return cmd
}
// Complete completes all the required options.
func (o *ValidateOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
return nil
}
// Validate makes sure there is no discrepency in command options.
func (o *ValidateOptions) Validate(cmd *cobra.Command, args []string) error {
return nil
}
// Run executes a validate sub command using the specified options.
func (o *ValidateOptions) Run(args []string) error {
data := [][]string{}
FAIL := color.RedString("FAIL")
PASS := color.GreenString("PASS")
validateInfo := ValidateInfo{}
// check if can access db
validateInfo.ItemName = "iam-apiserver"
target, err := url.Parse(viper.GetString("server.address"))
if err != nil {
return err
}
_, err = net.Dial("tcp", target.Host)
// defer client.Close()
if err != nil {
validateInfo.Status = FAIL
validateInfo.Message = fmt.Sprintf("%v", err)
} else {
validateInfo.Status = PASS
validateInfo.Message = ""
}
data = append(data, []string{validateInfo.ItemName, validateInfo.Status, validateInfo.Message})
table := tablewriter.NewWriter(o.Out)
table.SetAlignment(tablewriter.ALIGN_LEFT)
table.SetColWidth(iamctl.TableWidth)
table.SetHeader([]string{"ValidateItem", "Result", "Message"})
for _, v := range data {
table.Append(v)
}
table.Render()
return nil
}

@ -0,0 +1,166 @@
// Copyright © 2023 OpenIM. All rights reserved.
//
// 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 version print the client and server version information.
package version
import (
"context"
"errors"
"fmt"
"github.com/ghodss/yaml"
restclient "github.com/marmotedu/marmotedu-sdk-go/rest"
"github.com/openim-sigs/component-base/pkg/json"
"github.com/openim-sigs/component-base/pkg/version"
"github.com/spf13/cobra"
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
)
// Version is a struct for version information.
type Version struct {
ClientVersion *version.Info `json:"clientVersion,omitempty" yaml:"clientVersion,omitempty"`
ServerVersion *version.Info `json:"serverVersion,omitempty" yaml:"serverVersion,omitempty"`
}
var versionExample = templates.Examples(`
# Print the client and server versions for the current context
iamctl version`)
// Options is a struct to support version command.
type Options struct {
ClientOnly bool
Short bool
Output string
client *restclient.RESTClient
genericclioptions.IOStreams
}
// NewOptions returns initialized Options.
func NewOptions(ioStreams genericclioptions.IOStreams) *Options {
return &Options{
IOStreams: ioStreams,
}
}
// NewCmdVersion returns a cobra command for fetching versions.
func NewCmdVersion(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
o := NewOptions(ioStreams)
cmd := &cobra.Command{
Use: "version",
Short: "Print the client and server version information",
Long: "Print the client and server version information for the current context",
Example: versionExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd))
cmdutil.CheckErr(o.Validate())
cmdutil.CheckErr(o.Run())
},
}
cmd.Flags().BoolVar(
&o.ClientOnly,
"client",
o.ClientOnly,
"If true, shows client version only (no server required).",
)
cmd.Flags().BoolVar(&o.Short, "short", o.Short, "If true, print just the version number.")
cmd.Flags().StringVarP(&o.Output, "output", "o", o.Output, "One of 'yaml' or 'json'.")
return cmd
}
// Complete completes all the required options.
func (o *Options) Complete(f cmdutil.Factory, cmd *cobra.Command) error {
var err error
if o.ClientOnly {
return nil
}
o.client, err = f.RESTClient()
if err != nil {
return err
}
return nil
}
// Validate validates the provided options.
func (o *Options) Validate() error {
if o.Output != "" && o.Output != "yaml" && o.Output != "json" {
return errors.New(`--output must be 'yaml' or 'json'`)
}
return nil
}
// Run executes version command.
func (o *Options) Run() error {
var (
serverVersion *version.Info
serverErr error
versionInfo Version
)
clientVersion := version.Get()
versionInfo.ClientVersion = &clientVersion
if !o.ClientOnly && o.client != nil {
// Always request fresh data from the server
if err := o.client.Get().AbsPath("/version").Do(context.TODO()).Into(&serverVersion); err != nil {
return err
}
versionInfo.ServerVersion = serverVersion
}
switch o.Output {
case "":
if o.Short {
fmt.Fprintf(o.Out, "Client Version: %s\n", clientVersion.GitVersion)
if serverVersion != nil {
fmt.Fprintf(o.Out, "Server Version: %s\n", serverVersion.GitVersion)
}
} else {
fmt.Fprintf(o.Out, "Client Version: %s\n", fmt.Sprintf("%#v", clientVersion))
if serverVersion != nil {
fmt.Fprintf(o.Out, "Server Version: %s\n", fmt.Sprintf("%#v", *serverVersion))
}
}
case "yaml":
marshaled, err := yaml.Marshal(&versionInfo)
if err != nil {
return err
}
fmt.Fprintln(o.Out, string(marshaled))
case "json":
marshaled, err := json.MarshalIndent(&versionInfo, "", " ")
if err != nil {
return err
}
fmt.Fprintln(o.Out, string(marshaled))
default:
// There is a bug in the program if we hit this case.
// However, we follow a policy of never panicking.
return fmt.Errorf("VersionOptions were not validated: --output=%q should have been rejected", o.Output)
}
return serverErr
}

@ -1,3 +1,17 @@
# Copyright © 2023 OpenIM. All rights reserved.
#
# 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.
# 使用官方Go镜像作为基础镜像
FROM golang:1.21 AS build-env
ENV CGO_ENABLED=0

@ -1,3 +1,17 @@
// Copyright © 2023 OpenIM. All rights reserved.
//
// 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 main
import (

@ -1,3 +1,17 @@
// Copyright © 2023 OpenIM. All rights reserved.
//
// 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 main
import (

Loading…
Cancel
Save