Saturday, October 6, 2018

Simple way to secure Akka Http rest services

One of the differences between Play Framework and Akka Http it is a security of the rest services. Akka Http has almost nothing for that. So, let's assume the next behavior for our application:
  •  All rest services except login should be secured
  • Login service returns JWT token as part of the header
  • Before to access any other services we have to validate JWT token in the header of a request
Our route without security has next view:
lazy val routes: Route =
pathPrefix("api") {
    pathPrefix("login") {
      post {
        parameter("email", "password") { (email, password) =>
          val loginResult: Future[Boolean] = loginService.login(email, password)
          onSuccess(loginResult) { result =>
            if(result) {
              respondWithHeader(RawHeader("TOKEN", createJwtToken(email))) {
                complete(StatusCodes.OK)
              }else{
                complete(StatusCodes.Unauthorized)
              }
            }
          }
        }
      }
    } ~
    pathPrefix("myService") {
      val data = myService.search()
      complite(data)
    }      
}
Now let's write a method which will wrap invocation of all services which we want to secure:
def authenticated: Directive1[String] =
    Directives.optionalHeaderValueByName("TOKEN").flatMap {
      case Some(jwt) if !isJwtTokenValid(jwt) =>
        Directives.complete(StatusCodes.Unauthorized -> "Invalid token.")
      case Some(jwt) if isJwtTokenExpired(jwt) =>
        Directives.complete(StatusCodes.Unauthorized -> "Token expired.")

      case Some(jwt) if !isJwtTokenExpired(jwt) => decodeJwtToken(jwt) match {
        case Some(email) =>
          Directives.provide(email)
        case None =>
          Directives.complete(StatusCodes.Unauthorized)
      }
      case _ => Directives.complete(StatusCodes.Unauthorized)
    }
And the last step is to invoke it before our services:

pathPrefix("api") {
    pathPrefix("login") {
      ...
    } ~
    authenticated{email =>    
      pathPrefix("myService") {
        val data = myService.search()
        complite(data)
      }      
    }
}

Sunday, March 18, 2018

Мої враження після 6 років використання GWT.

Для початку поясню чому вибір взагалі впав на GWT - це дуже просто, бо пристойного іншого не було. Всі сучасні javascript фреймворки(бібліотеки) на той час або ще не існували або тільки тільки зароджувалися. А GWT уже пропонував мінімізацію і оптимізацію коду, розбивку коду на модулі і завантаження за запитом, інтеграцію із мавеном, тестування, дебагінг. Зараз той самий angular чи reactjs можуть робити те саме, але в 2011 - 2012, наприклад, reactjs ще не існував.
І так, що мені подобається в GWT:
  • Мова програмування Java - це значить, що швидкість написання коду для клієнта і для сервера однакова, що підтримка зі сторони IDE максимальна. Один і той самий код можна використовувати як для клієнта так і для сервера.
  • Дебагінг - хоча в ранніх версіях від був кращий, бо браузери ще підтримували NPAPI GWT плагін дозволяв дебажити прямо в улюбленій IDE. В поточних версіях, так само як і в сучасному javascript, це робиться в developer tools і за допомогою source maps. До того ж підтримка в Chrome найкраща.
  • Інтеграція з мавеном - не потрібно нічого придумавати як все збирати і тестити. Наприклад компіляцію GWT можна виділити в окремий профайл.
  • Тести за допомогою JUnit - тести виконуються при компіляції, так що головне відділити код основної логіки від предствалення. Саме тому MVP краще підходить ніж MVC.
  • Можливість розширення - любий клас чи інтерфейс можна підмінити на свою реалізацію. Плюс код можна згенерувати. Наприклад робота із JSON в GWT схожа на роботу із XML DOM в яві. І щоб руками не пробігати весь JSON і не писати свої парсери, то такий код можна просто згенерувати на етапі компіляції.
  • Робота із помилками і логами - із коробки є можливість все відправляти на сервер, де все буде писатися уже в log4j тощо. Плюс якщо трішки послабити опції оптимізації, то можна добитися того, що стек трейси із клієнта будуть містити назву класів і номера рядків ще відбулася помилка. І все це буде писатися на сервері в лог файли.
  • Можливіть реалізувати плагіни - головно точка старту в GWT це клас який реалізує EntryPoint. Але якщо таких класів два? Обидва будуть виконані. Навіть якщо другий клас знайходиться в іншому jar файлі. Правда потрібні деякі маніпуляції із конфіг файлом. І виходить, що функціональність аплікухи можна розширяти просто добавляючии залежності в pom.xml.
  • Guava - на 99% підтримується в GWT.
Звісно ж у GWT ще багато гарних плюшок типу завантаження коду за запитом, i18n та l10n, інтеграція із javascript тощо. Але й з тим є деякі мінуси.
І ось вони
  • Повільна компіляція - наприклад наший проект на роботі збирається десь за 8 хвилин на 4-х ядерному core i7 з 16 гігами пам'яті. Звісно є всякі обхідні варіанти типу при розробці не проводити оптимізації і компіляти тільки для одного браузера. Але всерівно швидкість не така як angular.
  • Вихідний код всерівно більш роздутий і це зрозуміло, бо замість того щоб працювати із натівними структурами javascript, ми працюємо із різномантними згенерованими враперами. Наприклад ArrayList врапить доступ до javascript масиву і добавляє методи із інтерфейса List.
  • Повільний розвиток - GWT уже далеко не самий популярний фреймворк і як результат його розвиток іде трохи повільнішими кроками ніж хотілось би.

Кілька цікавих і безкоштовнийх бібліотек: guava-gwt, gwt-jackson, gwt highcharts, gwt-charts, GWT-OpenLayers 3, SmartGWT, gwtmaterialdesign, GWTP

Thursday, June 25, 2009

JPA в Google App Engine

Як відомо, робота зі стореджем в google app engine зроблена через JDO i JPA. В якості імплементації цих технологій google вибрав DataNucleus. Так от, якщо з JDO наче все більш чи меньше все працює, то з підтримкою JPA не все так добре. А саме, якщо у вас є параметризований запит і ви очікуєте отримати колекцію об'єктів, то, скоріше за все, у вас нічого не вийде. Щоб отримати колекцію з результатами, як правило, визивається javax.persistence.Query.getResultList(). Реалізація цього методу знаходиться у класі org.datanucleus.jpa.JPAQuery. Так от в момент виконання запиту до бази, ця реалізація замість того щоб передати список параметрів зі значеннями. передає null - query.executeWithMap(null). Ну і ви відповідно отримуєте NullPointerException.

Saturday, May 9, 2009

Помилки в логах JBoss

Досить цікава ситуація виникла, коли спробував підняти solr на JBoss. При старті JBoss почав в логи сипати кучу повідомлень про помилки. Після читання вікі та сорців solr виявилося, що solr в якості логера використовую jdk logger, який по дефолту всі повідоплення відправляє в консоль (sys.err). А JBoss, який використовує lo4j, в свою чергу перехоплює всі звернення до системних потоків, приводить їх до формату lo4j і ставить рівень логи як ERROR.
Щоб привести це в порядок, потрібно сворити файл logger.properties приблизно наступного виду
# Default global logging level:
.level= INFO

# Write to a file:
handlers= java.util.logging.FileHandler

# Write log messages in XML format:
java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter 

# Log to the current working directory, with log files named solrxxx.log
java.util.logging.FileHandler.pattern = solr%u.log
і підправити опці з якими стартує JBoss.
JAVA_OPTS=%JAVA_OPTS% -Djava.util.logging.config.file=/logging.properties

Thursday, March 5, 2009

Розбираємо результат Yahoo geocodes

Є у Yahoo такий сервіс, як по адресі визначити географічні координати місця. Причому задавати можна досить загальну адресу. Наприклад при пошуку за адресою Kiev вертаються координати приблизно Бессарабського ринку. Детальніше про сам сервіс можна прочитати тут.
Результат цей сервіс повертає в XML форматі.
<?xml version="1.0"?>
<ResultSet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:yahoo:maps"
xsi:schemaLocation="urn:yahoo:maps http://api.local.yahoo.com/MapsService/V1/GeocodeResponse.xsd">
<Result precision="zip">
<Latitude>50.441380</Latitude>
<Longitude>30.522495</Longitude>
<Address></Address>
<City>Kiev</City>
<State>Ukraine</State>
<Zip></Zip>
<Country>UA</Country>
</Result>
</ResultSet>
Щоб дістати значення Latitude і Longitude використаємо XPath. Так як в xml вказаний namespace, то потрібно врахувати це при створені документу
DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
domFactory.setNamespaceAware(true); //вмикаємо підтримку namespace
DocumentBuilder builder = domFactory.newDocumentBuilder();
Document doc = builder.parse(inputStream);
а також при написанні xpath запиту - хоч в самому xml документі перфікс дефолтового namespace не вказується для тегів, при написанні xpath запитів їх потрібно вкзувати. В нашому випадку превіксом являється urn:yahoo:maps. Таким чином запит для отримання значення Latitude буде мати вигляд
//urn:yahoo:maps:Result/urn:yahoo:maps:Latitude
Часом в результаті може бути кілька записів, наприклад в різних країнах можуть бути міста з однаковою назвою. В такому випадку до запиту можна добавити фільтр який буде вибирати тільки результати з України
//urn:yahoo:maps:Result/urn:yahoo:maps:Latitude[following-sibling::urn:yahoo:maps:State='Ukraine']

Thursday, January 22, 2009

Налаштування прінтера HP 1018 в MacOSX 10.4

Не знаю чим керувалася HP, але якщо з прінтерами LaserJet 1010, 1012 на диску були драйвера для мака, то з новими прінтерами LaserJet 1018, 1020 драйверів крім як для вінді більше ніяких немає. Тому щоб заставити ці прінтери все таки працювати на маках потрібно встановити відкриті драйвера з пакету foo2zjs. Але цей пакет залежить ще від пакетів foomatic-rip та ghostscript. Актуальні лінки на пакети можна отримати тут. Але встановити пакети і вибрати відповідний драйвер це ще не все. Потрібно ще підправити firmware для прінтера. Для цього зкачуємо архів sihp1018.tar.gzв якому знаходиться файл sihp1018.img. В складі пакету foo2zjs є команда arm2hpdl яка зконвертує файл sihp1018.img в потрібний для прінтеру формат. Для цього виконуємо в терміналі команду:
arm2hpdl sihp1018.img > sihp1018.dl
Тепер тільки залишилося відправити цей файл на прінтер, для цього виконуємо команду
lp -oraw sihp1018.dl
Єдиний мінус - останню команду потрібно буде виконувати кожен раз коли включаєте прінтер.

Sunday, November 16, 2008

Підключаємося до транзакції

Часом в проекті з CMT бінами потірібно працювати з ресурсами які немають підтримки JTA, але мають підтримку двофазних транзакцій (Наприклад з бінів потрібно робити запити до CORBA ресурсів). В таких випадках трохи не зрозуміло де для таких ресурсів потрібно починати і закінчувати транзакцію. І тут на допомогу приходить інтерфейс javax.transaction.Synchronization. Його реалізацію можна зареєструвати в поточній транзакції і відслідковувати коли визвати першу і другу фазу коміту. Отже реалізація має бути типу
public class MyResourceSynchronization implements Synchronization{
public void beforeCompletion() {
    myResource.validate();
}
public void afterCompletion(int status) {
    if(status==Status.STATUS_ROLLEDBACK)
        myResource.rollback();
    else
        myResource.commit();
}}
і чіпляємо його до транзакції наспуним чином
TransactionManager manager=(TransactionManager)sessionContext.getUserTransaction();
manager.getTransaction().registerSynchronization(new MyResourceSynchronization());