Support X-Ai-Routing-Id to pin ai_routing, treat empty model_pattern as non-wildcard, and wire MCP agent to aiproxy credentials instead of direct LLM api_key.
Fetch the referenced LLM with the caller session before filling access URL fields.
Co-authored-by: Qiu Jian <qiujian@yunionyun.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Require role_id and reuse the role add-policy checks when creating a binding.
Co-authored-by: Qiu Jian <qiujian@yunionyun.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Keep the caller session when authorizing assume and reuse join-project role checks.
Co-authored-by: Qiu Jian <qiujian@yunionyun.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
When admin or scope query parameters do not map to a concrete view, use the resource's own scope and keep owner filters applied.
Co-authored-by: Qiu Jian <qiujian@yunionyun.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Allow force restart when instances are failed, protect restarting status
during sync, refresh GPU memory from deployment, and tolerate crash-loop
while waiting for service ready.
Align the csrf resource handler with other API resource handlers by requiring a token and forwarding with the caller's own session.
Co-authored-by: Qiu Jian <qiujian@yunionyun.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
The sftp list/download/upload endpoints were not protected by
authentication, only by the UUID4 session id. Anyone who learned the
session id (it appears in URLs and logs) could list, download and
upload files of the VM over the victim's active SSH/SFTP channel.
Wrap all three endpoints with auth.Authenticate and record the owner
of each sftp session at registration, so only the user who opened the
session can use it.
Co-authored-by: Qiu Jian <qiujian@yunionyun.com>
Co-authored-by: Claude <noreply@anthropic.com>
The ssh console endpoint accepted a raw ip address in the URL path
and dialed it directly with user provided credentials, so any
authenticated user could use the webconsole service as an internal
SSH proxy, port scanner and credential brute-forcer.
Resolve the ip against hosts (access_ip) and servers (ip_addr)
visible to the user's session and require it to match one of them,
rejecting all other addresses.
Co-authored-by: Qiu Jian <qiujian@yunionyun.com>
Co-authored-by: Claude <noreply@anthropic.com>
- Reject signed requests older or newer than 15 minutes (x-amz-date
for v4, Date for v2), so intercepted valid requests can no longer
be replayed indefinitely
- Decode the object key exactly once: r.URL.Path is already decoded
by net/http and is the form the signature covers, decoding again
let a signature for one key be replayed against another
(e.g. %252e%252e%252f resolving to ../)
- Add unit tests for both behaviors
Co-authored-by: Qiu Jian <qiujian@yunionyun.com>
Co-authored-by: Claude <noreply@anthropic.com>
The k8s shell/log endpoints fetched the target cluster kubeconfig
with the admin session, bypassing RBAC and owner scope checks, so any
authenticated user could exec into pods and read logs of any
registered cluster.
Fetch the kubeconfig with the user's own session instead, so policy
checks and owner filtering apply, and command records are attributed
to the real user.
Co-authored-by: Qiu Jian <qiujian@yunionyun.com>
Co-authored-by: Claude <noreply@anthropic.com>
The AES key encrypting all console session tokens was derived from
rand.Float32 with only about 24 bits of entropy, so it could be
recovered by brute force from a single known (session id, token)
pair and then be used to forge access tokens of arbitrary sessions.
Generate the key from 32 crypto/rand bytes instead (about 192 bits
of effective entropy). Sessions live in process memory only, so a
per-process random key is sufficient.
Co-authored-by: Qiu Jian <qiujian@yunionyun.com>
Co-authored-by: Claude <noreply@anthropic.com>
RemoteRDPConsoleInfo.GetId() always returned an empty string, so all
RDP sessions were stored under the same key in the session manager:
any valid RDP access token resolved to whatever RDP session was stored
last, letting a user take over another user's console session
(host/port/username/password).
Give every RDP session a random UUID id and validate in
SSessionManager.Get that the presented access token is exactly the
one issued for the session, so sessions can not be reached by any
other token.
Co-authored-by: Qiu Jian <qiujian@yunionyun.com>
Co-authored-by: Claude <noreply@anthropic.com>
With cors_hosts unset (the default), the CORS middleware allowed all
origins and echoed the request origin together with
Access-Control-Allow-Credentials, so any website could make
authenticated cross-origin requests against the API with the user's
cookies.
Credentials are now only honored for explicitly listed origins:
with an empty or wildcard origin allowlist the middleware responds
with Access-Control-Allow-Origin: * and no credentials header.
Co-authored-by: Qiu Jian <qiujian@yunionyun.com>
Co-authored-by: Claude <noreply@anthropic.com>
- Generate verification codes with crypto/rand instead of the
predictable time-seeded math/rand
- Count failed verification attempts and invalidate the code after
verify_max_attempts (default 5) failures, closing the unlimited
online brute force of the 6-digit code at the login endpoint
- Delete the verification record after a successful check so codes
are single use and can not be replayed within the validity window
- Add unit tests for code generation
Co-authored-by: Qiu Jian <qiujian@yunionyun.com>
Co-authored-by: Claude <noreply@anthropic.com>
The nbd export endpoints interpolated the request disk_id into
shell commands (sh -c with the joined qemu-nbd command line, and
ps|grep for the process check), so a crafted disk_id could execute
arbitrary commands as root on the host image service.
- Require the disk_id to be a plain UUID in both export and close
endpoints
- Run qemu-nbd with argv instead of sh -c
- Check the export process via its pid file and kill -0 instead of
shell pipelines
- Add unit tests for the validation and the process check
Co-authored-by: Qiu Jian <qiujian@yunionyun.com>
Co-authored-by: Claude <noreply@anthropic.com>
Full session tokens were written to logs in policy.go, mcclient auth,
oidc handler and the webconsole session manager; guest root passwords
were logged by the linux fsdriver and the nbdkit mount command; MCP
tool arguments and full JSON-RPC bodies (which may contain
server-reset-password passwords) were logged by the llm agent.
Truncate tokens in log messages, drop or mask password values and
log only the tool/method name for MCP calls.
Co-authored-by: Qiu Jian <qiujian@yunionyun.com>
Co-authored-by: Claude <noreply@anthropic.com>
server-monitor sends arbitrary QMP/HMP commands (pmemsave, migrate,
etc.) to a running guest, which exceeds the semantics of monitoring.
As an MCP tool it could be triggered without human awareness through
LLM prompt injection or the tool-request endpoint.
Remove the mcp-desc registration tag from ServerMonitorOptions so
climcgen no longer registers the tool, drop its mention from the MCP
instructions, and add a regression test asserting it is not exposed.
The climc command and the permission-gated region API are unchanged.
Co-authored-by: Qiu Jian <qiujian@yunionyun.com>
Co-authored-by: Claude <noreply@anthropic.com>
- Quote the heredoc delimiter when writing deploy params to the
deployer guest, so deploy content, passwords and other
user-supplied values are written literally and shell expansions
($(...), backticks, $VAR) inside them are not evaluated by the
remote shell
- Escape JSON passed via --deploy-params as a single POSIX shell
word, preventing single quotes in user data from breaking out of
the argument
- Add unit tests covering injection payloads
Co-authored-by: Qiu Jian <qiujian@yunionyun.com>
Co-authored-by: Claude <noreply@anthropic.com>
- Run ssh via exec.Command argv instead of "bash -c" string
interpolation, so user-supplied fields can no longer escape into
local shell commands on the webconsole server
- Quote every interpolated value (env, command, args) as a POSIX
shell word, so they stay literal data on the remote shell
- Validate username charset and limit target_ip to climc pod or container
- Add unit tests covering injection payloads
Co-authored-by: Qiu Jian <qiujian@yunionyun.com>
Co-authored-by: Claude <noreply@anthropic.com>
* feat(aiproxy): expose catalog context window on AI models
Populate known 1M+ context windows in catalog seed data and return
context_window in AI model list/detail API responses.
* feat(aiproxy): add instance test-connectivity and set-models actions
Add provider instance test-connectivity using stored ai_key secrets,
and set-models to import selected catalog models while skipping
existing model_key rows. Register corresponding climc perform commands.
Only call tryUnmanageInterface when the slave interface has IPv4 or IPv6
addresses configured, avoiding unnecessary NetworkManager unmanage on
address-less interfaces during bridge setup.
* fix(baremetal): add AllowFallback guard for OEM profile LAN channel authority
OEM/model profiles are authoritative allowlists; their LanChannels must not be overridden by the fallback 1..11 scan. Add AllowFallback bool to LanConfigSelectionOptions, wrapping the fallback scan in a conditional. When false (default), only preferred and persisted channels are probed.
* fix(baremetal): set AllowFallback on ipmiprobe and manager callers
manager.checkIpmiInfo: AllowFallback=false (profile always present). ipmiprobe.doRawIpmiProbe: AllowFallback=profile==nil (fallback only when no profile exists).
* fix(baremetal): set AllowFallback=false in configIPMISetting, cleanup bm_register
configIPMISetting: AllowFallback=false (profile always present). bm_register.updateIpmiInfo: replace profile-based channel scan with GetLanConfig on the registered channel directly.
---------
Co-authored-by: Bo Xu <i@186526.xyz>
Handle Ed25519 public and private keys in the AES-based encryption path.
Add round-trip coverage for OpenSSH and PKCS#8 private keys.
Co-authored-by: hechang <117586514+Haibersut@users.noreply.github.com>
Emit output_item.done, reasoning_summary_part.done, function_call_arguments.done,
and related finalize events for text, reasoning, and tool calls before the
response.completed event to match the Responses API stream contract.
* feat(aiproxy): support streaming responses for visual messages
* refactor(aiproxy): share API log helpers and support input/output token usage
Extract common chatlog record lifecycle across proxy handlers, and fall back
to Responses/Anthropic usage field aliases when filling token counts.
Promote visual_provider_id/visual_model_key to dedicated ai_model columns,
force non-stream upstream orchestration for visual paths, and synthesize SSE
chunks for streaming Responses requests that include images.
* refactor(aiproxy): rename chat log config to API log with S3 fields
* feat(aiproxy): add visual extension for Responses image tools
Wire visual model catalog, orchestration, and OpenAI Responses compat so chat/Codex can invoke image generation and editing tools.
Add /v1/messages handler with Anthropic-to-OpenAI translation, upstream
failover, and probe endpoints. Replace shell-based functional test scripts
with pkg/aiproxy/ft and climc test commands; consolidate documentation.
Add ModelScope hub integration, download driver, and instant model import
flow parallel to HuggingFace. Extract shared HuggingFace download helpers
and reuse them across vLLM/SGLang container drivers.
On service startup, find LLM instances without llm_deployment_id and
create a matching SLLMDeployment, then link the instance. Idempotent
across restarts; skipped on slave nodes.
Add auto_register_aiproxy on llm_deployment creation, background sync task,
and register/unregister CLI actions. Link aiproxy providers, routings, and
models to llm replicas via llm_id and llm_deployment_id fields.
Unify spelling, grammar, and semantics of API error strings without changing error types or business logic.
Co-authored-by: Cursor <cursoragent@cursor.com>
* feat(llm): hostpath support in deployment
* fix(llm): status error in deployment & sku deleted by deployment accidentally
---------
Co-authored-by: cwz <cwz_eikoh@163.com>
When using S3/MinIO backend, the progress callback in AcquireImage was
called synchronously on every 4KB read chunk (via StreamPipe2). Each
call invokes UpdateServerProgress which makes a blocking HTTP PUT to the
compute API (~50ms), capping download speed at ~0.65 Mbps regardless of
available bandwidth.
Fix: throttle the callback to fire at most once every 5 seconds and make
the HTTP call asynchronous (goroutine), matching the behavior of v3.11.x
which used a 1-second ticker with async callbacks.
Fixes#24897
Co-authored-by: Mrliuch <liu15094534492@gmail.com>
When multiple NVIDIA GPUs were attached to a container, each device was
passed individually to GetContainerExtraConfigures, producing one
NVIDIA_VISIBLE_DEVICES env per GPU. Duplicate env keys are collapsed by
the runtime to the last value, so the container only saw one GPU even
though the spec listed all of them.
Group RestDevs by DeviceType and invoke GetContainerExtraConfigures
once per type so all GPU UUIDs are joined into a single
NVIDIA_VISIBLE_DEVICES value.
Add a generic `feature-config <feature> <on|off>` command alongside the
existing per-feature `feature-config-<name>` commands. A `--force` flag
skips the known-features validation so new features can be toggled
without a code change. Also adds "ai" to the known feature list.
* feat(llm): support cursor
* feat(llm): support import from huggingface
* fix(llm): direction error
* feat(llm): support instant model for comfyui
* feat(llm): support multi file select when save-instant-model
---------
Co-authored-by: cwz <cwz_eikoh@163.com>
- Return shmPath instead of empty string when mountpoint is already
mounted, so callers get the correct path
- Remove incorrect +1 offset in memory comparison and size formatting
during LLM restart config change
When ManualConfig is enabled, inject OPENCLAW_MANUAL_CONFIG=1 env var
and skip provider/channel credential injection, allowing users to
configure providers and channels themselves inside the desktop.
Move common desktop/webtop container envs, base spec, rootfs, and login-info parsing
into llm_container helpers and reuse in openclaw and hermes-agent drivers.
- Wait for disk/desktop status after resize via backup worker
- Centralize volume SizeMB update with resize action log
- Expose vCPU/memory fields in LLM customize columns
silent_period is stored as Frequency on the notification, not on the
alert itself. PostUpdate only called UpdateNotification (which recreates
notifications with the new silent_period) when Channel was non-empty.
When channel was an empty array, the silent_period update was silently
skipped. Add an else-if branch to directly update the existing
notifications' Frequency when silent_period changes but channel is empty.
Add shellQuote helper that safely escapes single quotes and apply it
consistently across tarGzDir and tarHostDir to prevent shell injection
via crafted paths, directory names, or exclude patterns.
* feat(region, climc): Add kickstart parameters and metadata
- Add kickstart parameters and metadata support
- Add kickstart CLI options and management commands
- Set kickstart metadata in PostCreate
- Add related kickstart metadata fields
* feat(host): Implement automated VM installation
- Mounts the installation ISO to a unique temp path, reusing existing mounts.
- Extracts kernel (`vmlinuz`, `initrd`) to configure QEMU boot.
- Injects kernel parameters to trigger the autoinstall.
- Add kickstart configuration validation.
Modified `guestLauncher` script to handle kernel parameters with spaces
that were previously truncated by the Python script.
* feat(host): Implement Kickstart monitoring
- Automatically updates the VM status upon
receiving `KICKSTART_SUCCESS` or `KICKSTART_FAILED` signals.
- On success, cleans up temporary mount points
and restarts the server via the Region API.
* feat(region): add kickstart type metadata
Adds the `VM_METADATA_KICKSTART_TYPE` to distinguish between
kickstart configurations passed by URL or by direct content.
* feat(host): Handle kickstart installing and timeout
- Set status to 'installing' upon successful kickstart boot
- Handle KICKSTART_INSTALLING serial message for status updates
- Add timeout handling for kickstart status monitoring
* feat(host): provide Kickstart config via content
Embed the provided config content as a file in a
labeled ISO (mkisofs), then mount the ISO during
the Kickstart installation to supply the config.
* refactor(host): Enhance the kickstart installation
- Integrates kickstart status directly into the VM status, replacing metadata.
- Unifies locations for kickstart logs and temporary files.
- Uses regex for more robust status matching.
- Moves helper functions to `kickstart_helper.go`.
- Fixes an incorrect kickstart configuration filename.
- Adjusts kickstart logging.
* feat(region, host): add kickstart support for openEuler
* refactor(host): extract kickstart cleanup function
* feat(region, climc): add kickstart-complete cli
Add this command to manually update the kickstart status and
reboot VM if needed. This is mainly used by users to manually
update the kickstart status when kickstart status detection is abnormal.
* refactor(region): reduce code duplication in Kickstart validation
- unify two validateKickstartConfig functions
- extract determineKickstartType function for type determination
* fix(region, host): fix kickstart auto-reboot
- Adjusted the timing of setting the status to
`kickstart_pending` to avoid incorrect reboot checks
- Skip setting kickstart if the status is already
`kickstart_completed` to prevent unnecessary reboots
- Added cleanup of kickstart files upon VM deletion
* feat(host): download kickstart config from URL
- download content when passing config via URL
- unified boot parameters
* feat(host): copy kickstart kernel files
* feat(host): ensure kickstart completion signal in config
---------
Co-authored-by: happygame <happygame1024@gmail.com>
* feat(llm): init vllm as llm_container
* feat(vllm): fix save-instant-model for vllm
* feat(llm): support quick-model for vllm
* feat(vllm): support preferred-model in llm
* fix(llm): add ValidateSpec in llm_container_driver
* feat(vllm): auto-set vllm params
* support comfyui and openclaw
feat(region,host): support env credential of container
feat(llm): openclaw url
* fix(region): delete server without host_id
* feat(llm): llm spec
Pass cObjs of type *SCommonAlert to SCommonAlertManager.FetchCustomizeColumns
instead of *SNodeAlert so the parent correctly fetches common alert columns.
- climc: add VlanId to host-add-netif, host-remove-netif, host-enable-netif,
host-disable-netif
- region: set bn.VlanId from netif.VlanId when attaching to network
- mcclient: include Vlan_ID in baremetalnetworks list columns
* fix(llm): some mistakes
* fix(llm): use CmpId instead of SvrId
* fix(llm): instant_model use id instead of modelId
---------
Co-authored-by: cwz <cwz_eikoh@163.com>
* fix(llm): mcp-agent apiKey encrypt
* fix(llm): error-handle when instant-model import
* feature(llm): add get-available-network for llm
* feature(llm): support history for mcp-agent-chat
* fix(llm): llm create with net instead of networktype & networkid
* fix(llm): delete modelName in llmSku & fix some bugs
// 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.
packageaiproxy
import(
"yunion.io/x/onecloud/pkg/aiproxy/ft"
"yunion.io/x/onecloud/pkg/mcclient"
)
funcinit(){
R(&ft.ProviderCreateOptions{},"ai-test-provider-create","Create custom ai_provider and verify",func(s*mcclient.ClientSession,args*ft.ProviderCreateOptions)error{
R(&HostEnableNetIfOptions{},"host-enable-netif","Enable a network interface for a host",func(s*mcclient.ClientSession,args*HostEnableNetIfOptions)error{
Networkinterfacestring`help:"ID or Name of Server"`
Networkinterfacestring`help:"ID or Name of Networkinterface"`
Networkstring`help:"Network ID or name"`
}
R(&NetworkinterfaceNetworkListOptions{},"networkinterface-network-list","List server network pairs",func(s*mcclient.ClientSession,args*NetworkinterfaceNetworkListOptions)error{
R(&NetworkCreate3Options{},"network-create3","Create a dual-stack virtual network",func(s*mcclient.ClientSession,args*NetworkCreate3Options)error{
params,err:=args.Params()
iferr!=nil{
returnerr
}
net,err:=modules.Networks.Create(s,params)
iferr!=nil{
returnerr
}
printObject(net)
returnnil
})
typeNetworkSplitOptionsstruct{
NETWORKstring`help:"ID or name of network to split"`
IPstring`help:"Start ip of the split network"`
@@ -196,7 +212,7 @@ func init() {
typeNetworkChangeOwnerOptionsstruct{
IDstring`help:"Network to change owner" json:"-"`
PROJECTstring`help:"Project ID or change" json:"tenant"`
PROJECTstring`help:"Project ID or name" json:"tenant"`
}
R(&NetworkChangeOwnerOptions{},"network-change-owner","Change owner project of a network",func(s*mcclient.ClientSession,args*NetworkChangeOwnerOptions)error{
Durationstring`help:"reservation duration, e.g. 1I, 1H, 2M"`
Durationstring`help:"reservation duration, e.g. 1D, 1H, 2M"`
Statusstring`help:"ip status"`
}
R(&NetworkReserveIPOptions{},"network-reserve-ip","Reserve an IP address from pool",func(s*mcclient.ClientSession,args*NetworkReserveIPOptions)error{
@@ -49,7 +49,7 @@ func init() {
})
typeNetworkReleaseReservedIPOptionsstruct{
NETWORKstring`help:"IP or name of network"`
NETWORKstring`help:"ID or name of network"`
IPstring`help:"IP to release"`
}
R(&NetworkReleaseReservedIPOptions{},"network-release-reserved-ip","Release a reserved IP into pool",func(s*mcclient.ClientSession,args*NetworkReleaseReservedIPOptions)error{
Driverstring`help:"Driver of vDisk" choices:"virtio|ide|sata|scsi|pvscsi"`
Cachestring`help:"Cache mode of vDisk" choices:"writethrough|none|writeback|directsync"`
Aiostring`help:"Asynchronous IO mode of vDisk" choices:"native|threads"`
Indexint64`help:"Index of vDisk" default:"-1"`
}
R(&ServerDiskUpdateOptions{},"server-disk-update","Update details of a virtual disk of a virtual server",func(s*mcclient.ClientSession,args*ServerDiskUpdateOptions)error{
R(&ServerAttachDiskOptions{},"server-attach-disk","Attach an existing virtual disks to a virtual server",func(s*mcclient.ClientSession,args*ServerAttachDiskOptions)error{
DeleteDiskbool`help:"Delete disk if the disk not has flag of auto_delete when detached"`
DeleteDiskbool`help:"Delete the disk after detach (ignored if disk has auto_delete)" mcp:"true"`
}
R(&ServerDetachDiskOptions{},"server-detach-disk","Detach a disk from a virtual server",func(s*mcclient.ClientSession,args*ServerDetachDiskOptions)error{
DEVICEstring`help:"ID of isolated device to attach"`
GpuTypestring`help:"Type of GPU to attach to" choices:"HPC|VGA"`
}
R(&ServerAttachDeviceOptions{},"server-attach-isolated-device","Attach an existing isolated device to a virtual server",func(s*mcclient.ClientSession,args*ServerAttachDeviceOptions)error{
DEVICEstring`help:"ID of isolated device to attach"`
SERVERstring`help:"ID or name of server"`
DEVICEstring`help:"ID of isolated device to detach"`
INDEXint`help:"Index of isolated device to detach"`
IsForcebool`help:"Force detach isolated device"`
}
R(&ServerDetachDeviceOptions{},"server-detach-isolated-device","Detach a isolated device from a virtual server",func(s*mcclient.ClientSession,args*ServerDetachDeviceOptions)error{
Driverstring`help:"Driver model of vNIC" choices:"virtio|e1000|vmxnet3|rtl8139"`
Indexint64`help:"Index of NIC" default:"-1"`
@@ -138,7 +144,10 @@ func init() {
typeServerNetworkBWOptionsstruct{
SERVERstring`help:"ID or Name of server"`
MACORIPstring`help:"IP, Mac, or Index of NIC"`
BWint64`help:"Bandwidth in Mbps"`
BWint64`help:"Bandwidth in Mbps"`
Txint64`help:"Tx bandwidth in Mbps"`
Rxint64`help:"Rx bandwidth in Mbps"`
}
R(&ServerNetworkBWOptions{},"server-change-bandwidth","Change server network bandwidth in Mbps",func(s*mcclient.ClientSession,args*ServerNetworkBWOptions)error{
Reservebool`help:"Put the release IP address into reserved address pool"`
Forcebool`help:"detach server network by force"`
}
R(&ServerDetachNetworkOptions{},"server-detach-network","Detach the virtual network fron a virtual server",func(s*mcclient.ClientSession,args*ServerDetachNetworkOptions)error{
R(&ServerDetachNetworkOptions{},"server-detach-network","Detach the virtual network from a virtual server",func(s*mcclient.ClientSession,args*ServerDetachNetworkOptions)error{
R(&ServerDiskSnapshotOptions{},"server-disk-create-snapshot","Task server disk snapshot",func(s*mcclient.ClientSession,args*ServerDiskSnapshotOptions)error{
DRIVERstring`help:"driver of idp to query" json:"driver" choices:"oidc|oauth2|saml|cas"`
TEMPLATEstring`help:"template of idp to query" json:"template"`
}
R(&IdentityProviderGetPropertyAttributeNamesOptions{},"idp-attribute-names","Get property attribute names of a idp",func(s*mcclient.ClientSession,args*IdentityProviderGetPropertyAttributeNamesOptions)error{
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.