Skip to content

Commit

Permalink
Integrated code lifecycle: Simplify user interface for ssh keys (#9
Browse files Browse the repository at this point in the history
…von 454)

Co-authored-by: Kaan Çaylı <38523756+kaancayli@users.noreply.github.com>
  • Loading branch information
SimonEntholzer and kaancayli authored Oct 12, 2024
1 parent 753f497 commit edf208e
Show file tree
Hide file tree
Showing 19 changed files with 463 additions and 90 deletions.
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;
}
}
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;
}
}
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());
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;
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',
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',
};

export type DocumentationType = keyof typeof DocumentationLinks;

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

documentationType = input<string>();
displayString = input<string>();
}
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"
/>
</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>
<div class="dropdown-content">
<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'">
</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>
<code> {{ copyInstructions }} </code>
</p>
</div>

@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

0 comments on commit edf208e

Please sign in to comment.