Skip to content

Commit

Permalink
Detect if a txt file contains Json - closes #468
Browse files Browse the repository at this point in the history
  • Loading branch information
straumat committed Jan 20, 2024
1 parent ca1d05d commit 4835687
Show file tree
Hide file tree
Showing 9 changed files with 233 additions and 42 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.royllo.explorer.core.service.asset;

import com.fasterxml.jackson.databind.ObjectMapper;
import io.micrometer.common.util.StringUtils;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
Expand All @@ -20,6 +21,7 @@
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.math.BigInteger;
import java.time.ZonedDateTime;
import java.util.Optional;
Expand Down Expand Up @@ -102,34 +104,44 @@ public void updateAsset(final String assetId,
if (metadata != null) {

try {
// Decoding (same as using xxd -r -p)
// Decoding (same as using xxd -r -p).
byte[] decodedBytes = Hex.decodeHex(metadata);

// Detecting the file type.
final String mimeType = new Tika().detect(decodedBytes);
final String extension = MimeTypes.getDefaultMimeTypes().forName(mimeType).getExtension();
String extension = MimeTypes.getDefaultMimeTypes().forName(mimeType).getExtension();

// If we have a file extension ".txt", we check if it's a JSON.
if (".txt".equalsIgnoreCase(extension)) {
if (isJSONValid(new String(decodedBytes))) {
extension = ".json";
}
}

// Saving the file.
final String fileName = assetId + extension;
contentService.storeFile(decodedBytes, fileName);
logger.info("Asset id update for {}: Metadata saved as {}", assetId, fileName);

// Setting the name of the file.
assetToUpdate.get().setMetaDataFileName(fileName);
} catch (DecoderException | MimeTypeException e) {
logger.error("Error decoding and saving metadata {}", e.getMessage());
logger.error("Asset id update for {}: Error decoding and saving metadata {}", assetId, e.getMessage());
}
}

// =============================================================================================================
// If we have the new amount.
if (amount != null) {
assetToUpdate.get().setAmount(amount);
logger.info("Asset id update for {}: Amount updated to {}", assetId, amount);
}

// =============================================================================================================
// If we have the issuance date.
if (issuanceDate != null) {
assetToUpdate.get().setIssuanceDate(issuanceDate);
logger.info("Asset id update for {}: Issuance date updated to {}", assetId, issuanceDate);
}

// We save the asset with the new information.
Expand Down Expand Up @@ -198,4 +210,20 @@ public Page<AssetDTO> getAssetsByAssetGroupId(final String assetGroupId, final i
.map(ASSET_MAPPER::mapToAssetDTO);
}


/**
* Returns true if the string is a valid JSON.
*
* @param content string to check
* @return true if content is a valid JSON
*/
private boolean isJSONValid(final String content) {
try {
new ObjectMapper().readTree(content);
return true;
} catch (IOException e) {
return false;
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,18 @@ public void updateAsset() {
assertEquals(asset1.getAssetId() + ".png", assetUpdated.get().getMetaDataFileName());
assertEquals(0, new BigInteger("100").compareTo(assetUpdated.get().getAmount()));
assertTrue(testDate.isEqual(assetUpdated.get().getIssuanceDate()));

// =============================================================================================================
// We test with a JSON File.
final String adamCoinMetadata = "227b226465736372697074696f6e223a20224120636f696e2064656469636174656420746f204164616d2066726f6d2047656e657369732c20746865206669727374206d616e206f6e2065617274682e20416c736f2061206669727374206173736574206d696e74656420696e20546972616d6973752077616c6c6574206f6e206d61696e6e65742e222c20226e616d65223a20224164616d436f696e222c20226163726f6e796d223a20224143222c202275736572223a20226661756365745f757365725f31222c2022656d61696c223a2022222c20226d696e7465645f7573696e67223a202268747470733a2f2f746573746e65742e7461726f77616c6c65742e6e65742f222c2022696d6167655f64617461223a2022646174613a696d6167652f6a70673b6261736536342c2f396a2f34414151536b5a4a5267414241514141415141424141442f32774244414d694b6c712b57666369766f362f6831636a752f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f327742444164586834662f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f7741415243414367414b41444153494141684542417845422f38514148774141415155424151454241514541414141414141414141414543417751464267634943516f4c2f3851417452414141674544417749454177554642415141414146394151494441415152425249684d5545474531466842794a7846444b426b61454949304b78775256533066416b4d324a7967676b4b4668635947526f6c4a69636f4b536f304e5459334f446b3651305246526b644953557054564656575631685a576d4e6b5a575a6e61476c7163335231646e643465587144684957476834694a69704b546c4a57576c35695a6d714b6a704b576d7036697071724b7a744c57327437693575734c44784d584778386a4a79744c54314e585731396a5a32754869342b546c3575666f3665727838765030396662332b506e362f38514148774541417745424151454241514542415141414141414141414543417751464267634943516f4c2f385141745245414167454342415144424163464241514141514a3341414543417845454253457842684a425551646863524d694d6f454946454b526f62484243534d7a55764156596e4c524368596b4e4f456c3852635947526f6d4a7967704b6a55324e7a67354f6b4e4552555a4853456c4b55315256566c64595756706a5a47566d5a326870616e4e3064585a3365486c36676f4f456859614869496d4b6b704f556c5a61586d4a6d616f714f6b7061616e714b6d7173724f3074626133754c6d367773504578636248794d6e4b3074505531646258324e6e613475506b3565626e364f6e7138765030396662332b506e362f396f4144414d4241414952417845415077434d6a42704b6d70724c6e703170697552305575302b6c49526a725347464646464142525252514155555555414646464641425252525141555555554146464646414252525251424e52535a48725453343755795239495642706d382b6c42633044734e49776355555539597965547750317044475534527365325072556f3272393066352b74495750722b564b3444664b50714b504a392f3070324365782f45306667507a6f4161596a36696d6c4748616e382b2f35307566582f43693479476970574150616d4663644b5968744646464142525252514155555555414646464641425252556b532f784838503861414652416f334e312f6c2f77445870784f66384b43632f7742422f582f5051556f475063306745436b396550616e594136554667426b314730685033525141386e74546161417a594f65445432544a42427869697777413570574978696d5959444f546e7350386154635239345557415870307065473664615145487052534161792b6c4d71587231707369344f5254454d6f6f6f7067464646464142525252514171727559437032394f77362f30464d68484262384b643149392b542f414a2f4b67425648633936474f31636d6e564337626a6a4f425373416e332b57622f363153717058766b5647713835794d65347151664b4f76483871594471544e423745476a48507451415647344a4a39425478366b2b76383652766d484641454979447855696e497068427a78316f35567552696b41382b7448576c7050616b4d6a497763556c4f666e42707455494b4b4b4b4143696969674364526949652f7744576c48336a2b5648384b2f682f4b68652f314e4a674448436d6f534d6438314d78414850725565437a4768414b754d444834342f6e2b46504f4d41446b31477041552b7450586b6261594337654b43636744756154357830352b7638416a536b5935787a514162514254547a2f414a37555964757449526a6967426a636367307056334737673030395451474936476743526334356f50576b5535424a3961576b4d61333354544b6c49345030714b6843436969696d41555555554154352b5666772f6c5372332b744d5535516533394b6550764833352f7a2b6c5377456b2b35544e35505370534d67696f56516e706a696d67486244672b76387853786e6e3844516f4b484a4f6330787547794b5945394a6b35786a38615253536f497853382b3141436e70554a4f4455684f4279616a54356d79653141434d75464761622b465062356a545370484e41446b2b37532f77434e47414647442b4648656b4d582b467638397159553944542f414f48366d696d684d6a3247676f52556c464d5679476969696b4d66476531536476702f4c2f3841562f4b6f4163484e5441385a2f77412f35464a6753557a6f782f4f6c48484835663455704761414774794b615275464b66656b397159434258586c66307054493354627a533434366b5530357a316f41546c75744f4879703961546236304830394b41416355756565656c4a547541754f357044457a514f6e316f417a394b584f4f6151444a4479414f314d6f4a796330565168636e3170435365706f6f6f414b4b4b4b41436e6f324f4430706c4641453252305054742f6e2b564f42787733352f3537314372646a55675048504970414b787a785463656c4c6a2b36632f352f774139615436385544444a704f63394b576c41396141454753616474413638306f474254533265425341516e4a6f41794f507850394253376637332b663841507451547836436741506f4f6c5275326542306f5a73384470546159676f6f6f706746464646414252525251415555555541464b435230707972334e506f734b34774d506f6165435436476b363030726a6c614c447550494864615441715065337253377a53416b7750536c336363635644755072535a7a525943517550716159574a704b4b5942525252514155555555414646464641425467704e4b4550656e3078584762423730465051302b696756776f6f6f6f414b4b4b4b414954316f71586150536a6150536764794b696e4d6d4f52546151776f6f6f6f414b4b4b4b414369696967416f705655734f4b556f5251422f2f396b3d227d22";
assetService.updateAsset(asset1.getAssetId(), adamCoinMetadata, null, null);

// We test the data.
assetUpdated = assetService.getAssetByAssetId(asset1.getAssetId());
assertTrue(assetUpdated.isPresent());
assertEquals(asset1.getAssetId() + ".json", assetUpdated.get().getMetaDataFileName());
assertEquals(0, new BigInteger("100").compareTo(assetUpdated.get().getAmount()));
assertTrue(testDate.isEqual(assetUpdated.get().getIssuanceDate()));
}

@Test
Expand Down
7 changes: 0 additions & 7 deletions backend/servers/explorer-web/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,3 @@ Server side:
Client side:
`npm run build && npm run watch`

## Design choices

- [HTML first for the philosophy](https://html-first.com/).
- [Tailwindcss as CSS framework](https://tailwindcss.com/).
- [Tailwind components library](https://daisyui.com/).
- [Tailwind dynamic components with Aline](https://devdojo.com/pines)
- [Example design](https://tailwindui.com/components/ecommerce/page-examples/shopping-cart-pages).
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.royllo.explorer.core.util.base.Base;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
Expand All @@ -18,7 +19,7 @@
*/
@Controller
@RequiredArgsConstructor
public class ErrorConfiguration implements ErrorController {
public class ErrorConfiguration extends Base implements ErrorController {

/**
* Handle error.
Expand All @@ -32,6 +33,7 @@ public String handleError(final HttpServletRequest request) {
if (status != null) {
int statusCode = Integer.parseInt(status.toString());
if (statusCode == NOT_FOUND.value()) {
logger.error("Error 404: Page not found: {}", request.getRequestURI());
return ERROR_404_PAGE;
}
if (statusCode == INTERNAL_SERVER_ERROR.value()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package org.royllo.explorer.web.configuration;

import jakarta.servlet.ServletContext;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.context.annotation.Bean;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.stereotype.Controller;
import org.tbk.lnurl.auth.InMemoryLnurlAuthPairingService;
import org.tbk.lnurl.auth.K1Manager;
import org.tbk.lnurl.auth.LnurlAuthFactory;
import org.tbk.lnurl.auth.LnurlAuthPairingService;
import org.tbk.lnurl.auth.SimpleK1Manager;
import org.tbk.lnurl.auth.SimpleLnurlAuthFactory;

import java.net.URI;
import java.net.URISyntaxException;

/**
* Configuration for LNURL-auth.
*/
@Controller
@RequiredArgsConstructor
@SuppressWarnings({"checkstyle:DesignForExtension"})
public class LnurlAuthConfiguration {

/**
* This factory creates a LNURL-auth callback url for the given k1.
*
* @param k1Manager k1 manager
* @param servletContext servlet context
* @return lnurl auth factory
*/
@Bean
@SneakyThrows(URISyntaxException.class)
LnurlAuthFactory lnurlAuthFactory(final K1Manager k1Manager,
final ServletContext servletContext) {
URI callbackUrl = new URI(servletContext.getContextPath() + "/api/v1/lnurl/auth/callback");
return new SimpleLnurlAuthFactory(callbackUrl, k1Manager);
}

/**
* This service acts like a user detail service for LNURL-auth.
* It has two methods to see if a k1 is paired with a linking key and to find a linking key by k1.
* boolean pairK1WithLinkingKey(K1 k1, LinkingKey linkingKey);
* Optional<LinkingKey> findPairedLinkingKeyByK1(K1 k1);
*
* @return lnurl auth security service
*/
@Bean
LnurlAuthPairingService lnurlAuthSecurityService() {
// TODO Implement this.
return new InMemoryLnurlAuthPairingService();
}

/**
* K1 manager. "k1" refers to a one-time, randomly generated key or token.
*
* @return k1 manager
*/
@Bean
SimpleK1Manager k1Manager() {
return new SimpleK1Manager();
}

@Bean
public UserDetailsService userDetailsService() {
// The primary purpose of the UserDetailsService is to load user-specific data.
// It is used by the AuthenticationManager to authenticate a user during the login process.
// When a username and password are submitted (e.g., via a login form), Spring Security's AuthenticationManager
// uses the UserDetailsService to load the user details.
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername("user")
.password("{bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG")
.roles("USER")
.build());
return manager;
}

}
Loading

0 comments on commit 4835687

Please sign in to comment.