mirror of
https://github.com/Sea-Haven-Industries/open-swe.git
synced 2026-09-30 10:23:14 +00:00
* feat: open Slack-triggered PRs as the triggering user Route the Slack per-user GitHub token through the dashboard OAuth store (the backend the self-service link prompt populates) and block runs that lack a valid user token, prompting the user to (re-)link. Per-user OAuth now wins over bot-token-only mode for mapped Slack/dashboard users. Flip commit/PR authorship across all sources: the triggering user is the commit author (via repo-local git identity using their resolvable GitHub noreply email) and open-swe[bot] is the Co-authored-by collaborator. * fix: address PR review — shell-escape commit identity, fix token cache impersonation - Shell-escape the triggering user's name/email with shlex.quote before embedding them in the repo-setup `git config` command, so a name like O'Connor (or a crafted one) can't break or inject into the command. - Stop consulting the shared thread-metadata token cache in _resolve_dashboard_user_token. Slack thread ids are shared across the conversation, so a cached token from a prior triggering user could be returned for the current github_login. Always resolve by login from the dashboard OAuth store instead. * feat: dashboard self-service user mapping + UI cleanup - Add session-scoped GET/PUT /dashboard/api/my-mapping so users can set their own work email / Slack member ID (keyed by their GitHub login, source=self). - Slack account-link prompt now redirects to Profile Settings after auth. - Rename "My Settings" -> "Profile Settings" and "Cloud Agents" -> "Open SWE Agent"; remove the Integrations tab/section (folded out, low value for now) and redirect /integrations to Profile Settings. - Add a "User mapping" section to Profile Settings (work email used by Slack and Linear, optional Slack member ID). - Make dashboard auth cookies scheme-aware: Secure;SameSite=None over HTTPS, non-Secure;SameSite=Lax over http://localhost so local login works. * feat: self-service Slack account linking via Sign in with Slack (OIDC) Replace the spoofable manual work-email/Slack-ID form with a verified "Sign in with Slack" flow so a logged-in GitHub user can only ever link their own Slack identity. - New agent/dashboard/slack_oauth.py: OIDC authorize URL, code exchange, userInfo identity parse, optional workspace gate, configured check. - routes.py: session-gated GET /slack/login and /slack/callback that upsert the mapping from Slack-verified user_id + email (source=slack_oauth). Remove the spoofable PUT /my-mapping; expose slack_oauth_enabled on /me. - UI: drop the editable inputs; add a Connect Slack button + status to the User mapping section. Admin-managed mappings are unaffected and still resolve at trigger time.
88 lines
2.8 KiB
TypeScript
88 lines
2.8 KiB
TypeScript
import { Link } from "@tanstack/react-router";
|
|
import {
|
|
IoArrowBackOutline,
|
|
IoCloudOutline,
|
|
IoGitPullRequestOutline,
|
|
IoOptionsOutline,
|
|
IoSettingsOutline,
|
|
} from "react-icons/io5";
|
|
import type { ComponentType, SVGProps } from "react";
|
|
|
|
import type { SessionUser } from "@/lib/api";
|
|
import { SidebarUserMenu } from "@/components/SidebarUserMenu";
|
|
import {
|
|
SidebarCollapseButton,
|
|
SidebarFrame,
|
|
useSidebarLayout,
|
|
} from "@/components/sidebar-layout";
|
|
import { cn } from "@/lib/utils";
|
|
|
|
type IconType = ComponentType<SVGProps<SVGSVGElement>>;
|
|
|
|
interface NavItem {
|
|
to: string;
|
|
label: string;
|
|
icon: IconType;
|
|
adminOnly?: boolean;
|
|
}
|
|
|
|
const NAV: Array<NavItem> = [
|
|
{ to: "/my-settings", label: "Profile Settings", icon: IoOptionsOutline },
|
|
{ to: "/cloud-agents", label: "Open SWE Agent", icon: IoCloudOutline },
|
|
{ to: "/review", label: "Open SWE Review", icon: IoGitPullRequestOutline },
|
|
{ to: "/admin", label: "Admin", icon: IoSettingsOutline, adminOnly: true },
|
|
];
|
|
|
|
export function AppSidebar({ user }: { user: SessionUser }) {
|
|
const layout = useSidebarLayout();
|
|
return (
|
|
<SidebarFrame {...layout} className="border-r border-border bg-sidebar text-sidebar-foreground">
|
|
<div className="flex items-center justify-between px-4 pt-5 pb-4">
|
|
<Link
|
|
to="/my-settings"
|
|
className="flex items-center gap-2 font-heading text-sm font-medium tracking-tight"
|
|
>
|
|
<img src="/logo-mark.png" alt="" className="size-5" />
|
|
open-swe
|
|
</Link>
|
|
<SidebarCollapseButton onToggle={layout.toggle} />
|
|
</div>
|
|
|
|
<nav className="flex flex-1 flex-col gap-0.5 px-2">
|
|
<Link
|
|
to="/agents"
|
|
className={cn(
|
|
"flex items-center gap-2.5 rounded-md px-2.5 py-1.5 text-xs/relaxed text-muted-foreground transition-colors",
|
|
"hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
|
|
)}
|
|
>
|
|
<IoArrowBackOutline className="size-4" />
|
|
<span>Back to Agents</span>
|
|
</Link>
|
|
{NAV.filter((n) => !n.adminOnly || user.is_admin).map((item) => {
|
|
const Icon = item.icon;
|
|
return (
|
|
<Link
|
|
key={item.to}
|
|
to={item.to}
|
|
className={cn(
|
|
"flex items-center gap-2.5 rounded-md px-2.5 py-1.5 text-xs/relaxed text-muted-foreground transition-colors",
|
|
"hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
|
|
)}
|
|
activeProps={{
|
|
className: "bg-sidebar-accent text-sidebar-accent-foreground font-medium",
|
|
}}
|
|
>
|
|
<Icon className="size-4" />
|
|
<span>{item.label}</span>
|
|
</Link>
|
|
);
|
|
})}
|
|
</nav>
|
|
|
|
<div className="p-2">
|
|
<SidebarUserMenu user={user} />
|
|
</div>
|
|
</SidebarFrame>
|
|
);
|
|
}
|