When selecting "login with zenodo" it will prompt and login with zenodo, and the access token will be registered to the current user instead of re-login you in with the zenodo account.

Also disable Zenodo user login
This commit is contained in:
George Kalampokis 2020-04-08 16:36:06 +03:00
parent 3fc5d68135
commit a63ae3045e
15 changed files with 212 additions and 27 deletions

View File

@ -153,16 +153,6 @@ public class Login {
return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem<Principal>().payload(this.nonVerifiedUserAuthenticationService.Touch(principal.getToken())).status(ApiMessageCode.NO_MESSAGE));
}
@RequestMapping(method = RequestMethod.GET, value = {"/hasDOIToken"}, consumes = "application/json", produces = "application/json")
public @ResponseBody
ResponseEntity<ResponseItem<Boolean>> hasDOIToken(Principal principal) throws NullEmailException {
try {
return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem<Boolean>().payload(this.userManager.isDOITokenValid(principal)).status(ApiMessageCode.NO_MESSAGE));
} catch (NonValidTokenException | ExpiredTokenException | IOException e) {
return ResponseEntity.status(460).body(new ResponseItem<Boolean>().payload(false).status(ApiMessageCode.ERROR_MESSAGE).message(e.getMessage()));
}
}
@Transactional
@RequestMapping(method = RequestMethod.POST, value = {"/logout"}, consumes = "application/json", produces = "application/json")
public @ResponseBody

View File

@ -1,9 +1,13 @@
package eu.eudat.controllers;
import eu.eudat.data.query.items.table.userinfo.UserInfoTableRequestItem;
import eu.eudat.exceptions.security.ExpiredTokenException;
import eu.eudat.exceptions.security.NonValidTokenException;
import eu.eudat.exceptions.security.NullEmailException;
import eu.eudat.logic.managers.UserManager;
import eu.eudat.logic.security.claims.ClaimedAuthorities;
import eu.eudat.logic.services.ApiContext;
import eu.eudat.models.data.doi.DOIRequest;
import eu.eudat.models.data.helpers.common.DataTableData;
import eu.eudat.models.data.helpers.responses.ResponseItem;
import eu.eudat.models.data.security.Principal;
@ -73,6 +77,24 @@ public class Users extends BaseController {
DataTableData<UserListingModel> dataTable = userManager.getCollaboratorsPaged(userInfoTableRequestItem, principal);
return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem<DataTableData<UserListingModel>>().payload(dataTable).status(ApiMessageCode.NO_MESSAGE));
}
@RequestMapping(method = RequestMethod.GET, value = {"/hasDOIToken"}, consumes = "application/json", produces = "application/json")
public @ResponseBody
ResponseEntity<ResponseItem<Boolean>> hasDOIToken(Principal principal) throws NullEmailException {
try {
return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem<Boolean>().payload(this.userManager.isDOITokenValid(principal)).status(ApiMessageCode.NO_MESSAGE));
} catch (NonValidTokenException | ExpiredTokenException | IOException e) {
return ResponseEntity.status(460).body(new ResponseItem<Boolean>().payload(false).status(ApiMessageCode.ERROR_MESSAGE).message(e.getMessage()));
}
}
@Transactional
@RequestMapping(method = RequestMethod.POST, value = {"/registerDOIToken"}, consumes = "application/json", produces = "application/json")
public @ResponseBody
ResponseEntity<ResponseItem<UserProfile>> registerDOIToken(@RequestBody DOIRequest doiRequest, Principal principal) throws NullEmailException, IOException {
userManager.registerDOIToken(doiRequest, principal);
return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem<UserProfile>().status(ApiMessageCode.NO_MESSAGE));
}
}

View File

@ -13,11 +13,14 @@ import eu.eudat.exceptions.security.NullEmailException;
import eu.eudat.exceptions.security.UnauthorisedException;
import eu.eudat.logic.builders.entity.UserRoleBuilder;
import eu.eudat.logic.builders.model.models.DataTableDataBuilder;
import eu.eudat.logic.security.customproviders.Zenodo.ZenodoCustomProvider;
import eu.eudat.logic.security.validators.zenodo.helpers.ZenodoResponseToken;
import eu.eudat.logic.services.ApiContext;
import eu.eudat.logic.services.operations.authentication.AuthenticationService;
import eu.eudat.logic.utilities.builders.XmlBuilder;
import eu.eudat.models.HintedModelFactory;
import eu.eudat.models.data.dmp.DataManagementPlan;
import eu.eudat.models.data.doi.DOIRequest;
import eu.eudat.models.data.helpers.common.DataTableData;
import eu.eudat.models.data.login.Credentials;
import eu.eudat.models.data.security.Principal;
@ -26,6 +29,7 @@ import eu.eudat.models.data.userinfo.UserProfile;
import eu.eudat.queryable.QueryableList;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
@ -39,10 +43,14 @@ import java.util.stream.Collectors;
public class UserManager {
private ApiContext apiContext;
private ZenodoCustomProvider zenodoCustomProvider;
private Environment environment;
@Autowired
public UserManager(ApiContext apiContext) {
public UserManager(ApiContext apiContext, ZenodoCustomProvider zenodoCustomProvider, Environment environment) {
this.apiContext = apiContext;
this.zenodoCustomProvider = zenodoCustomProvider;
this.environment = environment;
}
public eu.eudat.models.data.user.composite.DatasetProfile generateDatasetProfileModel(eu.eudat.data.entities.DatasetProfile profile) {
@ -127,4 +135,15 @@ public class UserManager {
}
throw new NonValidTokenException("This account has no Zenodo Token");
}
public void registerDOIToken(DOIRequest doiRequest, Principal principal) throws IOException {
ZenodoResponseToken responseToken = this.zenodoCustomProvider.getAccessToken(doiRequest.getZenodoRequest().getCode()
, this.environment.getProperty("zenodo.login.client_id")
, this.environment.getProperty("zenodo.login.client_secret")
, doiRequest.getRedirectUri());
Map<String, Object> settings = new HashMap<>();
settings.put("zenodoToken", responseToken.getAccessToken());
settings.put("expirationDate", Instant.now().plusSeconds(responseToken.getExpiresIn()).toEpochMilli());
this.updateSettings(settings, principal);
}
}

View File

@ -0,0 +1,25 @@
package eu.eudat.models.data.doi;
import eu.eudat.logic.security.validators.zenodo.helpers.ZenodoRequest;
public class DOIRequest {
private ZenodoRequest zenodoRequest;
private String redirectUri;
public ZenodoRequest getZenodoRequest() {
return zenodoRequest;
}
public void setZenodoRequest(ZenodoRequest zenodoRequest) {
this.zenodoRequest = zenodoRequest;
}
public String getRedirectUri() {
return redirectUri;
}
public void setRedirectUri(String redirectUri) {
this.redirectUri = redirectUri;
}
}

View File

@ -2,6 +2,8 @@ import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { ReloadHelperComponent } from '@app/ui/misc/reload-helper/reload-helper.component';
import { B2AccessLoginComponent } from './ui/auth/login/b2access/b2access-login.component';
import { Oauth2DialogModule } from './ui/misc/oauth2-dialog/oauth2-dialog.module';
import { Oauth2DialogComponent } from './ui/misc/oauth2-dialog/oauth2-dialog.component';
const appRoutes: Routes = [
{
@ -219,7 +221,8 @@ const appRoutes: Routes = [
data: {
},
},
{ path: 'reload', component: ReloadHelperComponent }
{ path: 'reload', component: ReloadHelperComponent },
{ path: 'oauth2', component: Oauth2DialogComponent }
];
@NgModule({

View File

@ -30,6 +30,7 @@ import { NgcCookieConsentConfig, NgcCookieConsentModule } from 'ngx-cookieconsen
import { TranslateServerLoader } from './core/services/language/server.loader';
import { BaseHttpService } from './core/services/http/base-http.service';
import { ConfigurationService } from './core/services/configuration/configuration.service';
import { Oauth2DialogModule } from './ui/misc/oauth2-dialog/oauth2-dialog.module';
// AoT requires an exported function for factories
export function HttpLoaderFactory(http: HttpClient, appConfig: ConfigurationService) {
@ -97,7 +98,8 @@ const cookieConfig: NgcCookieConsentConfig = {
DatasetCreateWizardModule,
NavbarModule,
SidebarModule,
NgcCookieConsentModule.forRoot(cookieConfig)
NgcCookieConsentModule.forRoot(cookieConfig),
Oauth2DialogModule
],
declarations: [
AppComponent,

View File

@ -159,13 +159,4 @@ export class AuthService extends BaseService {
})
);
}
public hasDOIToken(): Observable<any> {
this.actionUrl = this.configurationService.server + 'auth/';
const url = this.actionUrl + '/hasDOIToken';
const principal = this.current();
let headers = this.headers;
headers = headers.set('AuthToken', principal.token);
return this.http.get(url, { headers: headers });
}
}

View File

@ -47,4 +47,14 @@ export class UserService {
getCollaboratorsPaged(dataTableRequest: DataTableRequest<UserCriteria>): Observable<DataTableData<UserListingModel>> {
return this.http.post<DataTableData<UserListingModel>>(this.actionUrl + 'getCollaboratorsPaged', JSON.stringify(dataTableRequest), { headers: this.headers });
}
public hasDOIToken(): Observable<any> {
const url = this.actionUrl + 'hasDOIToken';
return this.http.get(url, { headers: this.headers });
}
public registerDOIToken(code: string, redirectUri: string): Observable<any> {
const url = this.actionUrl + 'registerDOIToken';
return this.http.post(url, {zenodoRequest: {code: code}, redirectUri: redirectUri}, { headers: this.headers });
}
}

View File

@ -24,6 +24,11 @@ import { Role } from "@app/core/common/enum/role";
import { DmpInvitationDialogComponent } from '../invitation/dmp-invitation.component';
import { MultipleChoiceDialogModule } from '@common/modules/multiple-choice-dialog/multiple-choice-dialog.module';
import { MultipleChoiceDialogComponent } from '@common/modules/multiple-choice-dialog/multiple-choice-dialog.component';
import { ConfigurationService } from '@app/core/services/configuration/configuration.service';
import { Oauth2DialogComponent } from '@app/ui/misc/oauth2-dialog/oauth2-dialog.component';
import { Oauth2DialogService } from '@app/ui/misc/oauth2-dialog/service/oauth2-dialog.service';
import { isNullOrUndefined } from 'util';
import { UserService } from '@app/core/services/user/user.service';
@Component({
selector: 'app-dmp-overview',
@ -49,7 +54,10 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit {
private authentication: AuthService,
private dialog: MatDialog,
private language: TranslateService,
private uiNotificationService: UiNotificationService
private uiNotificationService: UiNotificationService,
private configurationService: ConfigurationService,
private oauth2DialogService: Oauth2DialogService,
private userService: UserService
) {
super();
}
@ -323,8 +331,17 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit {
return dmp.doi == null ? true : false;
}
getAccessUrl(): string {
const redirectUri = this.configurationService.app + 'oauth2';
const url = this.configurationService.loginProviders.zenodoConfiguration.oauthUrl
+ '?client_id=' + this.configurationService.loginProviders.zenodoConfiguration.clientId
+ '&response_type=code&scope=deposit:write+deposit:actions&state=astate&redirect_uri='
+ redirectUri;
return url;
}
getDoi(dmp: DmpOverviewModel) {
this.authentication.hasDOIToken().subscribe(response => {
this.userService.hasDOIToken().subscribe(response => {
this.showConfirmationDOIDialog(dmp);
}, error => {
this.showErrorConfirmationDOIDialog(error.error.message, dmp);
@ -368,8 +385,9 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit {
dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => {
switch (result) {
case 0:
this.authentication.logout();
this.router.navigate(['/login/external/zenodo']);
// this.authentication.logout();
// this.router.navigate(['/login/external/zenodo']);
this.showOauth2Dialog(this.getAccessUrl(), dmp);
break;
case 1:
this.showConfirmationDOIDialog(dmp);
@ -378,6 +396,18 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit {
});
}
showOauth2Dialog(url: string, dmp: DmpOverviewModel) {
this.oauth2DialogService.login(url)
.pipe(takeUntil(this._destroyed))
.subscribe(code => {
if (!isNullOrUndefined(code)) {
this.userService.registerDOIToken(code, this.configurationService.app + 'oauth2')
.pipe(takeUntil(this._destroyed))
.subscribe(() => this.showConfirmationDOIDialog(dmp));
}
});
}
onDOICallbackSuccess(): void {
this.uiNotificationService.snackBarNotification(this.language.instant('DMP-EDITOR.SNACK-BAR.SUCCESSFUL-DOI'), SnackBarNotificationLevel.Success);
}

View File

@ -0,0 +1 @@
<p>oauth2-dialog works!</p>

View File

@ -0,0 +1,41 @@
import { Component, OnInit, Inject } from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
import { ActivatedRoute, Params } from '@angular/router';
import { BaseComponent } from '@common/base/base.component';
import { takeUntil } from 'rxjs/operators';
import { Oauth2DialogService } from './service/oauth2-dialog.service';
@Component({
selector: 'app-oauth2-dialog',
templateUrl: './oauth2-dialog.component.html',
styleUrls: ['./oauth2-dialog.component.scss']
})
export class Oauth2DialogComponent extends BaseComponent implements OnInit{
constructor(
private route: ActivatedRoute,
private oauth2dialogService: Oauth2DialogService
) {
super();
}
ngOnInit(): void {
this.route.queryParams.pipe(takeUntil(this._destroyed))
.subscribe((params: Params) => {
const url = params['url'];
if (!params['code']) { this.loadUrl(url) } else { this.sendCode(params['code']); }
});
}
private loadUrl(url: string ) {
console.log(url);
window.location.href = url;
}
private sendCode(code: string) {
localStorage.setItem('oauthCode', code);
window.close();
}
}

View File

@ -0,0 +1,17 @@
import { NgModule } from '@angular/core';
import { Oauth2DialogComponent } from './oauth2-dialog.component';
import { CommonUiModule } from '@common/ui/common-ui.module';
import { Oauth2DialogService } from './service/oauth2-dialog.service';
@NgModule({
declarations: [Oauth2DialogComponent],
imports: [
CommonUiModule
],
providers: [
Oauth2DialogService
]
})
export class Oauth2DialogModule { }

View File

@ -0,0 +1,34 @@
import { Injectable, Inject } from '@angular/core';
import { BaseService } from '@common/base/base.service';
import { BehaviorSubject, Observable, interval } from 'rxjs';
import { ConfigurationService } from '@app/core/services/configuration/configuration.service';
import { isNullOrUndefined } from 'util';
import { takeUntil } from 'rxjs/operators';
@Injectable()
export class Oauth2DialogService extends BaseService{
private code: BehaviorSubject<string> = new BehaviorSubject(undefined);
constructor(private configurationService: ConfigurationService) {
super();
}
public registerCode(code: string) {
this.code.next(code);
}
public login(url: string): Observable<string> {
const windows = window.open(this.configurationService.app + 'oauth2?url=' + encodeURIComponent(url) ,'', 'height=500px,width=500px');
const sub = interval(300).pipe(takeUntil(this._destroyed)).subscribe(() => {
if (windows.closed) {
const oauthCode = localStorage.getItem('oauthCode');
localStorage.removeItem('oauthCode');
this.code.next(oauthCode);
sub.unsubscribe();
}
});
return this.code.asObservable();
}
}

View File

@ -8,7 +8,7 @@
},
"defaultCulture": "en-US",
"loginProviders": {
"enabled": [1, 2, 3, 4, 5, 6, 7, 8, 10],
"enabled": [1, 2, 3, 4, 5, 6, 7, 8],
"facebookConfiguration": { "clientId": "" },
"googleConfiguration": { "clientId": "" },
"linkedInConfiguration": {