end0tknr's kipple - web写経開発

太宰府天満宮の狛犬って、妙にカワイイ

install tomcat8.5 , nginx from amazon-linux-extras to amazon linux2

$ sudo amazon-linux-extras install tomcat8.5
$ ls -l /usr/share/tomcat
total 0
drwxr-xr-x 2 root root   76 Sep 23 07:40 bin
lrwxrwxrwx 1 root tomcat 11 Sep 23 07:40 conf -> /etc/tomcat
lrwxrwxrwx 1 root tomcat 22 Sep 23 07:40 lib -> /usr/share/java/tomcat
lrwxrwxrwx 1 root tomcat 15 Sep 23 07:40 logs -> /var/log/tomcat
lrwxrwxrwx 1 root tomcat 22 Sep 23 07:40 temp -> /var/cache/tomcat/temp
lrwxrwxrwx 1 root tomcat 23 Sep 23 07:40 webapps -> /var/lib/tomcat/webapps
lrwxrwxrwx 1 root tomcat 22 Sep 23 07:40 work -> /var/cache/tomcat/work

$ sudo systemctl enable tomcat
$ sudo systemctl start  tomcat


$ sudo amazon-linux-extras install nginx1
$ /usr/sbin/nginx -v
nginx version: nginx/1.18.0
$ sudo systemctl enable nginx
$ sudo systemctl start  nginx

AmazonLinux or AmazonLinux2 における timezone , hostname 設定

以下の通り

AmazonLinux では、/etc/sysconfig/network や clock を編集

$ sudo vi /etc/sysconfig/network
  HOSTNAME=test-navi-sso13-1.end0tknr.com      ##CHANGE

$ sudo vi /etc/sysconfig/clock
  ZONE="Asia/Tokyo"                            ##CHANGE
$ sudo ln -sf  /usr/share/zoneinfo/Asia/Tokyo /etc/localtime

$ sudo reboot

AmazonLinux2 では、hostnamectl や timedatectl コマンドを利用

$ sudo hostnamectl set-hostname test-sso14-1.end0tknr.com
$ sudo timedatectl set-timezone Asia/Tokyo

$ sudo reboot

OpenAM13 & WebAgent v4.1.1 for apache2.4 on linux 64 で、ログイン後、403 エラー

work around 的に対応しましたが、原因特定できたと考えておらず、 改めて調べたい為、以下にメモしておきます。

問題点

以下のような構成で、OpenAM13 と WebAgent v4.1.1 の認証テストを実施したところ、 ログインに成功するものの、ログイン後、WebAgent側へ画面遷移すると、403エラーが発生。

┌────┐    ┌──┐              ┌──┐    ┌────┐
│OpenAM13├http┤ALB ├── https──┤ALB ├http┤WebAgent│
└────┘    └──┘              └──┘    └────┘

OpenAM - WebAgent 間のパスワード誤り?

OpenAM インストール時に指定するパスワードと、 WebAgentのインストール時に指定するパスワードの不整合かと思いましたが、 そうでもないかも知れません。

/opt/sso/sso/debug/CoreSystem ログでのエラー

その後、OpenAM13 にある /opt/sso/sso/debug/CoreSystem を見たところ、 次のようなエラーを発見。

ERROR: Cannot send notification to https://test-nxxi13.end0tknr.com:443/UpdateAgentCacheServlet?shortcircuit=false
java.io.FileNotFoundException: https://test-nxxi13.end0tknr.com:443/UpdateAgentCacheServlet?shortcircuit=false
        at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1896)
        at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1498)
        at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:268)
        at com.iplanet.services.comm.server.NotificationSender.run(NotificationSender.java:93)
        at com.iplanet.services.comm.server.PLLServer.send(PLLServer.java:69)
        at com.iplanet.dpro.session.service.SessionNotificationSender$SessionNotificationSenderTask.run(SessionNotificationSender.java:265)
        at org.forgerock.openam.audit.context.AuditRequestContextPropagatingRunnable.run(AuditRequestContextPropagatingRunnable.java:42)
        at com.iplanet.am.util.ThreadPool$WorkerThread.run(ThreadPool.java:314)

暫定的に OpenAM の web agent 設定画面にある「適用されない URL 処理」に値追加

UpdateAgentCacheServlet に対してのエラーでしたので、 暫定的に OpenAM の web agent 設定画面にある「適用されない URL 処理」に値追加し、 サーバ再起動することで、画面遷移後も正しく表示されるようになりましたが、 本来の対応方法でない気がとってもします。

追加した適用されないurl

  • http*://test-nxxi13.end0tknr.com/UpdateAgentCacheServlet
  • http*://test-nxxi13.end0tknr.com/amagent

f:id:end0tknr:20200922220258p:plain

f:id:end0tknr:20200922220309p:plain

OpenAM の ssoadm コマンド代替の ssoadm.jsp を有効化

OpenAM の設定 or 管理者ツールである ssoadm は、 コマンドラインによる ssoadm は使用しますが、ssoadm.jsp はあまり利用実績がありません。

その ssoadm.jsp は有効化方法すら忘れてしまうので、以下、メモ

と言っても、ブラウザで root である amadmin によるログインし、 Configuration > サーバおよびサイト> サーバ > サーバ名 で画面遷移後。

「高度」タブ画面で、ssoadm.disabled = false の属性を追加すれば、 後は、https://test-sso.end0tknr.com/sso/ssoadm.jsp のように ssoadm.jsp へアクセスできます。

f:id:end0tknr:20200922151203p:plain

↑こう登録すると、↓このように ssoadm コマンド? を利用できます。

f:id:end0tknr:20200922151407p:plain

f:id:end0tknr:20200922151621p:plain

ldap に登録の OpenAM 設定は、ldapsearch で export できますが、肝心な部分は暗号化

大量にある OpenAMの設定は、 ssoadm コマンドで export できます。

「OpenAMの設定は、ldap に登録されている為、ldapsearch コマンドで export できるのでは?」 と考え、試しに実行しましたが、肝心な部分は暗号化されており、NG でした。

そりゃそうですね。

以下は、試しに ldapsearch した結果の抜粋です。

$ /usr/bin/ldapsearch -x -h localhost -p 50389  \
    -D "cn=Directory Manager" -w ないしょ \
    -b "dc=openam,dc=forgerock,dc=org" \
    "*" | less

# extended LDIF
#
# LDAPv3
# base <dc=openam,dc=forgerock,dc=org> with scope subtree
# filter: (objectclass=*)
# requesting: * 
#

# openam.forgerock.org
dn: dc=openam,dc=forgerock,dc=org
objectClass: top
objectClass: domain
dc: openam

# people, openam.forgerock.org
dn: ou=people,dc=openam,dc=forgerock,dc=org
objectClass: top
objectClass: organizationalUnit
ou: People

<略>

# tokens, openam.forgerock.org
dn: ou=tokens,dc=openam,dc=forgerock,dc=org
objectClass: top
objectClass: organizationalUnit
ou: tokens

# services, openam.forgerock.org
dn: ou=services,dc=openam,dc=forgerock,dc=org
objectClass: top
objectClass: organizationalunit
objectClass: sunServiceComponent
sunKeyValue: sunidentityrepositoryservice-sunOrganizationStatus=Active
ou: services
sunxmlKeyValue: sunidentityrepositoryservice-sunOrganizationAliases=openam
sunxmlKeyValue: sunidentityrepositoryservice-sunOrganizationAliases=test-navi-
 sso13.end0tknr.com

# dashboardService, services, openam.forgerock.org
dn: ou=dashboardService,ou=services,dc=openam,dc=forgerock,dc=org
objectClass: sunService
objectClass: top
ou: dashboardService

# 1.0, dashboardService, services, openam.forgerock.org
dn: ou=1.0,ou=dashboardService,ou=services,dc=openam,dc=forgerock,dc=org
objectClass: sunService
objectClass: top
sunServiceSchema:: PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KCjxTZXJ
 2aWNlc0NvbmZpZ3VyYXRpb24+PFNlcnZpY2UgbmFtZT0iZGFzaGJvYXJkU2VydmljZSIgdmVyc2lv
 bj0iMS4wIj48U2NoZW1hIGkxOG5GaWxlTmFtZT0iZGFzaGJvYXJkIiAgaTE4bktleT0iZm9yZ2Vyb
 2NrLWFtLWRhc2hib2FyZC1zZXJ2aWNlLWRlc2NyaXB0aW9uIiAgcmVzb3VyY2VOYW1lPSJkYXNoYm
 9hcmQiICByZXZpc2lvbk51bWJlcj0iMSIgIHNlcnZpY2VIaWVyYXJjaHk9Ii9EU0FNRUNvbmZpZy9
 kYXNoYm9hcmRTZXJ2aWNlIiA+CiAgICAgICAgICAgIDxHbG9iYWwgdmFsaWRhdGU9InllcyIgPgog
 ICAgICAgICAgICAgICAgPEF0dHJpYnV0ZVNjaGVtYSBjb3NRdWFsaWZpZXI9ImRlZmF1bHQiICBpM
 ThuS2V5PSIiICBpc1NlYXJjaGFibGU9Im5vIiAgbGlzdE9yZGVyPSJuYXR1cmFsIiAgbmFtZT0ic2
 VydmljZU9iamVjdENsYXNzZXMiICBzeW50YXg9InN0cmluZyIgIHR5cGU9Imxpc3QiID4KICAgICA
 gICAgICAgICAgICAgICA8RGVmYXVsdFZhbHVlcz4KICAgICAgICAgICAgICAgICAgICAgICAgPFZh
 bHVlPmZvcmdlcm9jay1hbS1kYXNoYm9hcmQtc2VydmljZTwvVmFsdWU+CiAgICAgICAgICAgICAgI
 CAgICAgPC9EZWZhdWx0VmFsdWVzPgogICAgICAgICAgICAgICAgPC9BdHRyaWJ1dGVTY2hlbWE+Cg
 ogICAgICAgICAgICAgICAgPFN1YlNjaGVtYSBoaWRlQ29uZmlnVUk9Im5vIiAgaW5oZXJpdGFuY2U
 9Im11bHRpcGxlIiAgbWFpbnRhaW5Qcmlvcml0eT0ibm8iICBuYW1lPSJkYXNoYm9hcmRBcHAiICBy
 ZWFsbUNsb25lYWJsZT0ieWVzIiAgcmVzb3VyY2VOYW1lPSJpbnN0YW5jZXMiICBzdXBwb3J0c0Fwc
 GxpY2FibGVPcmdhbml6YXRpb249Im5vIiAgdmFsaWRhdGU9InllcyIgPgogICAgICAgICAgICAgIC
 AgICAgIDxBdHRyaWJ1dGVTY2hlbWEgY29zUXVhbGlmaWVyPSJkZWZhdWx0IiAgaTE4bktleT0iRGF

OpenAM ver.12の管理画面で、ユーザ追加を行うと、org.forgerock.openam.idrepo.ldap.DJLDAPv3Repo errorcode=21

以下の通り。

OpenAMの管理画面へ、管理ユーザ(amadmin)へログイン後、 Top Level Realm -> Data Stores -> 「対象」の画面でユーザ追加を行おうとすると

プラグイン org.forgerock.openam.idrepo.ldap.DJLDAPv3Repo で
LDAP 例外が発生しました。LDAP エラーコード = 21

のエラー発生。

forgerock社のサイトを見ると、当時のバグらしい。

なので、openldap-clients を installし、 ldapコマンドラインでユーザ追加を実施することで、解消。

f:id:end0tknr:20200919112928p:plain

step 0 - install openldap-clients

$ sudo yum install openldap-clients

step 1 - ldif ファイルの準備

$ vi add_ldap.ldif

dn: uid=testuser010,ou=People,dc=openam,dc=forgerock,dc=org
uid: testuser010
sn: testuser010
cn: testuser010
userPassword: testtest
mail: 
inetUserStatus: Active
objectClass: iplanet-am-managed-person
objectClass: inetuser
objectClass: sunFederationManagerDataStore
objectClass: sunFMSAML2NameIdentifier
objectClass: inetOrgPerson
objectClass: sunIdentityServerLibertyPPService
objectClass: iPlanetPreferences
objectClass: iplanet-am-user-service
objectClass: organizationalPerson
objectClass: top
objectClass: person
objectClass: sunAMAuthAccountLockout
objectClass: iplanet-am-auth-configuration-service

step 2 - ldapadd コマンドによるユーザ追加

$ /usr/bin/ldapadd -x -h localhost -p 50389 \
>    -D "cn=Directory Manager" -w $ROOTPASSWD \
>    -f ./add_ldap.ldif
adding new entry "uid=testuser101,ou=People,dc=openam,dc=forgerock,dc=org"

step 3 - ldapsearch コマンドによるユーザ一覧表示

$ /usr/bin/ldapsearch -x -h localhost -p 50389  \
    -D "cn=Directory Manager" -w $ROOTPASSWD \
    -b "ou=People,dc=openam,dc=forgerock,dc=org" \
    "uid=*"
# extended LDIF
#
# LDAPv3
# base <ou=People,dc=openam,dc=forgerock,dc=org> with scope subtree
# filter: uid=*
# requesting: ALL
#
<略>
# testuser101, People, openam.forgerock.org
dn: uid=testuser101,ou=People,dc=openam,dc=forgerock,dc=org
objectClass: inetuser
objectClass: iplanet-am-managed-person
objectClass: iplanet-am-auth-configuration-service
objectClass: sunFederationManagerDataStore
objectClass: inetOrgPerson
objectClass: sunFMSAML2NameIdentifier
objectClass: sunIdentityServerLibertyPPService
objectClass: top
objectClass: iPlanetPreferences
objectClass: sunAMAuthAccountLockout
objectClass: organizationalPerson
objectClass: person
objectClass: iplanet-am-user-service
mail:
sn: testuser101
cn: testuser101
userPassword:: e1NTSEF9RXNMYU8vL0NsT09ZZlZJdFRXMFdkOC9LRXluSEllajRMSWtyTXc9PQ=
 =
uid: testuser101
inetUserStatus: Active

# search result
search: 2
result: 0 Success

# numResponses: 3
# numEntries: 2

antlr4-python3-runtime for python3 による java source の parse / 構文解析

java (seasar2) の source を parse / 構文解析 する必要があり、 ANTLR + Python を利用した際のメモ。

qiita.com

と言っても、上記 url の写経です。 上記urlとの違いは、

BasicInfoListener (basic_info_listener.py)内でgetText()した場合、
空白や改行がないtextが取得される為、
AstProcessor (ast_processor.py) で CommonTokenStream から getText()

という点かと思います。

dir 構成

以下に記載の通りですが 「ast_analyze_executor.py」「ast_processor.py」 「basic_info_listener.py」がメインで、 これらについては、以降に記載しています

$ tree
.
│     # src解析する際、command lineから以下のpyを実行
├── ast_analyze_executor.py  ★別途記載
│     # ANTLR から downloadしたjar
├── antlr-4.8-complete.jar
├── antlr-runtime-4.8.jar
│     # logとその設定ですが、自分でも怪しい
├── antlr.log
├── log_conf.yaml
├── ast
│   ├── __init__.py
│   ├── ast_processor.py        ★別途記載
│   ├── basic_info_listener.py  ★別途記載
│   ├── JavaLexer.py
│   ├── JavaParserListener.py
│   └── JavaParser.py
│    # ANTLR の github から downloadした文法file
├── grammar
│   ├── Java8Lexer.g4
│   ├── Java8Lexer.interp
│   ├── Java8Lexer.py
│   ├── Java8Lexer.tokens
│   ├── Java8Parser.g4
│   ├── Java8Parser.interp
│   ├── Java8ParserListener.py
│   ├── Java8Parser.py
│   ├── Java8Parser.tokens
│   ├── JavaLexer.g4
│   ├── JavaLexer.interp
│   ├── JavaLexer.py
│   ├── JavaLexer.tokens
│   ├── JavaParser.g4
│   ├── JavaParser.interp
│   ├── JavaParserListener.py
│   ├── JavaParser.py
│   └── JavaParser.tokens
│ # test解析に使用した
│ # 「Seasar2徹底入門 - SAStruts/S2JDBC対応 - 翔泳社」のサンプルコード
└── CsvAction.java

python script

ast_analyze_executor.py

# -*- coding: utf-8 -*-

import logging.config
from ast.ast_processor import AstProcessor
from ast.basic_info_listener import BasicInfoListener
import sys
import yaml
import pprint

log_conf = './log_conf.yaml'  # log設定は自分でも怪しいと思う

def main():
    logging.config.dictConfig(yaml.load(open(log_conf).read(),
                                        Loader=yaml.SafeLoader))
    logger = logging.getLogger('mainLogger')

    target_file_path = sys.argv[1]
    ast_info = \
        AstProcessor(logging, BasicInfoListener()).execute(target_file_path)

    print(pprint.pformat(ast_info, width=80)) # 幅:80文字に整形
#    pprint.pprint(ast_info)


if __name__ == "__main__":
    main()

ast.ast_processor.py

# -*- coding: utf-8 -*-
from antlr4 import FileStream, CommonTokenStream, ParseTreeWalker
from ast.JavaLexer import JavaLexer
from ast.JavaParser import JavaParser
import pprint

source_encode = "utf-8"

class AstProcessor:

    def __init__(self, logging, listener):
        self.logging = logging
        self.logger = logging.getLogger(self.__class__.__name__)
        self.listener = listener

    def execute(self, input_source):
        file_stream = FileStream(input_source,encoding=source_encode)
        common_token_stream = CommonTokenStream(JavaLexer(file_stream))
        
        
        parser = JavaParser(common_token_stream)
        walker = ParseTreeWalker()
        walker.walk(self.listener, parser.compilationUnit())

        ast_info = self.listener.ast_info

        # BasicInfoListener 内で getText()した場合、
        # 空白や改行がないtextが取得される為、ここで
        # CommonTokenStream から getText() します
        for method in ast_info['methods']:
            pprint.pprint(method)

            start_index = method['body_pos']['start_index']
            stop_index  = method['body_pos']['stop_index']
            
            method['body_src'] = \
                common_token_stream.getText(start_index,stop_index)
        
        return ast_info

ast.basic_info_listener.py

# -*- coding: utf-8 -*-
from ast.JavaParserListener import JavaParserListener
from ast.JavaParser import JavaParser
import re
import sys
import pprint

class BasicInfoListener(JavaParserListener):
    def __init__(self):
        self.ast_info = {'packageName': '',
                         'className'  : '',
                         'annotation' : [],
                         'modifier'   : [],
                         'implements' : [],
                         'extends'    : '',
                         'imports'    : [],
                         'fields'     : [],
                         'methods'    : []}
        self.tmp_annotation = []
        self.tmp_modifier   = []

    def enterPackageDeclaration(self, ctx):
        # pprint.pprint(sys._getframe().f_code.co_name+' '+ctx.getText())
        self.ast_info['packageName'] = ctx.qualifiedName().getText()
        pprint.pprint(self.ast_info['packageName'])

    def enterImportDeclaration(self, ctx):
        # pprint.pprint(sys._getframe().f_code.co_name+' '+ctx.getText())
        import_class = ctx.qualifiedName().getText()
        self.ast_info['imports'].append(import_class)

    def enterClassOrInterfaceModifier(self, ctx):
        pprint.pprint(sys._getframe().f_code.co_name+' '+ ctx.getText())
        if re.match('^@',ctx.getText()):
            self.tmp_annotation.append(ctx.getText())
        else :
            self.tmp_modifier.append(ctx.getText())
        
    def enterClassDeclaration(self, ctx):
        # pprint.pprint(sys._getframe().f_code.co_name+' '+ctx.getText())
        
        self.ast_info['annotation'] += self.tmp_annotation
        self.ast_info['modifier']   += self.tmp_modifier
        self.tmp_annotation = []
        self.tmp_modifier = []
        
        child_count = int(ctx.getChildCount())
        if child_count == 7:
            # c1 = ctx.getChild(0)                      # class
            c2 = ctx.getChild(1).getText()              # class name
            c3 = ctx.getChild(2)                        # extends
            c4 = ctx.getChild(3).getChild(0).getText()  # extends class name
            c5 = ctx.getChild(4)                        # implements
            c7 = ctx.getChild(6)                        # class body
            
            self.ast_info['className']  = c2
            self.ast_info['extends']    = c4
            self.ast_info['implements'] = \
            self.parse_implements_block(ctx.getChild(5))
            return
        
        if child_count == 5:
            c1 = ctx.getChild(0)                        # class
            c2 = ctx.getChild(1).getText()              # class name
            c3 = ctx.getChild(2).getText()              # extends or implements
            c5 = ctx.getChild(4)                        # class body

            
            self.ast_info['className'] = c2
            if c3 == 'implements':
                self.ast_info['implements'] = \
                    self.parse_implements_block(ctx.getChild(3))
            elif c3 == 'extends':
                c4 = ctx.getChild(3).getChild(0).getText()
                self.ast_info['extends'] = c4
            return
        
        if child_count == 3:
            c1 = ctx.getChild(0)                        # class
            c2 = ctx.getChild(1).getText()              # class name
            c3 = ctx.getChild(2)                        # class body
            self.ast_info['className'] = c2
            
            return
        
        print("unknown child_count"+ str(child_count))
        sys.exit()
        
    
    def enterFieldDeclaration(self, ctx):
        # pprint.pprint(sys._getframe().f_code.co_name+' '+ctx.getText())
        field = {'fieldType'      : ctx.getChild(0).getText(),
                 'fieldDefinition': ctx.getChild(1).getText(),
                 'annotation'     : [],
                 'modifier'       : []  }
        
        field['annotation'] += self.tmp_annotation
        field['modifier']   += self.tmp_modifier
        self.tmp_annotation = []
        self.tmp_modifier = []
        
        self.ast_info['fields'].append(field)

#    def exitFieldDeclaration(self, ctx): pass
        
    def enterMemberDeclaration(self, ctx): pass
        # pprint.pprint(sys._getframe().f_code.co_name+' '+ctx.getText())
        # print("{0} {1} {2} {3}".format(sys._getframe().f_code.co_name,
        #                                ctx.start.line,
        #                                ctx.start.column,
        #                                ctx.getText()))

    def enterMethodDeclaration(self, ctx):
        # pprint.pprint(sys._getframe().f_code.co_name+' '+ctx.getText())
        
        c1 = ctx.getChild(0).getText()  # return type
        c2 = ctx.getChild(1).getText()  # method name
        # params
        params = self.parse_method_params_block(ctx.getChild(2))

        # method bodyを CommonTokenStream と tokenIndex により得る為
        ctx_method_body = ctx.getChild(-1)


        method_info = {'returnType': c1,
                       'methodName': c2,
                       'annotation': [],
                       'modifier'  : [],
                       'params': params,
                       'body_pos' : {
                           'start_line'  : ctx_method_body.start.line,
                           'start_column': ctx_method_body.start.column,
                           'start_index' : ctx_method_body.start.tokenIndex,
                           'stop_line'   : ctx_method_body.stop.line,
                           'stop_column' : ctx_method_body.stop.column,
                           'stop_index'  : ctx_method_body.stop.tokenIndex}}
        method_info['annotation'] += self.tmp_annotation
        method_info['modifier']   += self.tmp_modifier
        self.tmp_annotation = []
        self.tmp_modifier = []
        self.ast_info['methods'].append(method_info)

        # cols = []
        # for child in ctx.getChildren():
        #     cols.append(child.getText())
        # print("HOGEHOGE:"+" ".join(cols))
        
           
    def parse_implements_block(self, ctx):
        implements_child_count = int(ctx.getChildCount())
        result = []
        if implements_child_count == 1:
            impl_class = ctx.getChild(0).getText()
            result.append(impl_class)
        elif implements_child_count > 1:
            for i in range(implements_child_count):
                if i % 2 == 0:
                    impl_class = ctx.getChild(i).getText()
                    result.append(impl_class)
        return result

    def parse_method_params_block(self, ctx):
        params_exist_check = int(ctx.getChildCount())
        result = []
        if params_exist_check == 3:
            params_child_count = int(ctx.getChild(1).getChildCount())
            if params_child_count == 1:
                param_type = ctx.getChild(1).getChild(0).getChild(0).getText()
                param_name = ctx.getChild(1).getChild(0).getChild(1).getText()
                param_info = {'paramType': param_type,
                              'paramName': param_name }
                result.append(param_info)
            elif params_child_count > 1:
                for i in range(params_child_count):
                    if i % 2 == 0:
                        param_type = \
                            ctx.getChild(1).getChild(i).getChild(0).getText()
                        param_name = \
                            ctx.getChild(1).getChild(i).getChild(1).getText()
                        param_info = {'paramType': param_type,
                                      'paramName': param_name }
                        result.append(param_info)
        return result

実行結果

$ /usr/local/python3/bin/python3 ast_analyze_executor.py CsvAction.java
'org.seasar.sastruts.example.action'
'enterClassOrInterfaceModifier public'
'enterClassOrInterfaceModifier @Resource'
'enterClassOrInterfaceModifier @ActionForm'
'enterClassOrInterfaceModifier protected'
'enterClassOrInterfaceModifier @Resource'
'enterClassOrInterfaceModifier protected'
'enterClassOrInterfaceModifier @Resource'
'enterClassOrInterfaceModifier protected'
'enterClassOrInterfaceModifier @Resource'
'enterClassOrInterfaceModifier protected'
'enterClassOrInterfaceModifier public'
'enterClassOrInterfaceModifier public'
'enterClassOrInterfaceModifier @Execute(validator=false)'
'enterClassOrInterfaceModifier public'
'enterClassOrInterfaceModifier @Execute(validator=true,input="index.jsp")'
'enterClassOrInterfaceModifier public'
{'annotation': ['@Execute(validator=false)'],
 'body_pos': {'start_column': 45,
              'start_index': 310,
              'start_line': 48,
              'stop_column': 4,
              'stop_index': 380,
              'stop_line': 62},
 'methodName': 'index',
 'modifier': ['public'],
 'params': [],
 'returnType': 'String'}
{'annotation': ['@Execute(validator=true,input="index.jsp")'],
 'body_pos': {'start_column': 42,
              'start_index': 407,
              'start_line': 65,
              'stop_column': 4,
              'stop_index': 583,
              'stop_line': 90},
 'methodName': 'read',
 'modifier': ['public'],
 'params': [],
 'returnType': 'String'}
{'annotation': [],
 'className': 'CsvAction',
 'extends': '',
 'fields': [{'annotation': ['@Resource', '@ActionForm'],
             'fieldDefinition': 'csvForm',
             'fieldType': 'CsvForm',
             'modifier': ['protected']},
            {'annotation': ['@Resource'],
             'fieldDefinition': 'csvService',
             'fieldType': 'CsvService',
             'modifier': ['protected']},
            {'annotation': ['@Resource'],
             'fieldDefinition': 's2csvCtrlFactory',
             'fieldType': 'S2CSVCtrlFactory',
             'modifier': ['protected']},
            {'annotation': ['@Resource'],
             'fieldDefinition': 'request',
             'fieldType': 'HttpServletRequest',
             'modifier': ['protected']},
            {'annotation': [],
             'fieldDefinition': 'deptList',
             'fieldType': 'List<DeptCsv>',
             'modifier': ['public']},
            {'annotation': [],
             'fieldDefinition': 'csv',
             'fieldType': 'String',
             'modifier': ['public']}],
 'implements': [],
 'imports': ['java.io.IOException',
             'java.io.InputStream',
             'java.io.StringWriter',
             'java.util.List',
             'javax.annotation.Resource',
             'javax.servlet.http.HttpServletRequest',
             'org.apache.commons.io.IOUtils',
             'org.apache.struts.action.ActionErrors',
             'org.seasar.framework.util.StringUtil',
             'org.seasar.s2csv.csv.S2CSVWriteCtrl',
             'org.seasar.s2csv.csv.factory.S2CSVCtrlFactory',
             'org.seasar.sastruts.example.csv.DeptCsv',
             'org.seasar.sastruts.example.form.CsvForm',
             'org.seasar.sastruts.example.service.CsvService',
             'org.seasar.struts.annotation.ActionForm',
             'org.seasar.struts.annotation.Execute',
             'org.seasar.struts.util.ActionMessagesUtil'],
 'methods': [{'annotation': ['@Execute(validator=false)'],
              'body_pos': {'start_column': 45,
                           'start_index': 310,
                           'start_line': 48,
                           'stop_column': 4,
                           'stop_index': 380,
                           'stop_line': 62},
              'body_src': '{\r\n'
                          '\r\n'
                          '        InputStream in = Thread.currentThread()\r\n'
                          '            .getContextClassLoader()\r\n'
                          '            '
                          '.getResourceAsStream("/data/dept.csv");\r\n'
                          '\r\n'
                          '        try {\r\n'
                          '            this.csvForm.csvData = '
                          'IOUtils.toString(in, "UTF-8");\r\n'
                          '\r\n'
                          '        } finally {\r\n'
                          '            IOUtils.closeQuietly(in);\r\n'
                          '        }\r\n'
                          '\r\n'
                          '        return "index.jsp";\r\n'
                          '    }',
              'methodName': 'index',
              'modifier': ['public'],
              'params': [],
              'returnType': 'String'},
             {'annotation': ['@Execute(validator=true,input="index.jsp")'],
              'body_pos': {'start_column': 42,
                           'start_index': 407,
                           'start_line': 65,
                           'stop_column': 4,
                           'stop_index': 583,
                           'stop_line': 90},
              'body_src': '{\r\n'
                          '        ActionErrors errors = new '
                          'ActionErrors();\r\n'
                          '\r\n'
                          '        '
                          'if(StringUtil.isNotEmpty(this.csvForm.processAll)){\r\n'
                          '            // 一括で処理\r\n'
                          '            this.deptList = '
                          'csvService.parseAll(this.csvForm.csvData, '
                          'errors);\r\n'
                          '        } else {\r\n'
                          '            // 1件ずつ処理\r\n'
                          '            this.deptList = '
                          'csvService.parse(this.csvForm.csvData, errors);\r\n'
                          '        }\r\n'
                          '\r\n'
                          '        if(!errors.isEmpty()){\r\n'
                          '            ActionMessagesUtil.addErrors(request, '
                          'errors);\r\n'
                          '            return "index.jsp";\r\n'
                          '        }\r\n'
                          '\r\n'
                          '        StringWriter writer = new '
                          'StringWriter();\r\n'
                          '\r\n'
                          '        S2CSVWriteCtrl<DeptCsv> controller\r\n'
                          '            = '
                          's2csvCtrlFactory.getWriteController(DeptCsv.class, '
                          'writer);\r\n'
                          '\r\n'
                          '        controller.writeAll(this.deptList);\r\n'
                          '        this.csv = writer.toString();\r\n'
                          '\r\n'
                          '        return "read.jsp";\r\n'
                          '    }',
              'methodName': 'read',
              'modifier': ['public'],
              'params': [],
              'returnType': 'String'}],
 'modifier': ['public'],
 'packageName': 'org.seasar.sastruts.example.action'}

再 LWP::UserAgentfor perl でリダイレクト先のコンテンツを取得

2010年に上記※1を記載しましたが、※2によれば、 LWP::UserAgent->new(%option) の %option には次があるらしい。

KEY default note
agent "libwww-perl/#.###" USER_AGENT
cookie_jar undef 使ったことはあります
timeout 180
max_size undef max contents size
requests_redirectable ['GET', 'HEAD'] redirectする対象method
max_redirect 7 max redirect count
protocols_allowed undef 許可protocolらしいが...
protocols_forbidden undef 禁止protocolらしいが...
from undef mail address?
default_headers HTTP::Headers->new HTTP::Headers object?
conn_cache undef 何だろう?
parse_head 1 何だろう?

特に、redirect に関しては、requests_redirectable や max_redirect が ありますので、上記※1は、以下のようになると思います。

#!/usr/local/bin/perl
use strict;
use warnings;
use HTTP::Request::Common;
use LWP::UserAgent;
use Encode;
use CGI;

# redirectを発生させる仮?のcgiを指定
my $TARGET_CGI = "http://cent80.a5.jp:8080/Test/searchto.pl";

main();

sub main {
    my $ua = LWP::UserAgent->new;

    # 301や302検知後、redirectする場合の対象method
    #   refer to https://soft.bko.to/perl/K-M/LWP-UserAgent.html
    $ua->requests_redirectable(["POST","GET"]);
  
    my $req_data =[limit              =>20,
           offset             =>20,
           blog_id            =>2,
           search             =>'("plantype|P1" OR "plantype|P2")',
           SearchSortBy       =>"text",
           SearchResultDisplay=>"descend",
           searchFields       =>"title,more"];

    my $req = HTTP::Request::Common::POST($TARGET_CGI, $req_data);
    $req->header("X-Requested-With"  =>'XMLHttpRequest',
         "X-MT-Authorization"=>'MTAuth accessToken=HOGEHOGE');
    
    my $res = $ua->request($req);

    # requests_redirectable([]) の様に空にした場合、以下のifがtrue
    if ( $res->is_redirect ) {  # redirectの場合
        print "REDIRECT DETECTED\n";
        print $res->as_string, "\n";
        return;
    }

    if ( $res->is_success ) {
        print $res->content, "\n";
        return;
    }
}

javascript - Geolocation API で緯度経度を取得し、更に google map api で、緯度経度→住所 変換

住所フォームへの入力の手間を軽減できないかと考えてみた。

動作はしますが、表示される住所精度は、PCやスマホ等の利用環境に影響を受ける為、 自動入力後、手修正が必要。

<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <title>input address test</title>
</head>
<body>
  <h1>end0tknr's input address test</h1>

  ブラウザの Geolocation API で緯度・経度を取得し、<br/>
  更に google map api で、緯度・経度→住所 変換します。<br/>
  表示される住所精度は、PCやスマホ等の利用環境に影響を受ける為、<br/>
  自動入力後、手修正を行って下さい。<br/>
  
  <form method="post">
    <table>
      <thead>
    <tr><td colspan="2">
        <button type="button" onClick="myform.input_address();">GET ADDRESS</button>
    </td></tr>
      </thead>
      <tbody>
    <tr>
      <td>緯度・経度</td>
      <td><input id="lat" type="text" style="width:95px;">
          <input id="lng" type="text" style="width:95px;">
      </td>
    </tr>
    <tr>
      <td>郵便番号</td>
      <td><input id="zipcode" type="text" style="width:200px;"></td>
    </tr>
    <tr>
      <td>都道府県</td>
      <td><input id="pref" type="text" style="width:200px;"></td>
    </tr>
    <tr>
      <td>市区町村</td>
      <td><input id="city" type="text" style="width:200px;"></td>
    </tr>
    <tr>
      <td>その他  </td>
      <td><input id="address_other" type="text" style="width:200px;"></td>
    </tr>
      </tbody>
    </table>
  </form>
  <script src="//cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.min.js"></script>
  <script src="form.js"></script>
</body>
</html>
(function() {
    var MyForm = function() {};
    MyForm.prototype = {
    gmap_api_url :'https://maps.googleapis.com/maps/api/geocode/json',
    gmap_api_opt :{"key"     :"AIza ないしょ sMu4",
               "language":"ja",
               "sensor"  :false},
        
    input_address: function(){
        if( ! navigator.geolocation ) { // Geolocation API 対応check
        alert( "ご利用のブラウザは、現在位置(緯度/軽度)を取得できません。");
        return false;
        }

        var this_obj = this;
        navigator.geolocation.getCurrentPosition(
        function(position){this_obj.post_current_pos_success(position)},
        function(error   ){this_obj.post_current_pos_error(error)},
        {"enableHighAccuracy": false,
         "timeout"   : 8000,
         "maximumAge": 2000});
    },

    post_current_pos_success: function(position){
        var lat = position.coords.latitude;
        var lng = position.coords.longitude;
        var acc_latlng = position.coords.accuracy;
        $("#lat").val(lat);
        $("#lng").val(lng);

        var req_data = this.gmap_api_opt;
        console.log(req_data);
        req_data["latlng"] = lat+","+lng;
        
            $.ajax({url: this.gmap_api_url,
                    type: 'GET',
                    data: req_data })
                .done( (data) => { this.post_conv_latlng_to_address(data) })
                .fail( (data) => { alert("error",data)  });
    },

    post_current_pos_error: function(error){
        var err_msgs = {0: "原因不明のエラーです" ,
                1: "位置情報の取得が未許可です" ,
                2: "通信状況等により位置情報取得に失敗" ,
                3: "位置情報取得がtimeout"};
        alert( err_msgs[error.code] );
    },
    

    post_conv_latlng_to_address: function(data){
        if (data.status != "OK"){
        alert("緯度・経度→住所返還に失敗しました");
        return false;
        }

        for (var i=0; i<data.results.length; i++) {
        var addr_compo = data.results[i].address_components.reverse();
        
        if(!addr_compo[0].long_name.match( /^\d{3}-?\d{4}$/g ) ||
           addr_compo[1].long_name != "日本" ){
            continue;
        }
        console.log(data.results[i]);
        
        $("#zipcode").val(addr_compo[0].long_name);
        $("#pref").val(addr_compo[2].long_name);
        $("#city").val(addr_compo[3].long_name);

        var formatted_address = data.results[i].formatted_address;
        var addr_header =
            "日本、〒" + addr_compo[0].long_name + " " +
            addr_compo[2].long_name +
            addr_compo[3].long_name;
        var address_other = formatted_address.replace(addr_header, '');
        $("#address_other").val( address_other );
        break;
        }
        
    }
    }
    window.myform = new MyForm();
})();

centos8 on aws へ ssh する際のlogin user は、centos

以下の通り、user=centos であれば、ssh可能です

$ ssh -i ~/.ssh/end0tknr_202008.pem centos@18.183.233.???
Activate the web console with: systemctl enable --now cockpit.socket

Last login: Sun Aug 30 07:53:17 2020 from 61.24.125.???
[centos@ip-172-31-6-60 ~]$ 

ちなみに、os=amazon linux2 のように ec2-user を利用すると、エラーとなります

$ ssh -i ~/.ssh/end0tknr_202008.pem ec2-user@18.183.233.???
ec2-user@18.183.233.???: Permission denied (publickey,gssapi-keyex,gssapi-with-mic).

更に、root で ssh接続しようとすると、user=centosの利用を促されます

$ ssh -i ~/.ssh/end0tknr_202008.pem root@18.183.233.???
Please login as the user "centos" rather than the user "root".

Connection to 18.183.233.??? closed.
$ 

AWS における CentOS8 by centos.org 利用は、Ami ID で直接検索

2020/8時点、AWS Console の EC2 新規作成画面において「centos8」で検索しても centos.org による centos8 は、すぐには見つかりません。 ( centos.org 以外による centos8 の方が先に見つかると思います )

f:id:end0tknr:20200830155350p:plain

どうやら https://wiki.centos.org/Cloud/AWS によれば、 os image の提供方法は、 「Official and current CentOS Public Images」と 「Legacy CentOS Public Images」で異なり、 「Official and current CentOS Public Images」は 「published outside of the AWS Market Place and are shared directly」のようです。

f:id:end0tknr:20200830155418p:plain

f:id:end0tknr:20200830155429p:plain

なので...東京リージョン=ap-northeast-1 の Ami ID=ami-089a156ea4f52a0a3 は EC2 新規作成画面において「ami-089a156ea4f52a0a3」で検索することで選択できます。

f:id:end0tknr:20200830155520p:plain

その他、「EC2 Image Builder」画面で custom AMI ID=ami-089a156ea4f52a0a3とすることでも選択できます。

f:id:end0tknr:20200830155556p:plain

Sublist3r for python によるsubdomain 探索

以下の通り。後はご自身でお試し下さい

https://github.com/aboul3la/Sublist3r

以下、install & 実行

git clone https://github.com/aboul3la/Sublist3r.git
cd Sublist3r

## 依存library install
$ sudo /usr/local/python3/bin/pip install -r requirements.txt 

## 実行
$ /usr/local/python3/bin/python3 sublist3r.py -d example.com

                 ____        _     _ _     _   _____
                / ___| _   _| |__ | (_)___| |_|___ / _ __
                \___ \| | | | '_ \| | / __| __| |_ \| '__|
                 ___) | |_| | |_) | | \__ \ |_ ___) | |
                |____/ \__,_|_.__/|_|_|___/\__|____/|_|

                # Coded By Ahmed Aboul-Ela - @aboul3la
    
[-] Enumerating subdomains now for example.com
[-] Searching now in Baidu..
[-] Searching now in Yahoo..
[-] Searching now in Google..
[-] Searching now in Bing..
[-] Searching now in Ask..
[-] Searching now in Netcraft..
[-] Searching now in DNSdumpster..
[-] Searching now in Virustotal..
[-] Searching now in ThreatCrowd..
[-] Searching now in SSL Certificates..
[-] Searching now in PassiveDNS..
sublist3r.py:614: DeprecationWarning: please use dns.resolver.Resolver.resolve() instead
  ip = Resolver.query(host, 'A')[0].to_text()
[-] Total Unique Subdomains Found: 16267
www.example.com
*.example.com
servers2.0.example.com
servers21.0.example.com
sabelfeld.00.example.com
testovyy1.yashchik.00.example.com
  :
<略 example.com の サブドメインはあまりに大量>