8000 feat: delete API token in /logout API by AbhineetJain · Pull Request #1770 · coder/coder · GitHub
[go: up one dir, main page]

Skip to content

feat: delete API token in /logout API #1770

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 19 commits into from
May 27, 2022
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion cli/logout.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,16 @@ func logout() *cobra.Command {
Use: "logout",
Short: "Remove the local authenticated session",
RunE: func(cmd *cobra.Command, args []string) error {
client, err := createClient(cmd)
if err != nil {
return err
}

var isLoggedOut bool

config := createConfig(cmd)

_, err := cliui.Prompt(cmd, cliui.PromptOptions{
_, err = cliui.Prompt(cmd, cliui.PromptOptions{
Text: "Are you sure you want to logout?",
IsConfirm: true,
Default: "yes",
Expand Down Expand Up @@ -54,6 +59,11 @@ func logout() *cobra.Command {
return xerrors.Errorf("remove organization file: %w", err)
}

err = client.Logout(cmd.Context())
if err != nil {
return xerrors.Errorf("logout: %w", err)
}

// If the user was already logged out, we show them a different message
if isLoggedOut {
_, _ = fmt.Fprintf(cmd.OutOrStdout(), notLoggedInMessage+"\n")
Expand Down
15 changes: 15 additions & 0 deletions coderd/database/databasefake/databasefake.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,21 @@ func (q *fakeQuerier) GetAPIKeyByID(_ context.Context, id string) (database.APIK
return database.APIKey{}, sql.ErrNoRows
}

func (q *fakeQuerier) DeleteAPIKeyByID(_ context.Context, id string) error {
q.mutex.Lock()
defer q.mutex.Unlock()

for index, apiKey := range q.apiKeys {
if apiKey.ID != id {
continue
}
q.apiKeys[index] = q.apiKeys[len(q.apiKeys)-1]
q.apiKeys = q.apiKeys[:len(q.apiKeys)-1]
return nil
}
return sql.ErrNoRows
}

func (q *fakeQuerier) GetFileByHash(_ context.Context, hash string) (database.File, error) {
q.mutex.RLock()
defer q.mutex.RUnlock()
Expand Down
1 change: 1 addition & 0 deletions coderd/database/querier.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions coderd/database/queries.sql.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions coderd/database/queries/apikeys.sql
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,10 @@ SET
oauth_expiry = $6
WHERE
id = $1;

-- name: DeleteAPIKeyByID :exec
DELETE
FROM
api_keys
WHERE
id = $1;
43 changes: 27 additions & 16 deletions coderd/users.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ func (api *API) postFirstUser(rw http.ResponseWriter, r *http.Request) {
// TODO: @emyrk this currently happens outside the database tx used to create
// the user. Maybe I add this ability to grant roles in the createUser api
// and add some rbac bypass when calling api functions this way??
// Add the admin role to this first user
// Add the admin role to this first user.
_, err = api.Database.UpdateUserRoles(r.Context(), database.UpdateUserRolesParams{
GrantedRoles: []string{rbac.RoleAdmin(), rbac.RoleMember()},
ID: user.ID,
Expand All @@ -109,7 +109,7 @@ func (api *API) users(rw http.ResponseWriter, r *http.Request) {
statusFilter = r.URL.Query().Get("status")
)

// Reading all users across the site
// Reading all users across the site.
if !api.Authorize(rw, r, rbac.ActionRead, rbac.ResourceUser) {
return
}
Expand Down Expand Up @@ -162,7 +162,7 @@ func (api *API) users(rw http.ResponseWriter, r *http.Request) {

// Creates a new user.
func (api *API) postUser(rw http.ResponseWriter, r *http.Request) {
// Create the user on the site
// Create the user on the site.
if !api.Authorize(rw, r, rbac.ActionCreate, rbac.ResourceUser) {
return
}
Expand Down Expand Up @@ -408,11 +408,11 @@ func (api *API) userRoles(rw http.ResponseWriter, r *http.Request) {
return
}

// Only include ones we can read from RBAC
// Only include ones we can read from RBAC.
memberships = AuthorizeFilter(api, r, rbac.ActionRead, memberships)

for _, mem := range memberships {
// If we can read the org member, include the roles
// If we can read the org member, include the roles.
if err == nil {
resp.OrganizationRoles[mem.OrganizationID] = mem.Roles
}
Expand All @@ -422,7 +422,7 @@ func (api *API) userRoles(rw http.ResponseWriter, r *http.Request) {
}

func (api *API) putUserRoles(rw http.ResponseWriter, r *http.Request) {
// User is the user to modify
// User is the user to modify.
user := httpmw.UserParam(r)
roles := httpmw.UserRoles(r)

Expand Down Expand Up @@ -470,7 +470,7 @@ func (api *API) putUserRoles(rw http.ResponseWriter, r *http.Request) {
// updateSiteUserRoles will ensure only site wide roles are passed in as arguments.
// If an organization role is included, an error is returned.
func (api *API) updateSiteUserRoles(ctx context.Context, args database.UpdateUserRolesParams) (database.User, error) {
// Enforce only site wide roles
// Enforce only site wide roles.
for _, r := range args.GrantedRoles {
if _, ok := rbac.IsOrgRole(r); ok {
return database.User{}, xerrors.Errorf("must only update site wide roles")
Expand Down Expand Up @@ -504,7 +504,7 @@ func (api *API) organizationsByUser(rw http.ResponseWriter, r *http.Request) {
return
}

// Only return orgs the user can read
// Only return orgs the user can read.
organizations = AuthorizeFilter(api, r, rbac.ActionRead, organizations)

publicOrganizations := make([]codersdk.Organization, 0, len(organizations))
Expand Down Expand Up @@ -584,7 +584,7 @@ func (api *API) postOrganizationsByUser(rw http.ResponseWriter, r *http.Request)
CreatedAt: database.Now(),
UpdatedAt: database.Now(),
Roles: []string{
// Also assign member role incase they get demoted from admin
// Also assign member role incase they get demoted from admin.
rbac.RoleOrgMember(organization.ID),
rbac.RoleOrgAdmin(organization.ID),
},
Expand Down Expand Up @@ -650,7 +650,7 @@ func (api *API) postLogin(rw http.ResponseWriter, r *http.Request) {
})
}

// Creates a new session key, used for logging in via the CLI
// Creates a new session key, used for logging in via the CLI.
func (api *API) postAPIKey(rw http.ResponseWriter, r *http.Request) {
user := httpmw.UserParam(r)

Expand All @@ -669,17 +669,28 @@ func (api *API) postAPIKey(rw http.ResponseWriter, r *http.Request) {
httpapi.Write(rw, http.StatusCreated, codersdk.GenerateAPIKeyResponse{Key: sessionToken})
}

// Clear the user's session cookie
func (*API) postLogout(rw http.ResponseWriter, _ *http.Request) {
// Get a blank token cookie
// Clear the user's session cookie.
func (api *API) postLogout(rw http.ResponseWriter, r *http.Request) {
// Get a blank token cookie.
cookie := &http.Cookie{
// MaxAge < 0 means to delete the cookie now
// MaxAge < 0 means to delete the cookie now.
MaxAge: -1,
Name: httpmw.SessionTokenKey,
Path: "/",
}

http.SetCookie(rw, cookie)

// Delete the session token from database.
apiKey := httpmw.APIKey(r)
err := api.Database.DeleteAPIKeyByID(r.Context(), apiKey.ID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("delete api key: %s", err.Error()),
})
return
}

httpapi.Write(rw, http.StatusOK, httpapi.Response{
Message: "Logged out!",
})
Expand Down Expand Up @@ -761,7 +772,7 @@ func (api *API) createUser(ctx context.Context, req codersdk.CreateUserRequest)
req.OrganizationID = organization.ID
orgRoles = append(orgRoles, rbac.RoleOrgAdmin(req.OrganizationID))
}
// Always also be a member
// Always also be a member.
orgRoles = append(orgRoles, rbac.RoleOrgMember(req.OrganizationID))

params := database.InsertUserParams{
Expand Down Expand Up @@ -807,7 +818,7 @@ func (api *API) createUser(ctx context.Context, req codersdk.CreateUserRequest)
UserID: user.ID,
CreatedAt: database.Now(),
UpdatedAt: database.Now(),
// By default give them membership to the organization
// By default give them membership to the organization.
Roles: orgRoles,
})
if err != nil {
Expand Down
0