Skip to content
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

Integrated code lifecycle: Simplify user interface for ssh keys #9454

Merged
merged 14 commits into from
Oct 12, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
5 changes: 5 additions & 0 deletions src/main/java/de/tum/cit/aet/artemis/core/domain/User.java
Original file line number Diff line number Diff line change
Expand Up @@ -561,4 +561,9 @@ public void hasAcceptedIrisElseThrow() {
public String getSshPublicKey() {
return sshPublicKey;
}

@Nullable
public @Size(max = 100) String getSshPublicKeyHash() {
return sshPublicKeyHash;
}
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved
}
10 changes: 10 additions & 0 deletions src/main/java/de/tum/cit/aet/artemis/core/dto/UserDTO.java
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ public class UserDTO extends AuditingEntityDTO {

private String sshPublicKey;

private String sshKeyHash;

private ZonedDateTime irisAccepted;

public UserDTO() {
Expand Down Expand Up @@ -291,4 +293,12 @@ public ZonedDateTime getIrisAccepted() {
public void setIrisAccepted(ZonedDateTime irisAccepted) {
this.irisAccepted = irisAccepted;
}

public String getSshKeyHash() {
return sshKeyHash;
}

public void setSshKeyHash(String sshKeyHash) {
this.sshKeyHash = sshKeyHash;
}
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ public ResponseEntity<UserDTO> getAccount() {
userDTO.setVcsAccessToken(user.getVcsAccessToken());
userDTO.setVcsAccessTokenExpiryDate(user.getVcsAccessTokenExpiryDate());
userDTO.setSshPublicKey(user.getSshPublicKey());
userDTO.setSshKeyHash(user.getSshPublicKeyHash());
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved
log.info("GET /account {} took {}ms", user.getLogin(), System.currentTimeMillis() - start);
return ResponseEntity.ok(userDTO);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@ public enum AuthenticationMechanism {
/**
* The user used the artemis client code editor to authenticate to the LocalVC
*/
CODE_EDITOR
CODE_EDITOR,
}
1 change: 1 addition & 0 deletions src/main/webapp/app/core/user/user.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export class User extends Account {
public vcsAccessToken?: string;
public vcsAccessTokenExpiryDate?: string;
public sshPublicKey?: string;
public sshKeyHash?: string;
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved
public irisAccepted?: dayjs.Dayjs;

constructor(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const DocumentationLinks = {
Quiz: 'exercises/quiz/',
Model: 'exercises/modeling/',
Programming: 'exercises/programming/',
SshSetup: 'exercises/programming.html#repository-access',
SshSetup: 'icl/ssh-intro',
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved
Text: 'exercises/textual/',
FileUpload: 'exercises/file-upload/',
Notifications: 'notifications/',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@if (displayString() && documentationType()) {
<a style="font-size: medium" class="text-primary ms-1 mb-1" href="{{ BASE_URL + DocumentationLinks[documentationType()!] }}">
<span [jhiTranslate]="displayString()!"></span>
</a>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Component, input } from '@angular/core';
import { TranslateDirective } from 'app/shared/language/translate.directive';

// The routes here are used to build the link to the documentation.
// Therefore, it's important that they exactly match the url to the subpage of the documentation.
// Additionally, the case names must match the keys in documentationLinks.json for the tooltip.
const DocumentationLinks: { [key: string]: string } = {
SshSetup: 'icl/ssh-intro',
};
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved

export type DocumentationType = keyof typeof DocumentationLinks;

@Component({
selector: 'jhi-documentation-link',
standalone: true,
templateUrl: './documentation-link.component.html',
imports: [TranslateDirective],
})
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved
export class DocumentationLinkComponent {
readonly BASE_URL = 'https://docs.artemis.cit.tum.de/user/';
readonly DocumentationLinks = DocumentationLinks;

documentationType = input<string>();
displayString = input<string>();
}
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -3,80 +3,153 @@ <h1 jhiTranslate="artemisApp.userSettings.sshSettings">
</h1>
@if (currentUser) {
<div class="list-group d-block">
<div class="list-group-item">
<dt>
<span jhiTranslate="artemisApp.userSettings.sshSettingsPage.sshSettingsInfoText"> </span>
<jhi-documentation-button [type]="documentationType" />
</dt>
</div>
@if (storedSshKey === '' && !editSshKey) {
<div class="list-group-item">
<jhi-button
[btnType]="ButtonType.PRIMARY"
[btnSize]="ButtonSize.SMALL"
[title]="'artemisApp.userSettings.sshSettingsPage.addNewSshKey'"
(onClick)="editSshKey = !editSshKey"
/>
</div>
}
@if (storedSshKey !== '' && !editSshKey) {
<dt class="list-group-item" jhiTranslate="artemisApp.userSettings.sshSettingsPage.sshKeyDisplayedInformation"></dt>
<div class="list-group-item">
{{ sshKey }}
</div>
<div class="list-group-item">
<div class="btn-group" role="group" aria-label="Actions">
<!-- Initial state: There are no keys -->
@if (keyCount === 0 && !showSshKey) {
<div class="list-group-item px-4 py-4">
<div class="mt-4"></div>
<h4 class="text-center mb-4 mt-8" jhiTranslate="artemisApp.userSettings.sshSettingsPage.noKeysHaveBeenAdded"></h4>
<div class="text-center mt-4 d-flex justify-content-center align-items-center flex-wrap narrower-box">
<p>
<span class="font-medium" jhiTranslate="artemisApp.userSettings.sshSettingsPage.whatToUseSSHForInfo"> </span>
<jhi-documentation-link [documentationType]="documentationType" [displayString]="'artemisApp.userSettings.sshSettingsPage.learnMore'">
</jhi-documentation-link>
</p>
</div>

<div class="d-flex justify-content-center mb-4">
<jhi-button
class="d-flex"
[btnType]="ButtonType.PRIMARY"
[btnSize]="ButtonSize.SMALL"
[icon]="faEdit"
[title]="'artemisApp.userSettings.sshSettingsPage.editExistingSshKey'"
(onClick)="editSshKey = !editSshKey"
[btnSize]="ButtonSize.MEDIUM"
[title]="'artemisApp.userSettings.sshSettingsPage.addNewSshKey'"
(onClick)="showSshKey = !showSshKey"
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved
/>
</div>
<div class="btn-group" role="group" aria-label="Actions">
<button
class="btn btn-md flex-grow-1 d-flex align-items-center"
jhiDeleteButton
[renderButtonText]="false"
(delete)="deleteSshKey()"
deleteQuestion="artemisApp.userSettings.sshSettingsPage.deleteSshKeyQuestion"
[dialogError]="dialogError$"
>
<fa-icon [icon]="faTrash" />
<div jhiTranslate="artemisApp.userSettings.sshSettingsPage.deleteSshKey" class="ms-2">Delete</div>
</button>
</div>
</div>
}
@if (editSshKey) {
<div class="list-group-item">
<div jhiTranslate="artemisApp.userSettings.sshSettingsPage.key"></div>

<!-- Initial state: There are keys -->
@if (keyCount > 0 && !showSshKey) {
<div class="list-group-item px-4 py-4">
<h4 jhiTranslate="artemisApp.userSettings.sshSettingsPage.keysTablePageTitle"></h4>

<div class="d-flex flex-wrap mt-4">
<p>
<span class="mt-4 font-medium" jhiTranslate="artemisApp.userSettings.sshSettingsPage.whatToUseSSHForInfo"> </span>
<jhi-documentation-link [documentationType]="documentationType" [displayString]="'artemisApp.userSettings.sshSettingsPage.learnMore'">
</jhi-documentation-link>
</p>
</div>

<table class="table">
<thead>
<tr>
<th jhiTranslate="artemisApp.userSettings.sshSettingsPage.keys"></th>
<th jhiTranslate="artemisApp.userSettings.sshSettingsPage.actions"></th>
</tr>
</thead>
<tbody>
<tr>
<td class="container">
<div jhiTranslate="artemisApp.userSettings.sshSettingsPage.keyName"></div>
<div style="font-size: x-small">
{{ sshKeyHash }}
</div>
</td>

<td class="container">
<div class="vertical-center">
<div class="dropdown" tabindex="1">
<i class="db2" tabindex="1"></i>
<a class="dropbtn">
<fa-icon [icon]="faEllipsis"></fa-icon>
</a>
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved
<div class="dropdown-content">
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved
<button class="btn dropdown-button" (click)="showSshKey = !showSshKey">
<span jhiTranslate="artemisApp.userSettings.sshSettingsPage.viewExistingSshKey"></span>
</button>
<button
jhiDeleteButton
class="btn dropdown-button"
[buttonSize]="ButtonSize.SMALL"
[renderButtonText]="false"
(delete)="deleteSshKey()"
deleteQuestion="artemisApp.userSettings.sshSettingsPage.deleteSshKeyQuestion"
[dialogError]="dialogError$"
>
<span jhiTranslate="artemisApp.userSettings.sshSettingsPage.deleteSshKey" class="ms-2"></span>
</button>
</div>
</div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
}

<!-- Editing existing key and creating new key -->
@if (showSshKey) {
<div class="list-group-item">
@if (isKeyReadonly) {
<h4 jhiTranslate="artemisApp.userSettings.sshSettingsPage.sshKeyDetails"></h4>
} @else {
<h4 jhiTranslate="artemisApp.userSettings.sshSettingsPage.addNewSshKey"></h4>
}

<div class="d-flex flex-wrap">
<p>
<span style="font-size: medium" jhiTranslate="artemisApp.userSettings.sshSettingsPage.whatToUseSSHForInfo"> </span>
<jhi-documentation-link [documentationType]="documentationType" [displayString]="'artemisApp.userSettings.sshSettingsPage.learnMore'">
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved
</jhi-documentation-link>
</p>
</div>

<dd>
<textarea class="form-control" rows="10" [readonly]="!editSshKey" [(ngModel)]="sshKey"></textarea>
<p style="font-size: smaller" jhiTranslate="artemisApp.userSettings.sshSettingsPage.key"></p>
<textarea style="resize: none" class="form-control" rows="10" [readonly]="isKeyReadonly" [(ngModel)]="sshKey"></textarea>
</dd>
<div class="col col-auto text-right">
<div class="btn-group" role="group" aria-label="Actions">
<jhi-button
[btnType]="ButtonType.PRIMARY"
[btnSize]="ButtonSize.SMALL"
[icon]="faSave"
[title]="'artemisApp.userSettings.sshSettingsPage.saveSshKey'"
(onClick)="saveSshKey()"
/>

<div class="d-flex flex-wrap mb-4">
<p>
<span style="font-size: small" jhiTranslate="artemisApp.userSettings.sshSettingsPage.alreadyHaveKey"> </span>
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved
<code> {{ copyInstructions }} </code>
</p>
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved
</div>

SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved
@if (!isKeyReadonly) {
<div class="col col-auto text-right">
<div class="btn-group" role="group" aria-label="Actions">
<jhi-button
[disabled]="!sshKey"
[btnType]="ButtonType.PRIMARY"
[btnSize]="ButtonSize.SMALL"
[icon]="faSave"
[title]="'artemisApp.userSettings.sshSettingsPage.saveSshKey'"
(onClick)="saveSshKey()"
/>
</div>
<div class="btn-group" role="group" aria-label="Actions">
<jhi-button
[btnType]="ButtonType.PRIMARY"
[btnSize]="ButtonSize.SMALL"
[title]="'artemisApp.userSettings.sshSettingsPage.cancelSavingSshKey'"
(onClick)="cancelEditingSshKey()"
/>
</div>
</div>
<div class="btn-group" role="group" aria-label="Actions">
<jhi-button
[btnType]="ButtonType.PRIMARY"
[btnSize]="ButtonSize.SMALL"
[title]="'artemisApp.userSettings.sshSettingsPage.cancelSavingSshKey'"
(onClick)="cancelEditingSshKey()"
/>
} @else {
<div class="col col-auto text-right">
<div class="btn-group" role="group" aria-label="Actions">
<jhi-button
[btnType]="ButtonType.PRIMARY"
[btnSize]="ButtonSize.SMALL"
[title]="'artemisApp.userSettings.sshSettingsPage.back'"
(onClick)="cancelEditingSshKey()"
/>
</div>
</div>
</div>
}
</div>
}
</div>
Expand Down
Loading
Loading