Skip to content
Open
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
23822fe
[CITE-217] Adding Access Token
diya17 Oct 19, 2023
17dbe63
[CITE-217] Adding user token manager
diya17 Oct 25, 2023
6f89887
[CITE-217] Adding create token method for access token
diya17 Oct 31, 2023
57f6eea
[CITE-217] Adding CRUD operations
diya17 Nov 3, 2023
befbf1d
Extending user access token repository and manager
diya17 Nov 3, 2023
ba2c4d1
[CITE-217] Adding Form and controller
diya17 Nov 7, 2023
8beb5a3
[CITE-217] Adding controllers
diya17 Nov 9, 2023
78d74b9
[CITE-217] : Controllers and views
diya17 Nov 14, 2023
d0b037d
[CITE-217] Adding views for details
diya17 Nov 16, 2023
24c6348
[CITE-217] Changing endpoints
diya17 Nov 21, 2023
8d4de31
[CITE-217] Adding to the header
diya17 Nov 28, 2023
abe4e3d
[CITE-217] Making changes to controllers and views
diya17 Jan 25, 2024
5c320f3
[CITE-217] Fixing issues with deletion and regeneration
diya17 Jan 26, 2024
0086228
[CITE-217] Changing token implementation to implicit token
diya17 Jan 29, 2024
163a27c
[CITE-217] Extending Oauthclient manager
diya17 Jan 31, 2024
a4047a1
[CITE-217] Changing CRUD to OauthClient
diya17 Feb 2, 2024
4a248d6
[CITE-217] Refactoring and changing CRUD class
diya17 Feb 6, 2024
5e4088d
[CITE-217] Changing access token generation
diya17 Feb 7, 2024
7c617b4
[CITE-217] Adding testcases
diya17 Feb 9, 2024
a122954
[CITE-217] : Refactoring
diya17 Feb 13, 2024
0f0f5fe
[CITE-217] Update pom
diya17 Feb 13, 2024
c7d70e0
[CITE-217] : Update Method
diya17 Mar 6, 2024
d648041
Merge branch 'develop' into story/CITE-217
PradnyaC11 Mar 28, 2025
27d7271
[CITE-217] Resolved the errors and removed duplicate codes
PradnyaC11 Apr 1, 2025
ec9d301
[CITE-217] Seperated manager class for personal oauth
Girik1105 Dec 22, 2025
51f7b94
[CITE-217] Created own seperate sub class and removed flag
Girik1105 Dec 23, 2025
8096355
[CITE-217] Removed garbage text
Girik1105 Dec 23, 2025
14630b3
[CITE-217] Fixed javascript to show tokens after regenration
Girik1105 Dec 23, 2025
bbdde80
[CITE-217] Code cleanup
Girik1105 Dec 23, 2025
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
11 changes: 10 additions & 1 deletion citesphere/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -542,14 +542,23 @@
<version>4.13.1</version>
<scope>test</scope>
</dependency>



<!-- https://mvnrepository.com/artifact/org.mockito/mockito-all -->

<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.10.19</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.12.4</version>
<scope>test</scope>
</dependency>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indentation


<!-- https://mvnrepository.com/artifact/xalan/xalan -->
<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ public interface IOAuthClient {
void setDescription(String description);

String getDescription();

boolean getIsUserAccessToken();

void setIsUserAccessToken(boolean isUserAccessToken);

IUser getCreatedBy();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ public class OAuthClient implements IOAuthClient, ClientDetails {
private int accessTokenValiditySeconds;
private int refereshTokenValiditySeconds;
private boolean autoApprove;
private boolean isUserAccessToken;

@OneToOne(targetEntity=User.class)
private IUser createdBy;

Expand Down Expand Up @@ -200,6 +202,15 @@ public void setAutoApprove(boolean autoApprove) {
}

@Override
public boolean getIsUserAccessToken() {
return isUserAccessToken;
}

@Override
public void setIsUserAccessToken(boolean isUserAccessToken) {
this.isUserAccessToken = isUserAccessToken;
}

public IUser getCreatedBy() {
return this.createdBy;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package edu.asu.diging.citesphere.core.repository.oauth;

import java.util.List;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;

import edu.asu.diging.citesphere.core.model.oauth.impl.OAuthClient;

public interface OAuthClientRepository extends JpaRepository<OAuthClient, String> {

Page<OAuthClient> findByIsUserAccessTokenAndCreatedByUsername(boolean isUserAccessToken, String createdByUsername, Pageable pageable);
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,8 @@ public interface IOAuthClientManager {
OAuthCredentials updateClientSecret(String clientId) throws CannotFindClientException;

List<OAuthClient> getClientsDetails(List<String> clientList);
}

UserAccessTokenResultPage getAllUserAccessTokenDetails(Pageable pageable, IUser user);

OAuthCredentials createUserAccessToken(String name, IUser user);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package edu.asu.diging.citesphere.core.service.oauth;

import java.util.List;

import edu.asu.diging.citesphere.core.model.oauth.IOAuthClient;
import edu.asu.diging.citesphere.core.model.oauth.IUserAccessToken;

public class UserAccessTokenResultPage {
private long totalPages;
private List<IOAuthClient> accessTokenList;

public long getTotalPages() {
return totalPages;
}

public void setTotalPages(long totalPages) {
this.totalPages = totalPages;
}

public List<IOAuthClient> getClientList() {
return accessTokenList;
}

public void setClientList(List<IOAuthClient> accessTokenList) {
this.accessTokenList = accessTokenList;
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all of the code related to personal access tokens should be in its own manager class not mixed in with the oauth clients. Also, I think we want only one OAuthClient (for the user) and then create multiple access token for the client, rather than multiple oauthclients. Or is there a reason to have one client per token? and then finally, I think I would consider creating a subclass for oauthclient for personal access tokens, rather than a boolean flag. but is that flag really needed? it looks like it's only used once, and with a separate manager class, it might not be necessary?

Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package edu.asu.diging.citesphere.core.service.oauth.impl;

import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
Expand All @@ -9,26 +13,43 @@

import javax.transaction.Transactional;

import org.javers.common.collections.Sets;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.InvalidClientException;
import org.springframework.security.oauth2.provider.AuthorizationRequest;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.ClientRegistrationException;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.OAuth2Request;
import org.springframework.security.oauth2.provider.OAuth2RequestFactory;
import org.springframework.security.oauth2.provider.TokenRequest;
import org.springframework.security.oauth2.provider.implicit.ImplicitTokenRequest;
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory;
import org.springframework.security.oauth2.provider.token.TokenStore;

import edu.asu.diging.citesphere.core.exceptions.CannotFindClientException;
import edu.asu.diging.citesphere.core.model.Role;
import edu.asu.diging.citesphere.core.model.oauth.IOAuthClient;
import edu.asu.diging.citesphere.core.model.oauth.impl.OAuthClient;
import edu.asu.diging.citesphere.core.repository.oauth.OAuthClientRepository;
import edu.asu.diging.citesphere.core.service.oauth.IOAuthClientManager;
import edu.asu.diging.citesphere.core.service.oauth.OAuthClientResultPage;
import edu.asu.diging.citesphere.core.service.oauth.OAuthCredentials;
import edu.asu.diging.citesphere.core.service.oauth.OAuthScope;
import edu.asu.diging.citesphere.core.service.oauth.UserAccessTokenResultPage;
import edu.asu.diging.citesphere.core.user.IUserManager;
import edu.asu.diging.citesphere.user.IUser;

@Transactional
Expand All @@ -39,11 +60,23 @@ public class OAuthClientManager implements ClientDetailsService, IOAuthClientMan
private BCryptPasswordEncoder bCryptPasswordEncoder;

private int accessTokenValidity;

private OAuth2RequestFactory requestFactory;

@Autowired
private TokenStore tokenStore;

@Autowired
private ClientDetailsService clientDetailsService;

@Autowired
private IUserManager userManager;

public OAuthClientManager(OAuthClientRepository repo, BCryptPasswordEncoder bCryptPasswordEncoder, int accessTokenValidity) {
this.clientRepo = repo;
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
this.accessTokenValidity = accessTokenValidity;
this.requestFactory = new DefaultOAuth2RequestFactory(clientDetailsService);
}

/* (non-Javadoc)
Expand Down Expand Up @@ -115,6 +148,13 @@ public OAuthCredentials updateClientSecret(String clientId) throws CannotFindCli
Optional<OAuthClient> clientOptional = clientRepo.findById(clientId);
if (clientOptional.isPresent()) {
OAuthClient client = clientOptional.get();
if (client.getIsUserAccessToken()) {
IUser user = client.getCreatedBy();
OAuth2AccessToken accessToken = createAccessToken(client.getClientId(), user);
client.setClientSecret(bCryptPasswordEncoder.encode(accessToken.getValue()));
OAuthClient storeClient = clientRepo.save(client);
return new OAuthCredentials(storeClient.getClientId(), accessToken.getValue());
}
String clientSecret = UUID.randomUUID().toString();
client.setClientSecret(bCryptPasswordEncoder.encode(clientSecret));
OAuthClient storeClient = clientRepo.save(client);
Expand All @@ -132,6 +172,74 @@ public List<OAuthClient> getClientsDetails(List<String> clientList){
return clients;
}

@Override
public UserAccessTokenResultPage getAllUserAccessTokenDetails(Pageable pageable, IUser user) {
List<IOAuthClient> clientList = new ArrayList<>();
Page<OAuthClient> userAccessClients = clientRepo.findByIsUserAccessTokenAndCreatedByUsername(true, user.getUsername(), pageable);
userAccessClients.forEach(oAuthClient -> clientList.add(oAuthClient));
UserAccessTokenResultPage result = new UserAccessTokenResultPage();
result.setClientList(clientList);
result.setTotalPages(userAccessClients.getTotalPages());
return result;
}

@Override
public OAuthCredentials createUserAccessToken(String name, IUser user) {
final OAuthClient client = new OAuthClient();
client.setName(name);
String clientSecret = UUID.randomUUID().toString();
client.setClientSecret(bCryptPasswordEncoder.encode(clientSecret));
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority(Role.TRUSTED_CLIENT));
client.setAuthorities(authorities);
client.setScope(new HashSet<>());
client.getScope().add(OAuthScope.READ.getScope());
client.setCreatedBy(user);
client.setIsUserAccessToken(true);
OAuthClient storeClient = clientRepo.save(client);
OAuth2AccessToken accessToken = createAccessToken(storeClient.getClientId(), user);
storeClient.setClientSecret(bCryptPasswordEncoder.encode(accessToken.getValue()));
clientRepo.save(storeClient);
return new OAuthCredentials(storeClient.getClientId(), accessToken.getValue());
}

private OAuth2AccessToken createAccessToken(String clientId, IUser user) {
DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(UUID.randomUUID().toString());
token.setScope(Sets.asSet(OAuthScope.READ.getScope()));
AuthorizationRequest request = new AuthorizationRequest(clientId, token.getScope());
TokenRequest implicitRequest = new ImplicitTokenRequest(requestFactory.createTokenRequest(request, "implicit"), requestFactory.createOAuth2Request(request));
OAuth2Authentication authentication = getOAuth2Authentication(clientDetailsService.loadClientByClientId(clientId), implicitRequest, user);
tokenStore.storeAccessToken(token, authentication);
System.out.println(extractTokenKey(token.getValue()));
return token;
}

private OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest, IUser user) {
OAuth2Request storedOAuth2Request = requestFactory.createOAuth2Request(client, tokenRequest);
Authentication authentication = new UsernamePasswordAuthenticationToken(user, null, user.getRoles());
return new OAuth2Authentication(storedOAuth2Request, authentication);
}

private String extractTokenKey(String value) {
if(value == null) {
return null;
} else {
MessageDigest digest;
try {
digest = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException var5) {
throw new IllegalStateException("MD5 algorithm not available. Fatal (should be in the JDK).");
}

try {
byte[] e = digest.digest(value.getBytes("UTF-8"));
return String.format("%032x", new Object[]{new BigInteger(1, e)});
} catch (UnsupportedEncodingException var4) {
throw new IllegalStateException("UTF-8 encoding not available. Fatal (should be in the JDK).");
}
}
}

public void setAccessTokenValidity(int accessTokenValidity) {
this.accessTokenValidity = accessTokenValidity;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package edu.asu.diging.citesphere.web.admin.forms;

public class UserAccessTokenForm {
private String name;
private String redirectUrl;

public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}

public String getRedirectUrl() {
return redirectUrl;
}
public void setRedirectUrl(String callbackUrl) {
this.redirectUrl = callbackUrl;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package edu.asu.diging.citesphere.web.admin.userOAuth;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import edu.asu.diging.citesphere.core.model.oauth.IOAuthClient;
import edu.asu.diging.citesphere.core.service.oauth.IOAuthClientManager;

@Controller
public class AccessTokenDetailsController {

@Autowired
private IOAuthClientManager clientManager;

@RequestMapping(value="/admin/user/auth/accessTokens/{accessTokenId}", method=RequestMethod.GET)
public String showAppDetails(Model model, @PathVariable("accessTokenId") String accessTokenId) {
IOAuthClient details = (IOAuthClient) clientManager.loadClientByClientId(accessTokenId);
model.addAttribute("clientName", details.getName());
model.addAttribute("clientId", details.getClientId());
return "admin/user/auth/details";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package edu.asu.diging.citesphere.web.admin.userOAuth;

import java.security.Principal;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import edu.asu.diging.citesphere.core.service.oauth.IOAuthClientManager;
import edu.asu.diging.citesphere.core.service.oauth.OAuthCredentials;
import edu.asu.diging.citesphere.core.user.IUserManager;
import edu.asu.diging.citesphere.user.IUser;
import edu.asu.diging.citesphere.web.admin.forms.UserAccessTokenForm;

@Controller
public class AddUserAccessTokenController {

@Autowired
private IUserManager userManager;

@Autowired
private IOAuthClientManager clientManager;

@RequestMapping(value="/admin/user/auth/accessTokens/add", method=RequestMethod.GET)
public String show(Model model) {
model.addAttribute("userAccessTokenForm", new UserAccessTokenForm());
return "admin/user/auth/add";
}

@RequestMapping(value="/admin/user/auth/accessTokens/add", method=RequestMethod.POST)
public String add(@Validated UserAccessTokenForm userAccessTokenForm, Model model, BindingResult errors, RedirectAttributes redirectAttrs, Principal principal) {
IUser user = userManager.findByUsername(principal.getName());
OAuthCredentials creds = clientManager.createUserAccessToken(userAccessTokenForm.getName(), user);
redirectAttrs.addFlashAttribute("clientId", creds.getClientId());
redirectAttrs.addFlashAttribute("secret", creds.getSecret());
return "redirect:/admin/user/auth/accessTokens/" + creds.getClientId();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package edu.asu.diging.citesphere.web.admin.userOAuth;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import edu.asu.diging.citesphere.core.service.oauth.IOAuthClientManager;

@Controller
public class DeleteAccessTokenController {

@Autowired
private IOAuthClientManager clientManager;

@RequestMapping(value = "/admin/user/auth/accessTokens/{accessTokenId}", method = RequestMethod.DELETE)
public ResponseEntity<String> deleteApp(@PathVariable("accessTokenId") String accessTokenId) {
clientManager.deleteClient(accessTokenId);
return new ResponseEntity<>(HttpStatus.OK);
}
}
Loading